]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/log.c
514b9f088ea9cab1d18ecff6b5de90f85abbf1de
[OpenSRF.git] / src / libopensrf / log.c
1 #include <opensrf/log.h>
2
3 #define OSRF_NO_LOG_TYPE -1
4
5 static int _prevLogType             = OSRF_NO_LOG_TYPE;
6 static int _osrfLogType                         = OSRF_LOG_TYPE_STDERR;
7 static int _osrfLogFacility                     = LOG_LOCAL0;
8 static int _osrfLogActFacility          = LOG_LOCAL1;
9 static char* _osrfLogFile                       = NULL;
10 static char* _osrfLogAppname            = NULL;
11 static int _osrfLogLevel                        = OSRF_LOG_INFO;
12 static int _osrfLogActivityEnabled      = 1;
13 static int _osrfLogIsClient         = 0;
14
15 static char* _osrfLogXid            = NULL; /* current xid */
16 static char* _osrfLogXidPfx         = NULL; /* xid prefix string */
17
18 static void osrfLogSetType( int logtype );
19 static void _osrfLogDetail( int level, const char* filename, int line, char* msg );
20 static void _osrfLogToFile( const char* label, long pid, const char* filename, int line,
21                                                         const char* xid, const char* msg );
22 static void _osrfLogSetXid( const char* xid );
23
24 void osrfLogCleanup( void ) {
25         free(_osrfLogAppname);
26         _osrfLogAppname = NULL;
27         free(_osrfLogFile);
28         _osrfLogFile = NULL;
29         _osrfLogType = OSRF_LOG_TYPE_STDERR;
30 }
31
32
33 /**
34         @brief Record some options for later reference by the logging routines.
35         @param type Type of logging; i.e. where the log messages go.
36         @param appname Pointer to the application name (may be NULL).
37         @param maxlevel Which levels of message to issue or suppress.
38
39         Typically the values for these parameters come from a configuration file.
40
41         There are three valid values for @a type:
42
43         - OSRF_LOG_TYPE_FILE -- write messages to a log file
44         - OSRF_LOG_TYPE_SYSLOG -- write messages to the syslog facility
45         - OSRF_LOG_TYPE_STDERR  -- write messages to standard error
46
47         If @a type has any other value, log messages will be written to standard error.
48
49         The logging type may be set separately by calling osrfLogSetType().  See also
50         osrfLogToStderr() and osrfRestoreLogType().
51
52         The @a appname string prefaces every log message written to a log file or to standard
53         error.  It also identifies the application to the Syslog facility, if the application
54         uses Syslog.  The default application name, if not overridden by this function or by
55         osrfLogSetAppname(), is "osrf".
56
57         Here are the valid values for @a maxlevel, with the corresponding macros:
58
59         - 1 OSRF_LOG_ERROR
60         - 2 OSRF_LOG_WARNING
61         - 3 OSRF_LOG_INFO (the default)
62         - 4 OSRF_LOG_DEBUG
63         - 5 OSRF_LOG_INTERNAL
64
65         With the special exception of activity messages (see osrfLogActivity()), the logging
66         routines will suppress any messages at a level greater than that specified by
67         @a maxlevel.  Setting @a maxlevel to zero or less suppresses all levels of message.
68         Setting it to 5 or more enables all levels of message.
69
70         The message level may be set separately by calling osrfLogSetLevel().
71 */
72 void osrfLogInit( int type, const char* appname, int maxlevel ) {
73         osrfLogSetType(type);
74         if(appname) osrfLogSetAppname(appname);
75         osrfLogSetLevel(maxlevel);
76         if( type == OSRF_LOG_TYPE_SYSLOG ) 
77                 openlog(_osrfLogAppname, 0, _osrfLogFacility );
78 }
79
80 static void _osrfLogSetXid( const char* xid ) {
81    if(xid) {
82       if(_osrfLogXid) free(_osrfLogXid);
83       _osrfLogXid = strdup(xid);
84    }
85 }
86
87 void osrfLogClearXid( void ) { _osrfLogSetXid(""); }
88 void osrfLogSetXid(char* xid) {
89    if(!_osrfLogIsClient) _osrfLogSetXid(xid);
90 }
91 void osrfLogForceXid(char* xid) {
92    _osrfLogSetXid(xid);
93 }
94
95 void osrfLogMkXid( void ) {
96    if(_osrfLogIsClient) {
97       static int _osrfLogXidInc = 0; /* increments with each new xid for uniqueness */
98       char buf[32];
99       snprintf(buf, sizeof(buf), "%s%d", _osrfLogXidPfx, _osrfLogXidInc);
100       _osrfLogSetXid(buf);
101       _osrfLogXidInc++;
102    }
103 }
104
105 char* osrfLogGetXid( void ) {
106    return _osrfLogXid;
107 }
108
109 void osrfLogSetIsClient(int is) {
110    _osrfLogIsClient = is;
111    if(!is) return;
112    /* go ahead and create the xid prefix so it will be consistent later */
113    static char buff[32];
114    snprintf(buff, sizeof(buff), "%d%ld", (int)time(NULL), (long) getpid());
115    _osrfLogXidPfx = buff;
116 }
117
118 /**
119         @brief Specify what kind of logging to perform.
120         @param logtype A code indicating the type of logging.
121
122         There are three valid values for @a logtype:
123
124         - OSRF_LOG_TYPE_FILE -- write messages to a log file
125         - OSRF_LOG_TYPE_SYSLOG -- write messages to the syslog facility
126         - OSRF_LOG_TYPE_STDERR  -- write messages to standard error
127
128         If @a logtype has any other value, log messages will be written to standard error.
129
130         This function merely records the log type for future reference.  It does not open
131         or close any files.
132
133         See also osrfLogInit(), osrfLogToStderr() and osrfRestoreLogType().
134
135 */
136 static void osrfLogSetType( int logtype ) {
137
138         switch( logtype )
139         {
140                 case OSRF_LOG_TYPE_FILE :
141                 case OSRF_LOG_TYPE_SYSLOG :
142                 case OSRF_LOG_TYPE_STDERR :
143                         _osrfLogType = logtype;
144                         break;
145                 default :
146                         fprintf(stderr, "Unrecognized log type.  Logging to stderr\n");
147                         _osrfLogType = OSRF_LOG_TYPE_STDERR;
148                         break;
149         }
150 }
151
152 void osrfLogToStderr( void )
153 {
154         if( OSRF_NO_LOG_TYPE == _prevLogType ) {
155                 _prevLogType = _osrfLogType;
156                 _osrfLogType = OSRF_LOG_TYPE_STDERR;
157         }
158 }
159
160 void osrfRestoreLogType( void )
161 {
162         if( _prevLogType != OSRF_NO_LOG_TYPE ) {
163                 _osrfLogType = _prevLogType;
164                 _prevLogType = OSRF_NO_LOG_TYPE;
165         }
166 }
167
168 void osrfLogSetFile( const char* logfile ) {
169         if(!logfile) return;
170         if(_osrfLogFile) free(_osrfLogFile);
171         _osrfLogFile = strdup(logfile);
172 }
173
174 void osrfLogSetActivityEnabled( int enabled ) {
175         _osrfLogActivityEnabled = enabled;
176 }
177
178 void osrfLogSetAppname( const char* appname ) {
179         if(!appname) return;
180         if(_osrfLogAppname) free(_osrfLogAppname);
181         _osrfLogAppname = strdup(appname);
182
183         /* if syslogging, re-open the log with the appname */
184         if( _osrfLogType == OSRF_LOG_TYPE_SYSLOG) {
185                 closelog();
186                 openlog(_osrfLogAppname, 0, _osrfLogFacility);
187         }
188 }
189
190 void osrfLogSetSyslogFacility( int facility ) {
191         _osrfLogFacility = facility;
192 }
193 void osrfLogSetSyslogActFacility( int facility ) {
194         _osrfLogActFacility = facility;
195 }
196
197 /** Sets the global log level.  Any log statements with a higher level
198  * than "level" will not be logged */
199 void osrfLogSetLevel( int loglevel ) {
200         _osrfLogLevel = loglevel;
201 }
202
203 /** Gets the current global log level. **/
204 int osrfLogGetLevel( void ) {
205         return _osrfLogLevel;
206 }
207
208 void osrfLogError( const char* file, int line, const char* msg, ... ) {
209         if( !msg ) return;
210         if( _osrfLogLevel < OSRF_LOG_ERROR ) return;
211         VA_LIST_TO_STRING( msg );
212         _osrfLogDetail( OSRF_LOG_ERROR, file, line, VA_BUF );
213 }
214
215 void osrfLogWarning( const char* file, int line, const char* msg, ... ) {
216         if( !msg ) return;
217         if( _osrfLogLevel < OSRF_LOG_WARNING ) return;
218         VA_LIST_TO_STRING( msg );
219         _osrfLogDetail( OSRF_LOG_WARNING, file, line, VA_BUF );
220 }
221
222 void osrfLogInfo( const char* file, int line, const char* msg, ... ) {
223         if( !msg ) return;
224         if( _osrfLogLevel < OSRF_LOG_INFO ) return;
225         VA_LIST_TO_STRING( msg );
226         _osrfLogDetail( OSRF_LOG_INFO, file, line, VA_BUF );
227 }
228
229 void osrfLogDebug( const char* file, int line, const char* msg, ... ) {
230         if( !msg ) return;
231         if( _osrfLogLevel < OSRF_LOG_DEBUG ) return;
232         VA_LIST_TO_STRING( msg );
233         _osrfLogDetail( OSRF_LOG_DEBUG, file, line, VA_BUF );
234 }
235
236 void osrfLogInternal( const char* file, int line, const char* msg, ... ) {
237         if( !msg ) return;
238         if( _osrfLogLevel < OSRF_LOG_INTERNAL ) return;
239         VA_LIST_TO_STRING( msg );
240         _osrfLogDetail( OSRF_LOG_INTERNAL, file, line, VA_BUF );
241 }
242
243 void osrfLogActivity( const char* file, int line, const char* msg, ... ) {
244         if( !msg ) return;
245         if( _osrfLogLevel >= OSRF_LOG_INFO
246                 || ( _osrfLogActivityEnabled && _osrfLogLevel >= OSRF_LOG_ACTIVITY ) )
247         {
248                 VA_LIST_TO_STRING( msg );
249
250                 if( _osrfLogActivityEnabled && _osrfLogLevel >= OSRF_LOG_ACTIVITY )
251                         _osrfLogDetail( OSRF_LOG_ACTIVITY, file, line, VA_BUF );
252
253                 /* also log at info level */
254                 if( _osrfLogLevel >= OSRF_LOG_INFO )
255                         _osrfLogDetail( OSRF_LOG_INFO, file, line, VA_BUF );
256         }
257 }
258
259 /**
260         @brief Issue a log message.
261         @param level The message level.
262         @param filename The name of the source file from whence the message is issued.
263         @param line The line number from whence the message is issued.
264         @param msg The text of the message.
265
266         This function is the final common pathway for all messages.
267
268         The @a level parameter determines the tag to be incorporated into the message: "ERR",
269         "WARN", "INFO", "DEBG", "INT " or "ACT".
270
271         The @a filename and @a name identify the location in the application code from whence the
272         message is being issued.
273
274         Here we format the message and route it to the appropriate output destination, depending
275         on the current setting of _osrfLogType: Syslog, a log file, or standard error.
276 */
277 static void _osrfLogDetail( int level, const char* filename, int line, char* msg ) {
278
279         if(!filename) filename = "";
280
281         char* label = "INFO";           /* level name */
282         int lvl = LOG_INFO;     /* syslog level */
283         int fac = _osrfLogFacility;
284
285         switch( level ) {
286                 case OSRF_LOG_ERROR:            
287                         label = "ERR "; 
288                         lvl = LOG_ERR;
289                         break;
290
291                 case OSRF_LOG_WARNING:  
292                         label = "WARN"; 
293                         lvl = LOG_WARNING;
294                         break;
295
296                 case OSRF_LOG_INFO:             
297                         label = "INFO"; 
298                         lvl = LOG_INFO;
299                         break;
300
301                 case OSRF_LOG_DEBUG:    
302                         label = "DEBG"; 
303                         lvl = LOG_DEBUG;
304                         break;
305
306                 case OSRF_LOG_INTERNAL: 
307                         label = "INT "; 
308                         lvl = LOG_DEBUG;
309                         break;
310
311                 case OSRF_LOG_ACTIVITY: 
312                         label = "ACT"; 
313                         lvl = LOG_INFO;
314                         fac = _osrfLogActFacility;
315                         break;
316         }
317
318    char* xid = (_osrfLogXid) ? _osrfLogXid : "";
319
320    int logtype = _osrfLogType;
321    if( logtype == OSRF_LOG_TYPE_FILE && !_osrfLogFile )
322    {
323            // No log file defined?  Temporarily reroute to stderr
324            logtype = OSRF_LOG_TYPE_STDERR;
325    }
326
327    if( logtype == OSRF_LOG_TYPE_SYSLOG ) {
328                 char buf[1536];  
329                 buf[0] = '\0';
330                 /* give syslog some breathing room, and be cute about it */
331                 strncat(buf, msg, 1535);
332                 buf[1532] = '.';
333                 buf[1533] = '.';
334                 buf[1534] = '.';
335                 buf[1535] = '\0';
336                 syslog( fac | lvl, "[%s:%ld:%s:%d:%s] %s", label, (long) getpid(), filename, line, xid, buf );
337         }
338
339         else if( logtype == OSRF_LOG_TYPE_FILE )
340                 _osrfLogToFile( label, (long) getpid(), filename, line, xid, msg );
341
342         else if( logtype == OSRF_LOG_TYPE_STDERR )
343                 fprintf( stderr, "[%s:%ld:%s:%d:%s] %s\n", label, (long) getpid(), filename, line, xid, msg );
344 }
345
346
347 /**
348         @brief Write a message to a log file.
349         @param label The message type: "ERR", "WARN", etc..
350         @param pid The process id.
351         @param filename Name of the source file from whence the message was issued.
352         @param line Line number from whence the message was issued.
353         @param xid Transaction id (or an empty string if there is no transaction).
354         @param msg Message text.
355
356         Open the log file named by _osrfLogFile, in append mode; write the message; close the
357         file.  If unable to open the log file, write the message to standard error.
358 */
359 static void _osrfLogToFile( const char* label, long pid, const char* filename, int line,
360         const char* xid, const char* msg ) {
361
362         if( !label || !filename || !xid || !msg )
363                 return;           // missing parameter(s)
364
365         if(!_osrfLogFile)
366                 return;           // No log file defined
367
368         if(!_osrfLogAppname)
369                 _osrfLogAppname = strdup("osrf");   // apply default application name
370
371         char datebuf[36];
372         time_t t = time(NULL);
373         struct tm* tms = localtime(&t);
374         strftime(datebuf, sizeof( datebuf ), "%Y-%m-%d %H:%M:%S", tms);
375
376         FILE* file = fopen(_osrfLogFile, "a");
377         if(!file) {
378                 fprintf(stderr,
379                         "Unable to fopen log file %s for writing; logging to standard error\n", _osrfLogFile);
380                 fprintf(stderr, "%s %s [%s:%ld:%s:%d:%s] %s\n",
381                         _osrfLogAppname, datebuf, label, (long) getpid(), filename, line, xid, msg );
382
383                 return;
384         }
385
386         fprintf(file, "%s %s [%s:%ld:%s:%d:%s] %s\n",
387                 _osrfLogAppname, datebuf, label, (long) getpid(), filename, line, xid, msg );
388         if( fclose(file) != 0 ) 
389                 fprintf( stderr, "Error closing log file: %s", strerror(errno));
390
391 }
392
393 int osrfLogFacilityToInt( const char* facility ) {
394         if(!facility) return LOG_LOCAL0;
395         if(strlen(facility) < 6) return LOG_LOCAL0;
396         switch( facility[5] ) {
397                 case '0': return LOG_LOCAL0;
398                 case '1': return LOG_LOCAL1;
399                 case '2': return LOG_LOCAL2;
400                 case '3': return LOG_LOCAL3;
401                 case '4': return LOG_LOCAL4;
402                 case '5': return LOG_LOCAL5;
403                 case '6': return LOG_LOCAL6;
404                 case '7': return LOG_LOCAL7;
405         }
406         return LOG_LOCAL0;
407 }
408
409