The qDecoder Project

[svn] / trunk / src / qLog.c

Parent Directory Parent Directory Revision Log Revision Log


Revision 590 - Download Blame
Fri Jun 4 02:47:48 2010 UTC (3 months ago) by wolkykim
File size: 7964 byte(s)
FIX) qLog() - added mode argument.
    1 /*
    2  * Copyright 2000-2010 The qDecoder Project. All rights reserved.
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  *
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE QDECODER PROJECT ``AS IS'' AND ANY
   15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   17  * DISCLAIMED. IN NO EVENT SHALL THE QDECODER PROJECT BE LIABLE FOR ANY
   18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   24  *
   25  * $Id$
   26  */
   27 
   28 /**
   29  * @file qLog.c Rotating File Logger API
   30  */
   31 
   32 #include <stdio.h>
   33 #include <stdlib.h>
   34 #include <stdbool.h>
   35 #include <string.h>
   36 #include <stdarg.h>
   37 #include <unistd.h>
   38 #include <sys/stat.h>
   39 #include "qDecoder.h"
   40 #include "qInternal.h"
   41 
   42 #ifndef _DOXYGEN_SKIP
   43 
   44 static bool _write(Q_LOG *log, const char *str);
   45 static bool _writef(Q_LOG *log, const char *format, ...);
   46 static bool _duplicate(Q_LOG *log, FILE *outfp, bool flush);
   47 static bool _flush(Q_LOG *log);
   48 static bool _free(Q_LOG *log);
   49 
   50 // internal usages
   51 static bool _realOpen(Q_LOG *log);
   52 
   53 #endif
   54 
   55 /**
   56  * Open ratating-log file
   57  *
   58  * @param filepathfmt		filename format. formatting argument is same as strftime()
   59  * @param mode			new file mode. 0 for system default
   60  * @param rotateinterval	rotating interval seconds, set 0 to disable rotation
   61  * @param flush			set to true if you want to flush everytime logging. false for buffered logging
   62  *
   63  * @return		a pointer of Q_LOG structure
   64  *
   65  * @note
   66  * rotateinterval is not relative time. If you set it to 3600, log file will be rotated at every hour.
   67  * And filenameformat is same as strftime(). So If you want to log with hourly rotating, filenameformat
   68  * must be defined including hour format like "/somepath/xxx-%Y%m%d%H.log". You can set it to
   69  * "/somepath/xxx-%H.log" for daily overrided log file.
   70  *
   71  * @code
   72  *   Q_LOG *log = qLog("/tmp/qdecoder-%Y%m%d.err", 0644, 86400, false);
   73  *   log->free(log);
   74  * @endcode
   75  *
   76  * @note
   77  * Use "--enable-threadsafe" configure script option to use under multi-threaded environments.
   78  */
   79 Q_LOG *qLog(const char *filepathfmt, mode_t mode, int rotateinterval, bool flush) {
   80 	Q_LOG *log;
   81 
   82 	/* malloc Q_LOG structure */
   83 	if ((log = (Q_LOG *)malloc(sizeof(Q_LOG))) == NULL) return NULL;
   84 
   85 	/* fill structure */
   86 	memset((void *)(log), 0, sizeof(Q_LOG));
   87 	qStrCpy(log->filepathfmt, sizeof(log->filepathfmt), filepathfmt);
   88 	log->mode = mode;
   89 	if(rotateinterval > 0) log->rotateinterval = rotateinterval;
   90 	log->logflush = flush;
   91 
   92 	if (_realOpen(log) == false) {
   93 		free(log);
   94 		return NULL;
   95 	}
   96 
   97 	// member methods
   98 	log->write	= _write;
   99 	log->writef	= _writef;
  100 	log->duplicate	= _duplicate;
  101 	log->flush	= _flush;
  102 	log->free	= _free;
  103 
  104 	// initialize recursive lock
  105 	Q_MUTEX_INIT(log->qmutex, true);
  106 
  107 	return log;
  108 }
  109 
  110 /**
  111  * Q_LOG->write(): Log messages
  112  *
  113  * @param log		a pointer of Q_LOG
  114  * @param str		message string
  115  *
  116  * @return		true if successful, otherewise returns false
  117  */
  118 static bool _write(Q_LOG *log, const char *str) {
  119 	if (log == NULL || log->fp == NULL) return false;
  120 
  121 	Q_MUTEX_ENTER(log->qmutex);
  122 
  123 	/* duplicate stream */
  124 	if (log->outfp != NULL) {
  125 		fprintf(log->outfp, "%s\n", str);
  126 		if(log->outflush == true) fflush(log->outfp);
  127 	}
  128 
  129 	/* check log rotate is needed*/
  130 	if (log->nextrotate > 0 && time(NULL) >= log->nextrotate) {
  131 		_realOpen(log);
  132 	}
  133 
  134 	/* log to file */
  135 	bool ret = false;
  136 	if (fprintf(log->fp, "%s\n", str) >= 0) {
  137 		if (log->logflush == true) fflush(log->fp);
  138 		ret = true;
  139 	}
  140 
  141 	Q_MUTEX_LEAVE(log->qmutex);
  142 
  143 	return ret;
  144 }
  145 
  146 /**
  147  * Q_LOG->writef(): Log messages
  148  *
  149  * @param log		a pointer of Q_LOG
  150  * @param format	messages format
  151  *
  152  * @return		true if successful, otherewise returns false
  153  */
  154 static bool _writef(Q_LOG *log, const char *format, ...) {
  155 	if (log == NULL || log->fp == NULL) return false;
  156 
  157 	char *str;
  158 	DYNAMIC_VSPRINTF(str, format);
  159 	if(str == NULL) return false;
  160 
  161 	bool ret = _write(log, str);
  162 
  163 	free(str);
  164 	return ret;
  165 }
  166 
  167 /**
  168  * Q_LOG->duplicate(): Duplicate log string into other stream
  169  *
  170  * @param log		a pointer of Q_LOG
  171  * @param fp		logging messages will be printed out into this stream. set NULL to disable.
  172  * @param flush		set to true if you want to flush everytime duplicating.
  173  *
  174  * @return		true if successful, otherewise returns false
  175  *
  176  * @code
  177  *   log->duplicate(log, stdout, true);	// enable console out with flushing
  178  *   log->duplicate(log, stderr, false);	// enable console out
  179  *   log->duplicate(log, NULL, false);	// disable console out (default)
  180  * @endcode
  181  */
  182 static bool _duplicate(Q_LOG *log, FILE *outfp, bool flush) {
  183 	if (log == NULL) return false;
  184 
  185 	Q_MUTEX_ENTER(log->qmutex);
  186 	log->outfp = outfp;
  187 	log->outflush = flush;
  188 	Q_MUTEX_LEAVE(log->qmutex);
  189 
  190 	return true;
  191 }
  192 
  193 /**
  194  * Q_LOG->flush(): Flush buffered log
  195  *
  196  * @param log		a pointer of Q_LOG
  197  *
  198  * @return		true if successful, otherewise returns false
  199  */
  200 static bool _flush(Q_LOG *log) {
  201 	if (log == NULL) return false;
  202 
  203 	// only flush if flush flag is disabled
  204 	Q_MUTEX_ENTER(log->qmutex);
  205 	if (log->fp != NULL && log->logflush == false) fflush(log->fp);
  206 	if (log->outfp != NULL && log->outflush == false) fflush(log->outfp);
  207 	Q_MUTEX_LEAVE(log->qmutex);
  208 
  209 	return false;
  210 }
  211 
  212 /**
  213  * Q_LOG->free(): Close ratating-log file & de-allocate resources
  214  *
  215  * @param log		a pointer of Q_LOG
  216  *
  217  * @return		true if successful, otherewise returns false
  218  */
  219 static bool _free(Q_LOG *log) {
  220 	if (log == NULL) return false;
  221 
  222 	_flush(log);
  223 	Q_MUTEX_ENTER(log->qmutex);
  224 	if (log->fp != NULL) {
  225 		fclose(log->fp);
  226 		log->fp = NULL;
  227 	}
  228 	Q_MUTEX_LEAVE(log->qmutex);
  229 	Q_MUTEX_DESTROY(log->qmutex);
  230 	free(log);
  231 	return true;
  232 }
  233 
  234 /////////////////////////////////////////////////////////////////////////
  235 // PRIVATE FUNCTIONS
  236 /////////////////////////////////////////////////////////////////////////
  237 
  238 #ifndef _DOXYGEN_SKIP
  239 
  240 static bool _realOpen(Q_LOG *log) {
  241 	const time_t nowtime = time(NULL);
  242 
  243 	/* generate filename */
  244 	char newfilepath[PATH_MAX];
  245 	strftime(newfilepath, sizeof(newfilepath), log->filepathfmt, localtime(&nowtime));
  246 
  247 	/* open or re-open log file */
  248 	if (log->fp == NULL) {
  249 		log->fp = fopen(newfilepath, "a");
  250 		if (log->fp == NULL) {
  251 			DEBUG("_realOpen: Can't open log file '%s'.", newfilepath);
  252 			return false;
  253 		}
  254 
  255 		if(log->mode != 0) fchmod(fileno(log->fp), log->mode);
  256 		qStrCpy(log->filepath, sizeof(log->filepath), newfilepath);
  257 	} else if(strcmp(log->filepath, newfilepath)) { /* have opened stream, only reopen if new filename is different with existing one */
  258 		FILE *newfp = fopen(newfilepath, "a");
  259 		if (newfp != NULL) {
  260 			if(log->mode != 0) fchmod(fileno(newfp), log->mode);
  261 			fclose(log->fp);
  262 			log->fp = newfp;
  263 			qStrCpy(log->filepath, sizeof(log->filepath), newfilepath);
  264 		} else {
  265 			DEBUG("_realOpen: Can't open log file '%s' for rotating.", newfilepath);
  266 		}
  267 	} else {
  268 		DEBUG("_realOpen: skip re-opening log file.");
  269 	}
  270 
  271 	/* set next rotate time */
  272 	if (log->rotateinterval > 0) {
  273 		time_t ct = time(NULL);
  274 		time_t dt = ct - mktime(gmtime(&ct));
  275 		log->nextrotate = (((ct + dt) / log->rotateinterval) + 1) * log->rotateinterval - dt;
  276 	} else {
  277 		log->nextrotate = 0;
  278 	}
  279 
  280 	return true;
  281 }
  282 
  283 #endif

Home | About | Examples | Changes | Download | SVN Repository | Install | Reference