1bb27474dba2c0fe6b58364b08fa4ebd97bf1ba1
[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 /** Pseudo-log type indicating that no previous log type was defined.
9         See also _prevLogType. */
10 #define OSRF_NO_LOG_TYPE -1
11
12 /** Stores a log type during temporary redirections to standard error. */
13 static int _prevLogType             = OSRF_NO_LOG_TYPE;
14 /** Defines the destination of log messages: standard error, a log file, or Syslog. */
15 static int _osrfLogType                         = OSRF_LOG_TYPE_STDERR;
16 /** Defines the Syslog facility number used for log messages other than activity messages.
17         Defaults to LOG_LOCAL0. */
18 static int _osrfLogFacility                     = LOG_LOCAL0;
19 /** Defines the Syslog facility number used for activity messages.  Defaults to LOG_LOCAL1. */
20 static int _osrfLogActFacility          = LOG_LOCAL1;
21 /** Name of the log file. */
22 static char* _osrfLogFile                       = NULL;
23 /** Application name.  This string will preface every log message. */
24 static char* _osrfLogAppname            = NULL;
25 static char* _osrfLogTag                = NULL;
26 /** Maximum message level.  Messages of higher levels will be suppressed.
27         Default: OSRF_LOG_INFO. */
28 static int _osrfLogLevel                        = OSRF_LOG_INFO;
29 /** Boolean.  If true, activity message are enabled.  Default: true. */
30 static int _osrfLogActivityEnabled      = 1;
31 /** Boolean.  If true, the current process is a client; otherwise it's a server.  Clients and
32         servers have different options with regard to transaction ids.  See osrfLogSetXid(),
33         osrfLogForceXid(), and osrfLogMkXid().  Default: false. */
34 static int _osrfLogIsClient         = 0;
35
36 /** An id identifying the current transaction.  If defined, it is included into every
37         log message. */
38 static char* _osrfLogXid            = NULL; /* current xid */
39 /** A prefix used to generate transaction ids.  It incorporates a timestamp and a process id. */
40 static char* _osrfLogXidPfx         = NULL; /* xid prefix string */
41
42 static void osrfLogSetType( int logtype );
43 static void _osrfLogDetail( int level, const char* filename, int line, char* msg );
44 static void _osrfLogToFile( const char* label, long pid, const char* filename, int line,
45                                                         const char* xid, const char* msg );
46 static void _osrfLogSetXid( const char* xid );
47
48 /**
49         @brief Reset certain local static variables to their initial values.
50
51         Of the various static variables, we here reset only three:
52         - application name (deleted)
53         - file name of log file (deleted)
54         - log type (reset to OSRF_LOG_TYPE_STDERR)
55 */
56 void osrfLogCleanup( void ) {
57         if (_osrfLogTag)
58                 free(_osrfLogTag);
59         _osrfLogTag = NULL;
60         free(_osrfLogAppname);
61         _osrfLogAppname = NULL;
62         free(_osrfLogFile);
63         _osrfLogFile = NULL;
64         _osrfLogType = OSRF_LOG_TYPE_STDERR;
65 }
66
67
68 /**
69         @brief Record some options for later reference by the logging routines.
70         @param type Type of logging; i.e. where the log messages go.
71         @param appname Pointer to the application name (may be NULL).
72         @param maxlevel Which levels of message to issue or suppress.
73
74         Typically the values for these parameters come from a configuration file.
75
76         There are three valid values for @a type:
77
78         - OSRF_LOG_TYPE_FILE -- write messages to a log file
79         - OSRF_LOG_TYPE_SYSLOG -- write messages to the syslog facility
80         - OSRF_LOG_TYPE_STDERR  -- write messages to standard error
81
82         If @a type has any other value, log messages will be written to standard error.
83
84         The logging type may be set separately by calling osrfLogSetType().  See also
85         osrfLogToStderr() and osrfRestoreLogType().
86
87         The @a appname string prefaces every log message.  The default application name, if
88         not overridden by this function or by osrfLogSetAppname(), is "osrf".
89
90         Here are the valid values for @a maxlevel, with the corresponding macros:
91
92         - 1 OSRF_LOG_ERROR
93         - 2 OSRF_LOG_WARNING
94         - 3 OSRF_LOG_INFO (the default)
95         - 4 OSRF_LOG_DEBUG
96         - 5 OSRF_LOG_INTERNAL
97
98         With the special exception of activity messages (see osrfLogActivity()), the logging
99         routines will suppress any messages at a level greater than that specified by
100         @a maxlevel.  Setting @a maxlevel to zero or less suppresses all levels of message.
101         Setting it to 5 or more enables all levels of message.
102
103         The message level may be set separately by calling osrfLogSetLevel().
104 */
105 void osrfLogInit( int type, const char* appname, int maxlevel ) {
106         osrfLogSetType(type);
107         if(appname) osrfLogSetAppname(appname);
108         osrfLogSetLevel(maxlevel);
109         if( type == OSRF_LOG_TYPE_SYSLOG ) 
110                 openlog(_osrfLogAppname, 0, _osrfLogFacility );
111 }
112
113 /**
114         @brief Store a copy of a transaction id for future use.
115         @param xid Pointer to the transaction id to be stored.
116 */
117 static void _osrfLogSetXid( const char* xid ) {
118    if(xid) {
119       if(_osrfLogXid) free(_osrfLogXid);
120       _osrfLogXid = strdup(xid);
121    }
122 }
123
124 /**
125         @brief Store an empty string as the transaction id.
126 */
127 void osrfLogClearXid( void ) { _osrfLogSetXid(""); }
128
129 /**
130         @brief Store a transaction id, unless running as a client process.
131         @param xid Pointer to the new transaction id
132 */
133 void osrfLogSetXid(const char* xid) {
134    if(!_osrfLogIsClient) _osrfLogSetXid(xid);
135 }
136
137 /**
138         @brief Store a transaction id for future use, whether running as a client or as a server.
139         @param xid Pointer to the new transaction id
140 */
141 void osrfLogForceXid(const char* xid) {
142    _osrfLogSetXid(xid);
143 }
144
145 /**
146         @brief For client processes only: create and store a unique transaction id.
147
148         The generated transaction id concatenates a prefix (which must have been generated
149         previously by osrfLogSetIsClient()) and a sequence number that is incremented on each
150         call.
151
152         Since the various pieces of the transaction id are of variable length, and not separated
153         by any non-numeric characters, the result is not guaranteed to be unique.  However
154         collisions should be rare.
155 */
156 void osrfLogMkXid( void ) {
157    if(_osrfLogIsClient) {
158       static int _osrfLogXidInc = 0; /* increments with each new xid for uniqueness */
159       char buf[32];
160       snprintf(buf, sizeof(buf), "%s%d", _osrfLogXidPfx, _osrfLogXidInc);
161       _osrfLogSetXid(buf);
162       _osrfLogXidInc++;
163    }
164 }
165
166 /**
167         @brief Return a pointer to the currently stored transaction id, if any.
168         @return Pointer to the currently stored transaction id.
169
170         If no transaction id has been stored, return NULL.
171 */
172 const char* osrfLogGetXid( void ) {
173    return _osrfLogXid;
174 }
175
176 /**
177         @brief Note whether the current process is a client; if so, generate a transaction prefix.
178         @param is A boolean; true means the current process is a client, false means it isn't.
179
180         The generated prefix concatenates a timestamp (the return from time()) and a process id.
181 */
182 void osrfLogSetIsClient(int is) {
183    _osrfLogIsClient = is;
184    if(!is) return;
185    /* go ahead and create the xid prefix so it will be consistent later */
186    static char buff[32];
187    snprintf(buff, sizeof(buff), "%d%ld", (int)time(NULL), (long) getpid());
188    _osrfLogXidPfx = buff;
189 }
190
191 /**
192         @brief Specify what kind of logging to perform.
193         @param logtype A code indicating the type of logging.
194
195         There are three valid values for @a logtype:
196
197         - OSRF_LOG_TYPE_FILE -- write messages to a log file
198         - OSRF_LOG_TYPE_SYSLOG -- write messages to the syslog facility
199         - OSRF_LOG_TYPE_STDERR  -- write messages to standard error
200
201         If @a logtype has any other value, log messages will be written to standard error.
202
203         This function merely records the log type for future reference.  It does not open
204         or close any files.
205
206         See also osrfLogInit(), osrfLogToStderr() and osrfRestoreLogType().
207
208 */
209 static void osrfLogSetType( int logtype ) {
210
211         switch( logtype )
212         {
213                 case OSRF_LOG_TYPE_FILE :
214                 case OSRF_LOG_TYPE_SYSLOG :
215                 case OSRF_LOG_TYPE_STDERR :
216                         _osrfLogType = logtype;
217                         break;
218                 default :
219                         fprintf(stderr, "Unrecognized log type.  Logging to stderr\n");
220                         _osrfLogType = OSRF_LOG_TYPE_STDERR;
221                         break;
222         }
223 }
224
225 /**
226         @brief Switch the logging output to standard error (but remember where it @em was going).
227
228         See also osrfRestoreLogType().
229 */
230 void osrfLogToStderr( void )
231 {
232         if( OSRF_NO_LOG_TYPE == _prevLogType ) {
233                 _prevLogType = _osrfLogType;
234                 _osrfLogType = OSRF_LOG_TYPE_STDERR;
235         }
236 }
237
238 /**
239         @brief Switch the logging output to wherever it was going before calling osrfLogtoStderr().
240
241         By using osrfRestoreLogType together with osrfRestoreLogType(), an application can
242         temporarily redirect log messages to standard error, either for an interactive
243         session or for debugging, and later revert to the original log output.
244 */
245 void osrfRestoreLogType( void )
246 {
247         if( _prevLogType != OSRF_NO_LOG_TYPE ) {
248                 _osrfLogType = _prevLogType;
249                 _prevLogType = OSRF_NO_LOG_TYPE;
250         }
251 }
252
253 /**
254         @brief Store a file name for a log file.
255         @param logfile Pointer to the file name.
256
257         The new file name replaces whatever file name was previously in place, if any.
258
259         This function does not affect the logging type.  The choice of file name makes a
260         difference only when the logging type is OSRF_LOG_TYPE_FILE.
261 */
262 void osrfLogSetFile( const char* logfile ) {
263         if(!logfile) return;
264         if(_osrfLogFile) free(_osrfLogFile);
265         _osrfLogFile = strdup(logfile);
266 }
267
268 /**
269         @brief Enable the issuance of activity log messages.
270
271         By default, activity messages are enabled.  See also osrfLogActivity().
272 */
273 void osrfLogSetActivityEnabled( int enabled ) {
274         _osrfLogActivityEnabled = enabled;
275 }
276
277 /**
278         @brief Store an application name for future use.
279         @param appname Pointer to the application name to be stored.
280
281         The @a appname string prefaces every log message.  The default application name is "osrf".
282 */
283 void osrfLogSetAppname( const char* appname ) {
284         if(!appname) return;
285
286         char buf[256];
287         if(_osrfLogTag) {
288                 snprintf(buf, sizeof(buf), "%s/%s", appname, _osrfLogTag);
289         } else {
290                 snprintf(buf, sizeof(buf), "%s", appname);
291         }
292
293         if(_osrfLogAppname) free(_osrfLogAppname);
294         _osrfLogAppname = strdup(buf);
295
296         /* if syslogging, re-open the log with the appname */
297         if( _osrfLogType == OSRF_LOG_TYPE_SYSLOG) {
298                 closelog();
299                 openlog(_osrfLogAppname, 0, _osrfLogFacility);
300         }
301 }
302
303 /**
304         @brief Store a facility number for future use.
305         @param facility The facility number to be stored.
306
307         A facility is a small integer passed to the Syslog system to characterize the source
308         of a message.  The value of this integer is typically derived from a configuration file.
309         If not otherwise specified, it defaults to LOG_LOCAL0.
310 */
311 void osrfLogSetSyslogFacility( int facility ) {
312         _osrfLogFacility = facility;
313 }
314
315 /**
316         @brief Store an arbitrary program name tag for future use.
317         @param logtag The string to be stored.
318
319         A log tag is a short string that is appended to the appname
320         we log under.  This can be used to segregate logs from different
321         users in, for instance, rsyslogd.
322 */
323
324 void osrfLogSetLogTag( const char* logtag ) {
325         if (logtag) _osrfLogTag = strdup(logtag);
326 }
327
328 /**
329         @brief Store a facility number for future use for activity messages.
330         @param facility The facility number to be stored.
331
332         A facility is a small integer passed to the Syslog system to characterize the source
333         of a message.  The value of this integer is typically derived from a configuration file.
334
335         The facility used for activity messages is separate and distinct from that used for
336         other log messages, and defaults to LOG_LOCAL1.
337  */
338 void osrfLogSetSyslogActFacility( int facility ) {
339         _osrfLogActFacility = facility;
340 }
341
342 /**
343         @brief Set the maximum level of messages to be issued.
344         @param loglevel The maximum message level.
345
346         A log message will be issued only if its level is less than or equal to this maximum.
347         For example, if @a loglevel is set to OSRF_LOG_INFO, then the logging routines will
348         issue information messages, warning messages, and error messages, but not debugging
349         messages or internal messages.
350
351         The default message level is OSRF_LOG_INFO.
352 */
353 void osrfLogSetLevel( int loglevel ) {
354         _osrfLogLevel = loglevel;
355 }
356
357 /**
358         @brief Get the current log message level.
359         @return The current log message level.
360
361         See also osrfLogSetLevel().
362  */
363 int osrfLogGetLevel( void ) {
364         return _osrfLogLevel;
365 }
366
367 /**
368         @name Message Logging Functions
369         
370         These five functions are the most widely used of the logging routines.  They all work
371         the same way, differing only in the levels of the messages they log, and in the tags
372         they use within those messages to indicate the message level.
373
374         The first two parameters define the location in the source code where the function is
375         called: the name of the source file and the line number.  In practice these are normally
376         provided through use of the OSRF_LOG_MARK macro.
377
378         The third parameter is a printf-style format string, which will be expanded to form the
379         message text.  Subsequent parameters, if any, will be formatted and inserted into the
380         expanded message text.
381
382         Depending on the current maximum message level, the message may or may not actually be
383         issued.  See also osrfLogSetLevel().
384 */
385 /*@{*/
386
387 /**
388         @brief Log an error message.
389         @param file The file name of the source code calling the function.
390         @param line The line number of the source code calling the function.
391         @param msg A printf-style format string that will be expanded to form the message text,
392
393         Tag: "ERR".
394 */
395 void osrfLogError( const char* file, int line, const char* msg, ... ) {
396         if( !msg ) return;
397         if( _osrfLogLevel < OSRF_LOG_ERROR ) return;
398         VA_LIST_TO_STRING( msg );
399         _osrfLogDetail( OSRF_LOG_ERROR, file, line, VA_BUF );
400 }
401
402 /**
403         @brief Log a warning message.
404         @param file The file name of the source code calling the function.
405         @param line The line number of the source code calling the function.
406         @param msg A printf-style format string that will be expanded to form the message text,
407
408         Tag: "WARN".
409  */
410 void osrfLogWarning( const char* file, int line, const char* msg, ... ) {
411         if( !msg ) return;
412         if( _osrfLogLevel < OSRF_LOG_WARNING ) return;
413         VA_LIST_TO_STRING( msg );
414         _osrfLogDetail( OSRF_LOG_WARNING, file, line, VA_BUF );
415 }
416
417 /**
418         @brief Log an informational message.
419         @param file The file name of the source code calling the function.
420         @param line The line number of the source code calling the function.
421         @param msg A printf-style format string that will be expanded to form the message text,
422
423         Tag: "INFO".
424  */
425 void osrfLogInfo( const char* file, int line, const char* msg, ... ) {
426         if( !msg ) return;
427         if( _osrfLogLevel < OSRF_LOG_INFO ) return;
428         VA_LIST_TO_STRING( msg );
429         _osrfLogDetail( OSRF_LOG_INFO, file, line, VA_BUF );
430 }
431
432 /**
433         @brief Log a debug message.
434         @param file The file name of the source code calling the function.
435         @param line The line number of the source code calling the function.
436         @param msg A printf-style format string that will be expanded to form the message text,
437  
438         Tag: "DEBG".
439  */
440 void osrfLogDebug( const char* file, int line, const char* msg, ... ) {
441         if( !msg ) return;
442         if( _osrfLogLevel < OSRF_LOG_DEBUG ) return;
443         VA_LIST_TO_STRING( msg );
444         _osrfLogDetail( OSRF_LOG_DEBUG, file, line, VA_BUF );
445 }
446
447 /**
448         @brief Log an internal message.
449         @param file The file name of the source code calling the function.
450         @param line The line number of the source code calling the function.
451         @param msg A printf-style format string that will be expanded to form the message text,
452
453         Tag: "INT ".
454  */
455 void osrfLogInternal( const char* file, int line, const char* msg, ... ) {
456         if( !msg ) return;
457         if( _osrfLogLevel < OSRF_LOG_INTERNAL ) return;
458         VA_LIST_TO_STRING( msg );
459         _osrfLogDetail( OSRF_LOG_INTERNAL, file, line, VA_BUF );
460 }
461
462 /*@}*/
463
464 /**
465         @brief Issue activity log message.
466         @param file The file name of the source code calling the function.
467         @param line The line number of the source code calling the function.
468         @param msg A printf-style format string that will be expanded to form the message text,
469
470         Tag: "ACT".
471
472         Activity messages behave like informational messages, with the following differences:
473         - They are tagged "ACT" instead of "INFO";
474         - Though enabled by default, they may be disabled by a previous call to
475                 osrfLogSetActivityEnabled();
476         - When Syslog is in use, they are assigned a separate facility number, which defaults
477                 to LOG_LOCAL1 instead of LOG_LOCAL0.  See also osrfLogSetSyslogActFacility().
478 */
479 void osrfLogActivity( const char* file, int line, const char* msg, ... ) {
480         if( !msg ) return;
481         if( _osrfLogLevel >= OSRF_LOG_INFO
482                 || ( _osrfLogActivityEnabled && _osrfLogLevel >= OSRF_LOG_ACTIVITY ) )
483         {
484                 VA_LIST_TO_STRING( msg );
485
486                 if( _osrfLogActivityEnabled && _osrfLogLevel >= OSRF_LOG_ACTIVITY )
487                         _osrfLogDetail( OSRF_LOG_ACTIVITY, file, line, VA_BUF );
488
489                 /* also log at info level */
490                 if( _osrfLogLevel >= OSRF_LOG_INFO )
491                         _osrfLogDetail( OSRF_LOG_INFO, file, line, VA_BUF );
492         }
493 }
494
495 /**
496         @brief Issue a log message.
497         @param level The message level.
498         @param filename The name of the source file from whence the message is issued.
499         @param line The line number from whence the message is issued.
500         @param msg The text of the message.
501
502         This function is the final common pathway for all messages.
503
504         The @a level parameter determines the tag to be incorporated into the message: "ERR",
505         "WARN", "INFO", "DEBG", "INT " or "ACT".
506
507         The @a filename and @a name identify the location in the application code from whence the
508         message is being issued.
509
510         Here we format the message and route it to the appropriate output destination, depending
511         on the current setting of _osrfLogType: Syslog, a log file, or standard error.
512
513         If the logging type has been set to OSRF_LOG_TYPE_FILE, but no file name has been
514         defined for the log file, the message is written to standard error.
515 */
516 static void _osrfLogDetail( int level, const char* filename, int line, char* msg ) {
517
518         if(!filename) filename = "";
519
520         char* label = "INFO";           /* level name */
521         int lvl = LOG_INFO;     /* syslog level */
522         int fac = _osrfLogFacility;
523
524         switch( level ) {
525                 case OSRF_LOG_ERROR:            
526                         label = "ERR "; 
527                         lvl = LOG_ERR;
528                         break;
529
530                 case OSRF_LOG_WARNING:  
531                         label = "WARN"; 
532                         lvl = LOG_WARNING;
533                         break;
534
535                 case OSRF_LOG_INFO:             
536                         label = "INFO"; 
537                         lvl = LOG_INFO;
538                         break;
539
540                 case OSRF_LOG_DEBUG:    
541                         label = "DEBG"; 
542                         lvl = LOG_DEBUG;
543                         break;
544
545                 case OSRF_LOG_INTERNAL: 
546                         label = "INT "; 
547                         lvl = LOG_DEBUG;
548                         break;
549
550                 case OSRF_LOG_ACTIVITY: 
551                         label = "ACT"; 
552                         lvl = LOG_INFO;
553                         fac = _osrfLogActFacility;
554                         break;
555         }
556
557    char* xid = (_osrfLogXid) ? _osrfLogXid : "";
558
559    int logtype = _osrfLogType;
560    if( logtype == OSRF_LOG_TYPE_FILE && !_osrfLogFile )
561    {
562            // No log file defined?  Temporarily reroute to stderr
563            logtype = OSRF_LOG_TYPE_STDERR;
564    }
565
566    if( logtype == OSRF_LOG_TYPE_SYSLOG ) {
567                 char buf[1536];  
568                 buf[0] = '\0';
569                 /* give syslog some breathing room, and be cute about it */
570                 strncat(buf, msg, 1535);
571                 buf[1532] = '.';
572                 buf[1533] = '.';
573                 buf[1534] = '.';
574                 buf[1535] = '\0';
575                 syslog( fac | lvl, "[%s:%ld:%s:%d:%s] %s", label, (long) getpid(), filename, line, xid, buf );
576         }
577
578         else if( logtype == OSRF_LOG_TYPE_FILE )
579                 _osrfLogToFile( label, (long) getpid(), filename, line, xid, msg );
580
581         else if( logtype == OSRF_LOG_TYPE_STDERR )
582                 fprintf( stderr, "[%s:%ld:%s:%d:%s] %s\n", label, (long) getpid(), filename, line, xid, msg );
583 }
584
585
586 /**
587         @brief Write a message to a log file.
588         @param label The message type: "ERR", "WARN", etc..
589         @param pid The process id.
590         @param filename Name of the source file from whence the message was issued.
591         @param line Line number from whence the message was issued.
592         @param xid Transaction id (or an empty string if there is no transaction).
593         @param msg Message text.
594
595         Open the log file named by _osrfLogFile, in append mode; write the message; close the
596         file.  If unable to open the log file, write the message to standard error.
597 */
598 static void _osrfLogToFile( const char* label, long pid, const char* filename, int line,
599         const char* xid, const char* msg ) {
600
601         if( !label || !filename || !xid || !msg )
602                 return;           // missing parameter(s)
603
604         if(!_osrfLogFile)
605                 return;           // No log file defined
606
607         if(!_osrfLogAppname)
608                 _osrfLogAppname = strdup("osrf");   // apply default application name
609
610         char datebuf[36];
611         time_t t = time(NULL);
612         struct tm* tms = localtime(&t);
613         strftime(datebuf, sizeof( datebuf ), "%Y-%m-%d %H:%M:%S", tms);
614
615         FILE* file = fopen(_osrfLogFile, "a");
616         if(!file) {
617                 fprintf(stderr,
618                         "Unable to fopen log file %s for writing; logging to standard error\n", _osrfLogFile);
619                 fprintf(stderr, "%s %s [%s:%ld:%s:%d:%s] %s\n",
620                         _osrfLogAppname, datebuf, label, (long) getpid(), filename, line, xid, msg );
621
622                 return;
623         }
624
625         fprintf(file, "%s %s [%s:%ld:%s:%d:%s] %s\n",
626                 _osrfLogAppname, datebuf, label, (long) getpid(), filename, line, xid, msg );
627         if( fclose(file) != 0 ) 
628                 fprintf( stderr, "Error closing log file: %s", strerror(errno));
629
630 }
631
632 /**
633         @brief Translate a character string to a facility number for Syslog.
634         @param facility The string to be translated.
635         @return An integer in the range 0 through 7.
636
637         Take the sixth character (counting from 1).  If it's a digit in the range '0' through '7',
638         return the corresponding value: LOG_LOCAL0, LOG_LOCAL1, etc...  Otherwise -- or if the
639         string isn't long enough -- return LOG_LOCAL0 as a default.
640
641         Example: "LOCAL3" => LOG_LOCAL3.
642
643         (Syslog uses the LOG_LOCALx macros to designate different kinds of locally defined
644         facilities that may issue messages.  Depending on the configuration, Syslog mey handle
645         messages from different facilities differently.)
646 */
647 int osrfLogFacilityToInt( const char* facility ) {
648         if(!facility) return LOG_LOCAL0;
649         if(strlen(facility) < 6) return LOG_LOCAL0;
650         switch( facility[5] ) {
651                 case '0': return LOG_LOCAL0;
652                 case '1': return LOG_LOCAL1;
653                 case '2': return LOG_LOCAL2;
654                 case '3': return LOG_LOCAL3;
655                 case '4': return LOG_LOCAL4;
656                 case '5': return LOG_LOCAL5;
657                 case '6': return LOG_LOCAL6;
658                 case '7': return LOG_LOCAL7;
659         }
660         return LOG_LOCAL0;
661 }
662
663