librnr  1.14.5
RoadNarrows Robotics Common Library 1
log.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: RoadNarrows Robotics Common Library 1
4 //
5 // Library: librnr
6 //
7 // File: log.c
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2014-12-06 13:48:30 -0700 (Sat, 06 Dec 2014) $
12  * $Rev: 3823 $
13  *
14  * \brief Logger definitions.
15  *
16  * Logging supports error and diagnostics (debugging) reports to stderr or a
17  * specified file. Logging reporting is filtered by a simple threshold level.
18  *
19  * \author Robin Knight (robin.knight@roadnarrows.com)
20  *
21  * \pkgcopyright{2005-2018,RoadNarrows LLC.,http://www.roadnarrows.com}
22  */
23 //
24 // Permission is hereby granted, without written agreement and without
25 // license or royalty fees, to use, copy, modify, and distribute this
26 // software and its documentation for any purpose, provided that
27 // (1) The above copyright notice and the following two paragraphs
28 // appear in all copies of the source code and (2) redistributions
29 // including binaries reproduces these notices in the supporting
30 // documentation. Substantial modifications to this software may be
31 // copyrighted by their authors and need not follow the licensing terms
32 // described here, provided that the new terms are clearly indicated in
33 // all files where they apply.
34 //
35 // IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
36 // OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
37 // PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
38 // DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
39 // EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
40 // THE POSSIBILITY OF SUCH DAMAGE.
41 //
42 // THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
43 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
44 // FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
45 // "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
46 // PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
47 //
48 ////////////////////////////////////////////////////////////////////////////////
49 
50 #include <stdio.h>
51 #include <stdarg.h>
52 #include <time.h>
53 #include <string.h>
54 #include <pthread.h>
55 
56 #include "rnr/rnrconfig.h"
57 #include "rnr/log.h"
58 #include "rnr/new.h"
59 
60 //
61 // Local variables in namespace.
62 //
63 #define LOG_VAR_FILENAME LOGNS_PUT(LogFileName) ///< define in namespace
64 #define LOG_VAR_FP LOGNS_PUT(LogFp) ///< define in namespace
65 
66 #ifdef LOG
68  ///< Log threshold level (log iff level <= threshold)
69 
71  ///< Log in color (false = disable, true = enable)
72 
74  ///< Timestamp logging (false = disable, true = enable)
75 
76 static const char *LOG_VAR_FILENAME = NULL; ///< Log File Name
77 static FILE *LOG_VAR_FP = NULL; ///< Opened Log file
78 
79 static pthread_mutex_t MutexLog = PTHREAD_MUTEX_INITIALIZER;
80 #endif // LOG
81 
82 /*!
83  * \brief Set new logging threshold level.
84  *
85  * All logging at the level <= threshold level will be enabled.
86  *
87  * This function is always defined allow blind sets.
88  *
89  * Actual Identifier: \p <LOGNS>LogSetThresholdLevel()
90  *
91  * \param nLevel New threshold level.
92  *
93  * \return New adjusted logging level. If logging is not availble
94  * (i.e. LOG was not defined) then LOGGING_NA is returned.
95  */
96 int LOG_SET_THRESHOLD(int nLevel)
97 {
98 #ifdef LOG
99  int nOldLevel;
100 
101  nOldLevel = LOG_VAR_THRESHOLD;
102 
103  if( nLevel < LOG_LEVEL_OFF )
104  {
106  }
107  else
108  {
109  LOG_VAR_THRESHOLD = nLevel;
110  }
111 
112  if( (nOldLevel >= LOG_LEVEL_DIAG1) || (LOG_VAR_THRESHOLD >= LOG_LEVEL_DIAG1) )
113  {
114  if( LOG_VAR_COLOR_EN )
115  {
116  LOGGER(LOGARGS_DIAG(2, "Logging level set to %d", LOG_VAR_THRESHOLD));
117  }
118  else
119  {
120  LOGGER(LOGARGS_DIAG_PLAIN(2, "Logging level set to %d",
122  }
123  }
124 
125  return LOG_VAR_THRESHOLD;
126 #else
127  return LOGGING_NA;
128 #endif // LOG
129 }
130 
131 /*!
132  * \brief Get current logging threshold level.
133  *
134  * This function is always defined to safeley determine if logging is
135  * available.
136  *
137  * Actual Identifier: \p <LOGNS>LogGetThresholdLevel()
138  *
139  * \return Current logging threshold. If logging is not available,
140  * (i.e. LOG was not defined) then LOGGING_NA is returned.
141  */
143 {
144 #ifdef LOG
145  return LOG_VAR_THRESHOLD;
146 #else
147  return LOGGING_NA;
148 #endif // LOG
149 }
150 
151 /*!
152  * \brief Enable/disable logging in color.
153  *
154  * This function is always defined to allow blind sets.
155  *
156  * Actual Identifier: \p <LOGNS>LogSetColorEnable()
157  *
158  * \param enable Enable(true) or disable(false).
159  */
161 {
162 #ifdef LOG
163  LOG_VAR_COLOR_EN = enable;
164 #endif // LOG
165 }
166 
167 /*!
168  * \brief Enable/disable log timestamps.
169  *
170  * This function is always defined to allow blind sets.
171  *
172  * Actual Identifier: \p <LOGNS>LogSetTimestampEnable()
173  *
174  * \param enable Enable(true) or disable(false).
175  */
177 {
178 #ifdef LOG
179  LOG_VAR_TIMESTAMP_EN = enable;
180 #endif // LOG
181 }
182 
183 /*!
184  * \brief Set new logging output file.
185  *
186  * The file is opened and truncated.
187  *
188  * This function is always defined allow blind sets.
189  *
190  * Actual Identifier: \p <LOGNS>LogSetLogFile()
191  *
192  * \todo On file open add time-stamp first entry.
193  *
194  * \param sLogFileName Name of new log file.
195  *
196  * \return On success, returns 0. On failure, return -1 with errno set
197  * appropriately.
198  */
199 int LOG_SET_LOGFILE(const char *sLogFileName)
200 {
201 #ifdef LOG
202  FILE *fp;
203 
204  // already opened
205  if( (LOG_VAR_FILENAME != NULL) && !strcmp(sLogFileName, LOG_VAR_FILENAME) )
206  {
207  return 0;
208  }
209 
210  //
211  // "Open" new log file.
212  //
213  if( !strcmp(sLogFileName, LOG_FILENAME_STDERR) )
214  {
215  fp = stderr;
216  }
217  else if( !strcmp(sLogFileName, LOG_FILENAME_STDOUT) )
218  {
219  fp = stdout;
220  }
221  else if( (fp = fopen(sLogFileName, "a+")) == NULL )
222  {
223  LOGSYSERROR("%s", sLogFileName);
224  return -1;
225  }
226 
227  LOGDIAG1("--- End Of Log %s ---", LOG_VAR_FILENAME);
228 
229  //
230  // Close old log file.
231  //
232  if( (LOG_VAR_FILENAME != NULL)
235  {
236  fclose(LOG_VAR_FP);
237  }
238 
239  //
240  // Set new log file data
241  //
242  LOG_VAR_FP = fp;
243  delete((char *)LOG_VAR_FILENAME);
244  LOG_VAR_FILENAME = new_strdup(sLogFileName);
245  LOGDIAG1("--- Start Of Log %s ---", LOG_VAR_FILENAME);
246 #endif // LOG
247 
248  return 0;
249 }
250 
251 /*!
252  * \brief Get logging output stream file name.
253  *
254  * Actual Identifier: \p <LOGNS>LogGetFileName()
255  *
256  * \return On success, returns 0. On failure, return -1 with errno set
257  * appropriately.
258  */
259 #ifdef LOG
260 const char *LOG_GET_LOGFILE()
261 {
262  if( LOG_VAR_FILENAME != NULL )
263  {
264  return LOG_VAR_FILENAME;
265  }
266  else
267  {
268  return LOG_FILENAME_DFT;
269  }
270 }
271 #endif // LOG
272 
273 /*!
274  * \brief Attach opened file pointer as the new logging output stream.
275  *
276  * Actual Identifier: \p <LOGNS>LogAttachLogFp()
277  *
278  * \param fp Opened FILE*.
279  * \param sFpFileName File name associated with fp.
280  */
281 #ifdef LOG
282 void LOG_ATTACH_LOGFP(FILE *fp, const char *sFpFileName)
283 {
284  LOG_VAR_FP = fp;
285  delete((char *)LOG_VAR_FILENAME);
286  LOG_VAR_FILENAME = new_strdup(sFpFileName);
287 }
288 #endif // LOG
289 
290 /*!
291  * \brief Get current logging output stream file pointer.
292  *
293  * This function is handing if an application has some complicated output
294  * for the logging stream.
295  *
296  * Actual Identifier: \p <LOGNS>LogGetLogFp()
297  *
298  * \return FILE*
299  */
300 #ifdef LOG
302 {
303  return LOG_VAR_FP;
304 }
305 #endif // LOG
306 
307 /*!
308  * \brief Print loggging diagnostics, debug, error, and system error messages
309  * to log output stream..
310  *
311  * Actual Identifier: \p <LOGNS>LogPrintf()
312  *
313  * \param sFmt Format string.
314  * \param ... Variable format arguments.
315  */
316 #ifdef LOG
317 void LOGGER(const char *sFmt, ...)
318 {
319  va_list ap;
320 
321  pthread_mutex_lock(&MutexLog);
322 
323  // lazy init
324  if( LOG_VAR_FP == NULL )
325  {
328  }
329 
330  if( LOG_WITH_TIMESTAMP() )
331  {
332  struct timespec tsNow;
333 
334  clock_gettime(CLOCK_REALTIME, &tsNow);
335  fprintf(LOG_VAR_FP, "[%ld.%09ld] ", tsNow.tv_sec, tsNow.tv_nsec);
336  }
337 
338  va_start(ap, sFmt);
339  vfprintf(LOG_VAR_FP, sFmt, ap);
340  fprintf(LOG_VAR_FP, "\n");
341  va_end(ap);
342  fflush(LOG_VAR_FP);
343 
344  pthread_mutex_unlock(&MutexLog);
345 }
346 #endif // LOG
347 
348 /*!
349  * \brief Parse function call argument format string to determine type.
350  *
351  * Type range is determined by what va_arg() takes.
352  *
353  * \param sFmt Argument format string.
354  *
355  * \return Quasi-specifier as in fprintf(3).
356  * Returns '?' for unreckonized formats.
357  */
358 #ifdef LOG
359 static int LogVaFmtType(char *sFmt)
360 {
361  bool_t bPercent = false;
362  char *s;
363  char last;
364  char length = 'i';
365 
366  for(last=0, s=sFmt; s && *s; last = *s, s++)
367  {
368  if( *s == '%')
369  {
370  if( last == '%' )
371  {
372  bPercent = false;
373  length = 'i';
374  }
375  else
376  {
377  bPercent = true;
378  }
379  }
380 
381  else if( bPercent )
382  {
383  switch( *s )
384  {
385  case 'h':
386  length = 'h'; // short
387  break;
388  case 'l':
389  length = 'l'; // long
390  break;
391  case 'c':
392  return 'c'; // char
393  case 'e':
394  case 'E':
395  case 'f':
396  case 'F':
397  case 'g':
398  case 'G':
399  return 'f'; // double
400  case 'd':
401  case 'i':
402  switch( length )
403  {
404  case 'l':
405  return 'l'; // long integer
406  default:
407  return 'd'; // integer
408  }
409  break;
410  case 'o':
411  case 'u':
412  case 'x':
413  case 'X':
414  switch( length )
415  {
416  case 'l':
417  return 'U'; // long unsigned
418  default:
419  return 'u'; // unsigned integer
420  }
421  break;
422  case 's':
423  return 's'; // string
424  case 'p':
425  return 'p'; // pointer
426  }
427  }
428  }
429  return '?';
430 }
431 
432 /*!
433  * \brief Print function call diagnostics tracing to log output stream..
434  *
435  * Actual Identifier: \p <LOGNS>LogCallPrintf()
436  *
437  * \param sPreface Logging preface string.
438  * \param nLevel Logging level.
439  * \param sFile File holding function definition.
440  * \param nLine File line number.
441  * \param sFuncName Function name string.
442  * \param ... Pairs of function format_string,argument pairs terminated
443  * by NULL,0
444  */
445 void LOGGER_CALL(const char *sPreface, int nLevel, const char *sFile, int nLine,
446  const char *sFuncName, ...)
447 {
448  bool_t bIsOk = true;
449  char *sArgFmt;
450  char *sSep = "";
451  va_list ap;
452 
453  pthread_mutex_lock(&MutexLog);
454 
455  // lazy init
456  if( LOG_VAR_FP == NULL )
457  {
460  }
461 
462  if( LOG_WITH_TIMESTAMP() )
463  {
464  struct timespec tsNow;
465 
466  clock_gettime(CLOCK_REALTIME, &tsNow);
467  fprintf(LOG_VAR_FP, "[%ld.%09ld] ", tsNow.tv_sec, tsNow.tv_nsec);
468  }
469 
470  va_start(ap, sFuncName);
471 
472  if( LogColorEnable )
473  {
474  fprintf(LOG_VAR_FP,
475  "%s" LOG_COLOR_DIAG "Diag%d: %s[%d] " LOG_COLOR_POST "%s(",
476  sPreface, nLevel-1, sFile, nLine, sFuncName);
477  }
478  else
479  {
480  fprintf(LOG_VAR_FP,
481  "%s" "Diag%d: %s[%d] " "%s(",
482  sPreface, nLevel-1, sFile, nLine, sFuncName);
483  }
484 
485  while( bIsOk )
486  {
487  // function call argument format
488  sArgFmt = va_arg(ap, char *);
489  if( sArgFmt == NULL )
490  {
491  break;
492  }
493 
494  // separator
495  fprintf(LOG_VAR_FP, "%s", sSep);
496  sSep = ",";
497 
498  // function call argument type and print
499  switch( LogVaFmtType(sArgFmt) )
500  {
501  case 'd':
502  fprintf(LOG_VAR_FP, sArgFmt, va_arg(ap, int));
503  break;
504  case 'l':
505  fprintf(LOG_VAR_FP, sArgFmt, va_arg(ap, long));
506  break;
507  case 'u':
508  fprintf(LOG_VAR_FP, sArgFmt, va_arg(ap, unsigned int));
509  break;
510  case 'U':
511  fprintf(LOG_VAR_FP, sArgFmt, va_arg(ap, unsigned long));
512  break;
513  case 'f':
514  fprintf(LOG_VAR_FP, sArgFmt, va_arg(ap, double));
515  break;
516  case 's':
517  fprintf(LOG_VAR_FP, sArgFmt, va_arg(ap, char *));
518  break;
519  case 'p':
520  fprintf(LOG_VAR_FP, sArgFmt, va_arg(ap, void *));
521  break;
522  case 'c':
523  fprintf(LOG_VAR_FP, sArgFmt, va_arg(ap, int));
524  break;
525  default:
526  fprintf(LOG_VAR_FP, "UNKNOWN_FORMAT");
527  bIsOk = false;
528  break;
529  }
530  }
531 
532  fprintf(LOG_VAR_FP, ")\n");
533  va_end(ap);
534 
535  fflush(LOG_VAR_FP);
536 
537  pthread_mutex_unlock(&MutexLog);
538 }
539 #endif // LOG
#define LOG_WITH_TIMESTAMP()
Test if logging includes timestamps.
Definition: log.h:205
void LOG_SET_TIMESTAMP_ENABLE(bool_t enable)
Enable/disable log timestamps.
Definition: log.c:176
void LOG_SET_COLOR_ENABLE(bool_t enable)
Enable/disable logging in color.
Definition: log.c:160
#define LOGARGS_DIAG(level, fmt,...)
Standard diagnostic logging output arguments with compiled color.
Definition: log.h:269
#define LOG_LEVEL_OFF
turn off all non-error logging
Definition: log.h:179
FILE * LOG_GET_LOGFP()
Get current logging output stream file pointer.
Definition: log.c:301
#define LOG_COLOR_EN_DFT
default is to log in color
Definition: log.h:218
#define LOG_FP_DFT
default log out stream
Definition: log.h:213
#define LOG_VAR_FILENAME
define in namespace
Definition: log.c:63
char * new_strdup(const char *s)
Duplicate a string.
Definition: new.c:176
int LOG_GET_THRESHOLD()
Get current logging threshold level.
Definition: log.c:142
#define LOG_TIMESTAMP_EN_DFT
default is to include timestamps
Definition: log.h:219
#define NULL
null pointer
Definition: rnrconfig.h:199
#define LOG_COLOR_POST
color escape sequence postfix
Definition: log.h:231
int LOG_SET_THRESHOLD(int nLevel)
Set new logging threshold level.
Definition: log.c:96
#define LOGARGS_DIAG_PLAIN(level, fmt,...)
Standard diagnostic logging output arguments in plain text.
Definition: log.h:279
Memory allocation and deallocation declarations.
#define LOGDIAG1(fmt,...)
Standard Diagnostic Level 1 logging.
Definition: log.h:407
#define LOGSYSERROR(fmt,...)
Standard System Error logging.
Definition: log.h:509
void LOG_ATTACH_LOGFP(FILE *fp, const char *sFpFileName)
Attach opened file pointer as the new logging output stream.
Definition: log.c:282
int LOG_SET_LOGFILE(const char *sLogFileName)
Set new logging output file.
Definition: log.c:199
bool_t LOG_VAR_COLOR_EN
Log in color (false = disable, true = enable)
Definition: log.c:70
int LOG_VAR_THRESHOLD
Log threshold level (log iff level <= threshold)
Definition: log.c:67
#define LOG_FILENAME_STDOUT
&#39;stdout&#39; log filename
Definition: log.h:211
static bool_t LOG_VAR_TIMESTAMP_EN
Timestamp logging (false = disable, true = enable)
Definition: log.c:73
const char * LOG_GET_LOGFILE()
Get logging output stream file name.
Definition: log.c:260
#define LOGGING_NA
logging not available (not compiled)
Definition: log.h:178
#define LOG_LEVEL_DFT
default log level is off
Definition: log.h:187
int bool_t
"boolean" T/F
Definition: rnrconfig.h:187
RoadNarrows Robotics common configuration file.
#define LOG_VAR_FP
define in namespace
Definition: log.c:64
#define LOG_LEVEL_DIAG1
diagnostic level 1
Definition: log.h:181
static int LogVaFmtType(char *sFmt)
Parse function call argument format string to determine type.
Definition: log.c:359
#define LOG_FILENAME_DFT
default log filename
Definition: log.h:212
void LOGGER_CALL(const char *sPreface, int nLevel, const char *sFile, int nLine, const char *sFuncName,...)
Print function call diagnostics tracing to log output stream..
Definition: log.c:445
Logger declarations.
bool_t LogColorEnable
color logging is [not] enabled
#define LOG_FILENAME_STDERR
&#39;stderr&#39; log filename
Definition: log.h:210
#define LOG_COLOR_DIAG
diagnostics color
Definition: log.h:247
void LOGGER(const char *sFmt,...)
Print loggging diagnostics, debug, error, and system error messages to log output stream...
Definition: log.c:317