NeoMutt  2020-06-26-89-g172cd3
Teaching an old dog new tricks
DOXYGEN
logging.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <errno.h>
31 #include <stdarg.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include "logging.h"
38 #include "date.h"
39 #include "file.h"
40 #include "memory.h"
41 #include "message.h"
42 #include "queue.h"
43 #include "string2.h"
44 
45 const char *LevelAbbr = "PEWM12345N";
46 
53 
54 FILE *LogFileFP = NULL;
55 char *LogFileName = NULL;
56 int LogFileLevel = 0;
57 char *LogFileVersion = NULL;
58 
62 static struct LogLineList LogQueue = STAILQ_HEAD_INITIALIZER(LogQueue);
63 
64 int LogQueueCount = 0;
65 int LogQueueMax = 0;
66 
77 static const char *timestamp(time_t stamp)
78 {
79  static char buf[23] = { 0 };
80  static time_t last = 0;
81 
82  if (stamp == 0)
83  stamp = mutt_date_epoch();
84 
85  if (stamp != last)
86  {
87  mutt_date_localtime_format(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", stamp);
88  last = stamp;
89  }
90 
91  return buf;
92 }
93 
98 void log_file_close(bool verbose)
99 {
100  if (!LogFileFP)
101  return;
102 
103  fprintf(LogFileFP, "[%s] Closing log.\n", timestamp(0));
104  fprintf(LogFileFP, "# vim: syntax=neomuttlog\n");
106  if (verbose)
107  mutt_message(_("Closed log file: %s"), LogFileName);
108 }
109 
119 int log_file_open(bool verbose)
120 {
121  if (!LogFileName)
122  return -1;
123 
124  if (LogFileFP)
125  log_file_close(false);
126 
127  if (LogFileLevel < LL_DEBUG1)
128  return -1;
129 
131  if (!LogFileFP)
132  return -1;
133  setvbuf(LogFileFP, NULL, _IOLBF, 0);
134 
135  fprintf(LogFileFP, "[%s] NeoMutt%s debugging at level %d\n", timestamp(0),
137  if (verbose)
138  mutt_message(_("Debugging at level %d to file '%s'"), LogFileLevel, LogFileName);
139  return 0;
140 }
141 
149 int log_file_set_filename(const char *file, bool verbose)
150 {
151  if (!file)
152  return -1;
153 
154  /* also handles both being NULL */
155  if (mutt_str_equal(LogFileName, file))
156  return 0;
157 
159 
160  if (!LogFileName)
161  log_file_close(verbose);
162 
163  return log_file_open(verbose);
164 }
165 
175 int log_file_set_level(enum LogLevel level, bool verbose)
176 {
177  if ((level < LL_MESSAGE) || (level >= LL_MAX))
178  return -1;
179 
180  if (level == LogFileLevel)
181  return 0;
182 
183  LogFileLevel = level;
184 
185  if (level == LL_MESSAGE)
186  {
187  log_file_close(verbose);
188  }
189  else if (LogFileFP)
190  {
191  if (verbose)
192  mutt_message(_("Logging at level %d to file '%s'"), LogFileLevel, LogFileName);
193  fprintf(LogFileFP, "[%s] NeoMutt%s debugging at level %d\n", timestamp(0),
195  }
196  else
197  {
198  log_file_open(verbose);
199  }
200 
201  if (LogFileLevel >= LL_DEBUG5)
202  {
203  fprintf(LogFileFP,
204  "\n"
205  "WARNING:\n"
206  " Logging at this level can reveal personal information.\n"
207  " Review the log carefully before posting in bug reports.\n"
208  "\n");
209  }
210 
211  return 0;
212 }
213 
221 void log_file_set_version(const char *version)
222 {
223  mutt_str_replace(&LogFileVersion, version);
224 }
225 
231 {
232  return LogFileFP;
233 }
234 
255 int log_disp_file(time_t stamp, const char *file, int line,
256  const char *function, enum LogLevel level, ...)
257 {
258  if (!LogFileFP || (level < LL_PERROR) || (level > LogFileLevel))
259  return 0;
260 
261  int ret = 0;
262  int err = errno;
263 
264  if (!function)
265  function = "UNKNOWN";
266 
267  ret += fprintf(LogFileFP, "[%s]<%c> %s() ", timestamp(stamp),
268  LevelAbbr[level + 3], function);
269 
270  va_list ap;
271  va_start(ap, level);
272  const char *fmt = va_arg(ap, const char *);
273  ret += vfprintf(LogFileFP, fmt, ap);
274  va_end(ap);
275 
276  if (level == LL_PERROR)
277  {
278  fprintf(LogFileFP, ": %s\n", strerror(err));
279  }
280  else if (level <= LL_MESSAGE)
281  {
282  fputs("\n", LogFileFP);
283  ret++;
284  }
285 
286  return ret;
287 }
288 
296 int log_queue_add(struct LogLine *ll)
297 {
298  if (!ll)
299  return -1;
300 
301  STAILQ_INSERT_TAIL(&LogQueue, ll, entries);
302 
303  if ((LogQueueMax > 0) && (LogQueueCount >= LogQueueMax))
304  {
305  ll = STAILQ_FIRST(&LogQueue);
306  STAILQ_REMOVE_HEAD(&LogQueue, entries);
307  FREE(&ll->message);
308  FREE(&ll);
309  }
310  else
311  {
312  LogQueueCount++;
313  }
314  return LogQueueCount;
315 }
316 
324 {
325  if (size < 0)
326  size = 0;
327  LogQueueMax = size;
328 }
329 
335 void log_queue_empty(void)
336 {
337  struct LogLine *ll = NULL;
338  struct LogLine *tmp = NULL;
339 
340  STAILQ_FOREACH_SAFE(ll, &LogQueue, entries, tmp)
341  {
342  STAILQ_REMOVE(&LogQueue, ll, LogLine, entries);
343  FREE(&ll->message);
344  FREE(&ll);
345  }
346 
347  LogQueueCount = 0;
348 }
349 
358 {
359  struct LogLine *ll = NULL;
360  STAILQ_FOREACH(ll, &LogQueue, entries)
361  {
362  disp(ll->time, ll->file, ll->line, ll->function, ll->level, "%s", ll->message);
363  }
364 
365  log_queue_empty();
366 }
367 
378 int log_queue_save(FILE *fp)
379 {
380  if (!fp)
381  return 0;
382 
383  char buf[32];
384  int count = 0;
385  struct LogLine *ll = NULL;
386  STAILQ_FOREACH(ll, &LogQueue, entries)
387  {
388  mutt_date_localtime_format(buf, sizeof(buf), "%H:%M:%S", ll->time);
389  fprintf(fp, "[%s]<%c> %s", buf, LevelAbbr[ll->level + 3], ll->message);
390  if (ll->level <= LL_MESSAGE)
391  fputs("\n", fp);
392  count++;
393  }
394 
395  return count;
396 }
397 
409 int log_disp_queue(time_t stamp, const char *file, int line,
410  const char *function, enum LogLevel level, ...)
411 {
412  char buf[1024] = { 0 };
413  int err = errno;
414 
415  va_list ap;
416  va_start(ap, level);
417  const char *fmt = va_arg(ap, const char *);
418  int ret = vsnprintf(buf, sizeof(buf), fmt, ap);
419  va_end(ap);
420 
421  if (level == LL_PERROR)
422  {
423  ret += snprintf(buf + ret, sizeof(buf) - ret, ": %s", strerror(err));
424  level = LL_ERROR;
425  }
426 
427  struct LogLine *ll = mutt_mem_calloc(1, sizeof(*ll));
428  ll->time = (stamp != 0) ? stamp : mutt_date_epoch();
429  ll->file = file;
430  ll->line = line;
431  ll->function = function;
432  ll->level = level;
433  ll->message = mutt_str_dup(buf);
434 
435  log_queue_add(ll);
436 
437  return ret;
438 }
439 
450 int log_disp_terminal(time_t stamp, const char *file, int line,
451  const char *function, enum LogLevel level, ...)
452 {
453  if ((level < LL_PERROR) || (level > LL_MESSAGE))
454  return 0;
455 
456  char buf[1024];
457 
458  va_list ap;
459  va_start(ap, level);
460  const char *fmt = va_arg(ap, const char *);
461  int ret = vsnprintf(buf, sizeof(buf), fmt, ap);
462  va_end(ap);
463 
464  log_disp_file(stamp, file, line, function, level, "%s", buf);
465 
466  FILE *fp = (level < LL_MESSAGE) ? stderr : stdout;
467  int err = errno;
468  int colour = 0;
469  bool tty = (isatty(fileno(fp)) == 1);
470 
471  if (tty)
472  {
473  switch (level)
474  {
475  case LL_PERROR:
476  case LL_ERROR:
477  colour = 31;
478  break;
479  case LL_WARNING:
480  colour = 33;
481  break;
482  case LL_MESSAGE:
483  // colour = 36;
484  break;
485  case LL_DEBUG1:
486  case LL_DEBUG2:
487  case LL_DEBUG3:
488  case LL_DEBUG4:
489  case LL_DEBUG5:
490  case LL_NOTIFY:
491  default:
492  break;
493  }
494  }
495 
496  if (colour > 0)
497  ret += fprintf(fp, "\033[1;%dm", colour); // Escape
498 
499  fputs(buf, fp);
500 
501  if (level == LL_PERROR)
502  ret += fprintf(fp, ": %s", strerror(err));
503 
504  if (colour > 0)
505  ret += fprintf(fp, "\033[0m"); // Escape
506 
507  ret += fprintf(fp, "\n");
508 
509  return ret;
510 }
511 
515 int log_disp_null(time_t stamp, const char *file, int line,
516  const char *function, enum LogLevel level, ...)
517 {
518  return 0;
519 }
int log_disp_null(time_t stamp, const char *file, int line, const char *function, enum LogLevel level,...)
Discard log lines - Implements log_dispatcher_t.
Definition: logging.c:515
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:876
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:414
Log at debug level 4.
Definition: logging.h:43
Log error.
Definition: logging.h:37
void log_file_close(bool verbose)
Close the log file.
Definition: logging.c:98
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:399
size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
Format localtime.
Definition: date.c:678
enum LogLevel level
Log level, e.g. LL_DEBUG1.
Definition: logging.h:75
Memory management wrappers.
Log of notifications.
Definition: logging.h:45
const char * LevelAbbr
Abbreviations of logging level names.
Definition: logging.c:45
#define mutt_message(...)
Definition: logging.h:83
char * message
Message to be logged.
Definition: logging.h:76
FILE * LogFileFP
Log file handle.
Definition: logging.c:54
static struct LogLineList LogQueue
In-memory list of log lines.
Definition: logging.c:62
static int const char * fmt
Definition: acutest.h:488
int log_disp_terminal(time_t stamp, const char *file, int line, const char *function, enum LogLevel level,...)
Save a log line to the terminal - Implements log_dispatcher_t.
Definition: logging.c:450
Log perror (using errno)
Definition: logging.h:36
vsnprintf(buffer, sizeof(buffer), fmt, args)
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:375
#define _(a)
Definition: message.h:28
static const char * timestamp(time_t stamp)
Create a YYYY-MM-DD HH:MM:SS timestamp.
Definition: logging.c:77
Definition: logging.h:47
#define STAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:386
Log informational message.
Definition: logging.h:39
#define STAILQ_REMOVE_HEAD(head, field)
Definition: queue.h:419
int LogQueueMax
Maximum number of entries in the log queue.
Definition: logging.c:65
void log_queue_flush(log_dispatcher_t disp)
Replay the log queue.
Definition: logging.c:357
log_dispatcher_t MuttLogger
The log dispatcher.
Definition: logging.c:52
void log_file_set_version(const char *version)
Set the program&#39;s version number.
Definition: logging.c:221
File management functions.
int log_file_set_filename(const char *file, bool verbose)
Set the filename for the log.
Definition: logging.c:149
Logging Dispatcher.
String manipulation functions.
int line
Line number in source file.
Definition: logging.h:73
LogLevel
Names for the Logging Levels.
Definition: logging.h:34
Log at debug level 2.
Definition: logging.h:41
A Log line.
Definition: logging.h:69
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
int log_disp_file(time_t stamp, const char *file, int line, const char *function, enum LogLevel level,...)
Save a log line to a file.
Definition: logging.c:255
va_start(args, fmt)
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:359
int log_file_set_level(enum LogLevel level, bool verbose)
Set the logging level.
Definition: logging.c:175
Message logging.
void log_queue_set_max_size(int size)
Set a upper limit for the queue length.
Definition: logging.c:323
bool log_file_running(void)
Is the log file running?
Definition: logging.c:230
int log_file_open(bool verbose)
Start logging to a file.
Definition: logging.c:119
int log_queue_save(FILE *fp)
Save the contents of the queue to a temporary file.
Definition: logging.c:378
void log_queue_empty(void)
Free the contents of the queue.
Definition: logging.c:335
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
Log at debug level 1.
Definition: logging.h:40
int(* log_dispatcher_t)(time_t stamp, const char *file, int line, const char *function, enum LogLevel level,...)
Prototype for a logging function.
Definition: logging.h:62
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:451
#define FREE(x)
Definition: memory.h:40
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
time_t time
Timestamp of the message.
Definition: logging.h:71
int LogFileLevel
Log file level.
Definition: logging.c:56
char * LogFileVersion
Program version.
Definition: logging.c:57
int const char * file
Definition: acutest.h:617
int const char int line
Definition: acutest.h:617
Log at debug level 5.
Definition: logging.h:44
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:588
int LogQueueCount
Number of entries currently in the log queue.
Definition: logging.c:64
int log_disp_queue(time_t stamp, const char *file, int line, const char *function, enum LogLevel level,...)
Save a log line to an internal queue - Implements log_dispatcher_t.
Definition: logging.c:409
Time and date handling routines.
const char * file
Source file.
Definition: logging.h:72
int log_queue_add(struct LogLine *ll)
Add a LogLine to the queue.
Definition: logging.c:296
#define STAILQ_FIRST(head)
Definition: queue.h:347
Log at debug level 3.
Definition: logging.h:42
Log warning.
Definition: logging.h:38
va_end(args)
const char * function
C function.
Definition: logging.h:74
char * LogFileName
Log file name.
Definition: logging.c:55