qLibc
qlog.c
Go to the documentation of this file.
1/******************************************************************************
2 * qLibc
3 *
4 * Copyright (c) 2010-2015 Seungyoung Kim.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *****************************************************************************/
28
29/**
30 * @file qlog.c Rotating file logger object.
31 *
32 * qlog implements a auto-rotating file logger.
33 *
34 * @code
35 * // create a daily-rotating log file.
36 * qlog_t *log = qlog("/tmp/dailylog-%Y%m%d.err", 0644, 86400, false);
37 * if(log == NULL) return;
38 *
39 * // screen out.
40 * log->duplicate(log, stdout, true);
41 *
42 * // write logs.
43 * log->write(log, "Service started.");
44 * log->writef(log, "Server Id: %d", 1);
45 *
46 * // close and release resources.
47 * log->free(log);
48 * @endcode
49 */
50
51#ifndef DISABLE_QLOG
52
53#include <stdio.h>
54#include <stdlib.h>
55#include <stdbool.h>
56#include <string.h>
57#include <stdarg.h>
58#include <unistd.h>
59#include <time.h>
60#include <sys/stat.h>
61#include <errno.h>
62#include "qinternal.h"
63#include "utilities/qstring.h"
64#include "extensions/qlog.h"
65
66#ifndef _DOXYGEN_SKIP
67static bool write_(qlog_t *log, const char *str);
68static bool writef(qlog_t *log, const char *format, ...);
69static bool duplicate(qlog_t *log, FILE *outfp, bool flush);
70static bool flush_(qlog_t *log);
71static void free_(qlog_t *log);
72
73// internal usages
74static bool _real_open(qlog_t *log);
75#endif
76
77/**
78 * Open ratating-log file
79 *
80 * @param filepathfmt filename format. formatting argument is same as
81 * strftime()
82 * @param mode new file mode. 0 for system default
83 * @param rotateinterval rotating interval seconds, set 0 to disable rotation
84 * @param options combination of options.
85 *
86 * @return a pointer of qlog_t structure
87 *
88 * @note
89 * rotateinterval is not relative time. If you set it to 3600, log file will be
90 * rotated at every hour. And filenameformat is same as strftime(). So If you
91 * want to log with hourly rotating, filenameformat must be defined including
92 * hour format like "/somepath/xxx-%Y%m%d%H.log". You can set it to
93 * "/somepath/xxx-%H.log" for daily overrided log file.
94 *
95 * @note
96 * Available options:
97 * - QLOG_OPT_THREADSAFE - make it thread-safe.
98 * - QLOG_OPT_FLUSH - flush out buffer everytime.
99 *
100 * @code
101 * qlog_t *log = qlog("/tmp/qdecoder-%Y%m%d.err", 0644, 86400, QLOG_OPT_THREADSAFE);
102 * log->free(log);
103 * @endcode
104 */
105qlog_t *qlog(const char *filepathfmt, mode_t mode, int rotateinterval,
106 int options) {
107 qlog_t *log;
108
109 // malloc qlog_t structure
110 log = (qlog_t *) calloc(1, sizeof(qlog_t));
111 if (log == NULL) {
112 errno = ENOMEM;
113 return NULL;
114 }
115
116 // set up the structure.
117 qstrcpy(log->filepathfmt, sizeof(log->filepathfmt), filepathfmt);
118 log->mode = mode;
119 if (rotateinterval > 0)
120 log->rotateinterval = rotateinterval;
121
122 // handle options
123 if (options & QLOG_OPT_THREADSAFE) {
124 Q_MUTEX_NEW(log->qmutex, true);
125 if (log->qmutex == NULL) {
126 errno = ENOMEM;
127 free(log);
128 return NULL;
129 }
130 }
131 if (options & QLOG_OPT_FLUSH) {
132 log->logflush = true;
133 }
134
135 // try to open the log file.
136 if (_real_open(log) == false) {
137 Q_MUTEX_DESTROY(log->qmutex);
138 free(log);
139 return NULL;
140 }
141
142 // member methods
143 log->write = write_;
144 log->writef = writef;
145 log->duplicate = duplicate;
146 log->flush = flush_;
147 log->free = free_;
148
149 return log;
150}
151
152/**
153 * qlog->write(): Log messages
154 *
155 * @param log a pointer of qlog_t
156 * @param str message string
157 *
158 * @return true if successful, otherewise returns false
159 */
160static bool write_(qlog_t *log, const char *str) {
161 if (log == NULL || log->fp == NULL)
162 return false;
163
164 Q_MUTEX_ENTER(log->qmutex);
165
166 /* duplicate stream */
167 if (log->outfp != NULL) {
168 fprintf(log->outfp, "%s\n", str);
169 if (log->outflush == true)
170 fflush(log->outfp);
171 }
172
173 /* check if log rotation is needed */
174 if (log->nextrotate > 0 && time(NULL) >= log->nextrotate) {
175 _real_open(log);
176 }
177
178 /* log to file */
179 bool ret = false;
180 if (fprintf(log->fp, "%s\n", str) >= 0) {
181 if (log->logflush == true)
182 fflush(log->fp);
183 ret = true;
184 }
185
186 Q_MUTEX_LEAVE(log->qmutex);
187
188 return ret;
189}
190
191/**
192 * qlog->writef(): Log messages
193 *
194 * @param log a pointer of qlog_t
195 * @param format messages format
196 *
197 * @return true if successful, otherewise returns false
198 */
199static bool writef(qlog_t *log, const char *format, ...) {
200 if (log == NULL || log->fp == NULL)
201 return false;
202
203 char *str;
204 DYNAMIC_VSPRINTF(str, format);
205 if (str == NULL)
206 return false;
207
208 bool ret = write_(log, str);
209
210 free(str);
211 return ret;
212}
213
214/**
215 * qlog->duplicate(): Duplicate log string into other stream
216 *
217 * @param log a pointer of qlog_t
218 * @param fp logging messages will be printed out into this stream.
219 * set NULL to disable.
220 * @param flush set to true if you want to flush everytime duplicating.
221 *
222 * @return true if successful, otherewise returns false
223 *
224 * @code
225 * log->duplicate(log, stdout, true); // enable console out with flushing
226 * log->duplicate(log, stderr, false); // enable console out
227 * log->duplicate(log, NULL, false); // disable console out (default)
228 * @endcode
229 */
230static bool duplicate(qlog_t *log, FILE *outfp, bool flush) {
231 if (log == NULL)
232 return false;
233
234 Q_MUTEX_ENTER(log->qmutex);
235 log->outfp = outfp;
236 log->outflush = flush;
237 Q_MUTEX_LEAVE(log->qmutex);
238
239 return true;
240}
241
242/**
243 * qlog->flush(): Flush buffered log
244 *
245 * @param log a pointer of qlog_t
246 *
247 * @return true if successful, otherewise returns false
248 */
249static bool flush_(qlog_t *log) {
250 if (log == NULL)
251 return false;
252
253 // only flush if flush flag is disabled
254 Q_MUTEX_ENTER(log->qmutex);
255 if (log->fp != NULL && log->logflush == false)
256 fflush(log->fp);
257 if (log->outfp != NULL && log->outflush == false)
258 fflush(log->outfp);
259 Q_MUTEX_LEAVE(log->qmutex);
260
261 return false;
262}
263
264/**
265 * qlog->free(): Close ratating-log file & de-allocate resources
266 *
267 * @param log a pointer of qlog_t
268 */
269static void free_(qlog_t *log) {
270 if (log == NULL)
271 return;
272
273 flush_(log);
274 Q_MUTEX_ENTER(log->qmutex);
275 if (log->fp != NULL) {
276 fclose(log->fp);
277 log->fp = NULL;
278 }
279 Q_MUTEX_LEAVE(log->qmutex);
280 Q_MUTEX_DESTROY(log->qmutex);
281 free(log);
282 return;
283}
284
285#ifndef _DOXYGEN_SKIP
286
287static bool _real_open(qlog_t *log) {
288 const time_t nowtime = time(NULL);
289
290 /* generate filename */
291 char newfilepath[PATH_MAX];
292 strftime(newfilepath, sizeof(newfilepath), log->filepathfmt,
293 localtime(&nowtime));
294
295 /* open or re-open log file */
296 if (log->fp == NULL) {
297 log->fp = fopen(newfilepath, "a");
298 if (log->fp == NULL) {
299 DEBUG("_real_open: Can't open log file '%s'.", newfilepath);
300 return false;
301 }
302
303 if (log->mode != 0)
304 fchmod(fileno(log->fp), log->mode);
305 qstrcpy(log->filepath, sizeof(log->filepath), newfilepath);
306 } else if (strcmp(log->filepath, newfilepath)) {
307 /* have opened stream, only reopen if new filename is different with
308 existing one */
309 FILE *newfp = fopen(newfilepath, "a");
310 if (newfp != NULL) {
311 if (log->mode != 0)
312 fchmod(fileno(newfp), log->mode);
313 fclose(log->fp);
314 log->fp = newfp;
315 qstrcpy(log->filepath, sizeof(log->filepath), newfilepath);
316 } else {
317 DEBUG("_real_open: Can't open log file '%s' for rotating.",
318 newfilepath);
319 }
320 } else {
321 DEBUG("_real_open: skip re-opening log file.");
322 }
323
324 /* set next rotate time */
325 if (log->rotateinterval > 0) {
326 time_t ct = time(NULL);
327 time_t dt = ct - mktime(gmtime(&ct));
328 log->nextrotate = (((ct + dt) / log->rotateinterval) + 1)
329 * log->rotateinterval - dt;
330 } else {
331 log->nextrotate = 0;
332 }
333
334 return true;
335}
336
337#endif
338
339#endif /* DISABLE_QLOG */
static bool writef(qlog_t *log, const char *format,...)
qlog->writef(): Log messages
Definition qlog.c:199
static bool write_(qlog_t *log, const char *str)
qlog->write(): Log messages
Definition qlog.c:160
static bool duplicate(qlog_t *log, FILE *outfp, bool flush)
qlog->duplicate(): Duplicate log string into other stream
Definition qlog.c:230
static void free_(qlog_t *log)
qlog->free(): Close ratating-log file & de-allocate resources
Definition qlog.c:269
static bool flush_(qlog_t *log)
qlog->flush(): Flush buffered log
Definition qlog.c:249
qlog_t * qlog(const char *filepathfmt, mode_t mode, int rotateinterval, int options)
Open ratating-log file.
Definition qlog.c:105
char * qstrcpy(char *dst, size_t size, const char *src)
Copy src string to dst.
Definition qstring.c:325