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