NeoMutt  2025-01-09-117-gace867
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
logging.c
Go to the documentation of this file.
1
30#include "config.h"
31#include <errno.h>
32#include <stdarg.h> // IWYU pragma: keep
33#include <stdbool.h>
34#include <stdio.h>
35#include <string.h>
36#include <time.h>
37#include <unistd.h>
38#include "date.h"
39#include "file.h"
40#include "logging2.h"
41#include "memory.h"
42#include "message.h"
43#include "queue.h"
44#include "string2.h"
45
46const char *LogLevelAbbr = "PEWM12345N";
47
54
55static FILE *LogFileFP = NULL;
56static char *LogFileName = NULL;
57static int LogFileLevel = 0;
58static char *LogFileVersion = NULL;
59
63static struct LogLineList LogQueue = STAILQ_HEAD_INITIALIZER(LogQueue);
64
65static int LogQueueCount = 0;
66static int LogQueueMax = 0;
67
78static const char *timestamp(time_t stamp)
79{
80 static char buf[23] = { 0 };
81 static time_t last = 0;
82
83 if (stamp == 0)
84 stamp = mutt_date_now();
85
86 if (stamp != last)
87 {
88 mutt_date_localtime_format(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", stamp);
89 last = stamp;
90 }
91
92 return buf;
93}
94
99void log_file_close(bool verbose)
100{
101 if (!LogFileFP)
102 return;
103
104 fprintf(LogFileFP, "[%s] Closing log.\n", timestamp(0));
105 fprintf(LogFileFP, "# vim: syntax=neomuttlog\n");
107 if (verbose)
108 mutt_message(_("Closed log file: %s"), LogFileName);
109}
110
120int log_file_open(bool verbose)
121{
122 if (!LogFileName)
123 return -1;
124
125 if (LogFileFP)
126 log_file_close(false);
127
129 return -1;
130
132 if (!LogFileFP)
133 return -1;
134 setvbuf(LogFileFP, NULL, _IOLBF, 0);
135
136 fprintf(LogFileFP, "[%s] NeoMutt%s debugging at level %d\n", timestamp(0),
138 if (verbose)
139 mutt_message(_("Debugging at level %d to file '%s'"), LogFileLevel, LogFileName);
140 return 0;
141}
142
150int log_file_set_filename(const char *file, bool verbose)
151{
152 if (!file)
153 return -1;
154
155 /* also handles both being NULL */
156 if (mutt_str_equal(LogFileName, file))
157 return 0;
158
160
161 if (!LogFileName)
162 log_file_close(verbose);
163
164 return log_file_open(verbose);
165}
166
176int log_file_set_level(enum LogLevel level, bool verbose)
177{
178 if ((level < LL_MESSAGE) || (level >= LL_MAX))
179 return -1;
180
181 if (level == LogFileLevel)
182 return 0;
183
184 LogFileLevel = level;
185
186 if (level == LL_MESSAGE)
187 {
188 log_file_close(verbose);
189 }
190 else if (LogFileFP)
191 {
192 if (verbose)
193 mutt_message(_("Logging at level %d to file '%s'"), LogFileLevel, LogFileName);
194 fprintf(LogFileFP, "[%s] NeoMutt%s debugging at level %d\n", timestamp(0),
196 }
197 else
198 {
199 log_file_open(verbose);
200 }
201
202 if (LogFileLevel >= LL_DEBUG5)
203 {
204 fprintf(LogFileFP, "\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
221void log_file_set_version(const char *version)
222{
224}
225
231{
232 return LogFileFP;
233}
234
246int log_disp_file(time_t stamp, const char *file, int line, const char *function,
247 enum LogLevel level, const char *format, ...)
248{
249 if (!LogFileFP || (level < LL_PERROR) || (level > LogFileLevel))
250 return 0;
251
252 int rc = 0;
253 int err = errno;
254
255 if (!function)
256 function = "UNKNOWN";
257
258 rc += fprintf(LogFileFP, "[%s]<%c> %s() ", timestamp(stamp),
259 LogLevelAbbr[level + 3], function);
260
261 va_list ap;
262 va_start(ap, format);
263 rc += vfprintf(LogFileFP, format, ap);
264 va_end(ap);
265
266 if (level == LL_PERROR)
267 {
268 fprintf(LogFileFP, ": %s\n", strerror(err));
269 }
270 else if (level <= LL_MESSAGE)
271 {
272 fputs("\n", LogFileFP);
273 rc++;
274 }
275
276 return rc;
277}
278
286int log_queue_add(struct LogLine *ll)
287{
288 if (!ll)
289 return -1;
290
291 STAILQ_INSERT_TAIL(&LogQueue, ll, entries);
292
293 if ((LogQueueMax > 0) && (LogQueueCount >= LogQueueMax))
294 {
295 ll = STAILQ_FIRST(&LogQueue);
296 STAILQ_REMOVE_HEAD(&LogQueue, entries);
297 FREE(&ll->message);
298 FREE(&ll);
299 }
300 else
301 {
303 }
304 return LogQueueCount;
305}
306
314{
315 if (size < 0)
316 size = 0;
317 LogQueueMax = size;
318}
319
326{
327 struct LogLine *ll = NULL;
328 struct LogLine *tmp = NULL;
329
330 STAILQ_FOREACH_SAFE(ll, &LogQueue, entries, tmp)
331 {
332 STAILQ_REMOVE(&LogQueue, ll, LogLine, entries);
333 FREE(&ll->message);
334 FREE(&ll);
335 }
336
337 LogQueueCount = 0;
338}
339
348{
349 struct LogLine *ll = NULL;
350 STAILQ_FOREACH(ll, &LogQueue, entries)
351 {
352 disp(ll->time, ll->file, ll->line, ll->function, ll->level, "%s", ll->message);
353 }
354
356}
357
362const struct LogLineList log_queue_get(void)
363{
364 return LogQueue;
365}
366
378int log_disp_queue(time_t stamp, const char *file, int line, const char *function,
379 enum LogLevel level, const char *format, ...)
380{
381 char buf[LOG_LINE_MAX_LEN] = { 0 };
382 int err = errno;
383
384 va_list ap;
385 va_start(ap, format);
386 int rc = vsnprintf(buf, sizeof(buf), format, ap);
387 va_end(ap);
388
389 if (level == LL_PERROR)
390 {
391 if ((rc >= 0) && (rc < sizeof(buf)))
392 rc += snprintf(buf + rc, sizeof(buf) - rc, ": %s", strerror(err));
393 level = LL_ERROR;
394 }
395
396 struct LogLine *ll = MUTT_MEM_CALLOC(1, struct LogLine);
397 ll->time = (stamp != 0) ? stamp : mutt_date_now();
398 ll->file = file;
399 ll->line = line;
400 ll->function = function;
401 ll->level = level;
402 ll->message = mutt_str_dup(buf);
403
404 log_queue_add(ll);
405
406 return rc;
407}
408
421int log_disp_terminal(time_t stamp, const char *file, int line, const char *function,
422 enum LogLevel level, const char *format, ...)
423{
424 char buf[LOG_LINE_MAX_LEN] = { 0 };
425
426 va_list ap;
427 va_start(ap, format);
428 int rc = vsnprintf(buf, sizeof(buf), format, ap);
429 va_end(ap);
430
431 log_disp_file(stamp, file, line, function, level, "%s", buf);
432
433 if ((level < LL_PERROR) || (level > LL_MESSAGE))
434 return 0;
435
436 FILE *fp = (level < LL_MESSAGE) ? stderr : stdout;
437 int err = errno;
438 int color = 0;
439 bool tty = (isatty(fileno(fp)) == 1);
440
441 if (tty)
442 {
443 switch (level)
444 {
445 case LL_PERROR:
446 case LL_ERROR:
447 color = 31;
448 break;
449 case LL_WARNING:
450 color = 33;
451 break;
452 case LL_MESSAGE:
453 default:
454 break;
455 }
456 }
457
458 if (color > 0)
459 rc += fprintf(fp, "\033[1;%dm", color); // Escape
460
461 fputs(buf, fp);
462
463 if (level == LL_PERROR)
464 rc += fprintf(fp, ": %s", strerror(err));
465
466 if (color > 0)
467 rc += fprintf(fp, "\033[0m"); // Escape
468
469 rc += fprintf(fp, "\n");
470
471 return rc;
472}
473
482void log_multiline_full(enum LogLevel level, const char *str, const char *file,
483 int line, const char *func)
484{
485 while (str && (str[0] != '\0'))
486 {
487 const char *end = strchr(str, '\n');
488 if (end)
489 {
490 int len = end - str;
491 MuttLogger(0, file, line, func, level, "%.*s\n", len, str);
492 str = end + 1;
493 }
494 else
495 {
496 MuttLogger(0, file, line, func, level, "%s\n", str);
497 break;
498 }
499 }
500}
Time and date handling routines.
File management functions.
#define mutt_file_fclose(FP)
Definition: file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:138
int log_disp_queue(time_t stamp, const char *file, int line, const char *function, enum LogLevel level, const char *format,...)
Save a log line to an internal queue - Implements log_dispatcher_t -.
Definition: logging.c:378
int log_disp_file(time_t stamp, const char *file, int line, const char *function, enum LogLevel level, const char *format,...)
Save a log line to a file - Implements log_dispatcher_t -.
Definition: logging.c:246
int log_disp_terminal(time_t stamp, const char *file, int line, const char *function, enum LogLevel level, const char *format,...)
Save a log line to the terminal - Implements log_dispatcher_t -.
Definition: logging.c:421
log_dispatcher_t MuttLogger
The log dispatcher -.
Definition: logging.c:53
#define mutt_message(...)
Definition: logging2.h:92
Logging Dispatcher.
int(* log_dispatcher_t)(time_t stamp, const char *file, int line, const char *function, enum LogLevel level, const char *format,...) __attribute__((__format__(__printf__
Definition: logging2.h:70
LogLevel
Names for the Logging Levels.
Definition: logging2.h:39
@ LL_ERROR
Log error.
Definition: logging2.h:41
@ LL_PERROR
Log perror (using errno)
Definition: logging2.h:40
@ LL_DEBUG5
Log at debug level 5.
Definition: logging2.h:48
@ LL_WARNING
Log warning.
Definition: logging2.h:42
@ LL_MESSAGE
Log informational message.
Definition: logging2.h:43
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:44
@ LL_MAX
Definition: logging2.h:51
#define LOG_LINE_MAX_LEN
Log lines longer than this will be truncated.
Definition: logging2.h:31
Memory management wrappers.
#define FREE(x)
Definition: memory.h:55
#define MUTT_MEM_CALLOC(n, type)
Definition: memory.h:40
size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
Format localtime.
Definition: date.c:951
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:456
int log_file_open(bool verbose)
Start logging to a file.
Definition: logging.c:120
static FILE * LogFileFP
Log file handle.
Definition: logging.c:55
void log_queue_empty(void)
Free the contents of the queue.
Definition: logging.c:325
void log_queue_set_max_size(int size)
Set a upper limit for the queue length.
Definition: logging.c:313
int log_file_set_level(enum LogLevel level, bool verbose)
Set the logging level.
Definition: logging.c:176
static struct LogLineList LogQueue
In-memory list of log lines.
Definition: logging.c:63
bool log_file_running(void)
Is the log file running?
Definition: logging.c:230
static char * LogFileVersion
Program version.
Definition: logging.c:58
static int LogQueueMax
Maximum number of entries in the log queue.
Definition: logging.c:66
static const char * timestamp(time_t stamp)
Create a YYYY-MM-DD HH:MM:SS timestamp.
Definition: logging.c:78
void log_multiline_full(enum LogLevel level, const char *str, const char *file, int line, const char *func)
Helper to dump multiline text to the log.
Definition: logging.c:482
const struct LogLineList log_queue_get(void)
Get the Log Queue.
Definition: logging.c:362
void log_queue_flush(log_dispatcher_t disp)
Replay the log queue.
Definition: logging.c:347
static int LogQueueCount
Number of entries currently in the log queue.
Definition: logging.c:65
static int LogFileLevel
Log file level.
Definition: logging.c:57
static char * LogFileName
Log file name.
Definition: logging.c:56
void log_file_close(bool verbose)
Close the log file.
Definition: logging.c:99
int log_file_set_filename(const char *file, bool verbose)
Set the filename for the log.
Definition: logging.c:150
int log_queue_add(struct LogLine *ll)
Add a LogLine to the queue.
Definition: logging.c:286
const char * LogLevelAbbr
Abbreviations of logging level names.
Definition: logging.c:46
void log_file_set_version(const char *version)
Set the program's version number.
Definition: logging.c:221
Message logging.
#define _(a)
Definition: message.h:28
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:254
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:661
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:281
#define STAILQ_REMOVE_HEAD(head, field)
Definition: queue.h:461
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:441
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define STAILQ_FIRST(head)
Definition: queue.h:388
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:390
#define STAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:427
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:400
String manipulation functions.
#define NONULL(x)
Definition: string2.h:37
A Log line.
Definition: logging2.h:79
const char * file
Source file.
Definition: logging2.h:81
char * message
Message to be logged.
Definition: logging2.h:85
const char * function
C function.
Definition: logging2.h:83
int line
Line number in source file.
Definition: logging2.h:82
enum LogLevel level
Log level, e.g. LL_DEBUG1.
Definition: logging2.h:84
time_t time
Timestamp of the message.
Definition: logging2.h:80