NeoMutt  2025-12-11-694-ga89709
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
logging.c
Go to the documentation of this file.
1
24
30
31#include "config.h"
32#include <errno.h>
33#include <stdarg.h> // IWYU pragma: keep
34#include <stdbool.h>
35#include <stdio.h>
36#include <string.h>
37#include <time.h>
38#include <unistd.h>
39#include "date.h"
40#include "file.h"
41#include "logging2.h"
42#include "memory.h"
43#include "message.h"
44#include "queue.h"
45#include "string2.h"
46
47const char *LogLevelAbbr = "PEWM12345N";
48
55
56static FILE *LogFileFP = NULL;
57static char *LogFileName = NULL;
58static int LogFileLevel = 0;
59static char *LogFileVersion = NULL;
60
64static struct LogLineList LogQueue = STAILQ_HEAD_INITIALIZER(LogQueue);
65
66static int LogQueueCount = 0;
67static int LogQueueMax = 0;
68
79static const char *timestamp(time_t stamp)
80{
81 static char buf[23] = { 0 };
82 static time_t last = 0;
83
84 if (stamp == 0)
85 stamp = mutt_date_now();
86
87 if (stamp != last)
88 {
89 mutt_date_localtime_format(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", stamp);
90 last = stamp;
91 }
92
93 return buf;
94}
95
100void log_file_close(bool verbose)
101{
102 if (!LogFileFP)
103 return;
104
105 fprintf(LogFileFP, "[%s] Closing log.\n", timestamp(0));
106 fprintf(LogFileFP, "# vim: ft=neomuttlog\n");
108 if (verbose)
109 mutt_message(_("Closed log file: %s"), LogFileName);
110}
111
121int log_file_open(bool verbose)
122{
123 if (!LogFileName)
124 return -1;
125
126 if (LogFileFP)
127 log_file_close(false);
128
130 return -1;
131
133 if (!LogFileFP)
134 return -1;
135 setvbuf(LogFileFP, NULL, _IOLBF, 0);
136
137 fprintf(LogFileFP, "[%s] NeoMutt%s debugging at level %d\n", timestamp(0),
139 if (verbose)
140 mutt_message(_("Debugging at level %d to file '%s'"), LogFileLevel, LogFileName);
141 return 0;
142}
143
151int log_file_set_filename(const char *file, bool verbose)
152{
153 if (!file)
154 return -1;
155
156 /* also handles both being NULL */
157 if (mutt_str_equal(LogFileName, file))
158 return 0;
159
161
162 if (!LogFileName)
163 log_file_close(verbose);
164
165 return log_file_open(verbose);
166}
167
177int log_file_set_level(enum LogLevel level, bool verbose)
178{
179 int rc = 0;
180
181 if ((level < LL_MESSAGE) || (level >= LL_MAX))
182 return -1;
183
184 if (level == LogFileLevel)
185 return 0;
186
187 LogFileLevel = level;
188
189 if (level == LL_MESSAGE)
190 {
191 log_file_close(verbose);
192 }
193 else if (LogFileFP)
194 {
195 if (verbose)
196 mutt_message(_("Logging at level %d to file '%s'"), LogFileLevel, LogFileName);
197 fprintf(LogFileFP, "[%s] NeoMutt%s debugging at level %d\n", timestamp(0),
199 }
200 else
201 {
202 rc = log_file_open(verbose);
203 }
204
205 if (rc != 0)
206 return rc;
207
208 if (LogFileFP && (LogFileLevel >= LL_DEBUG5))
209 {
210 fprintf(LogFileFP, "\n"
211 "WARNING:\n"
212 " Logging at this level can reveal personal information.\n"
213 " Review the log carefully before posting in bug reports.\n"
214 "\n");
215 }
216
217 return 0;
218}
219
227void log_file_set_version(const char *version)
228{
230}
231
237{
238 return LogFileFP;
239}
240
252int log_disp_file(time_t stamp, const char *file, int line, const char *function,
253 enum LogLevel level, const char *format, ...)
254{
255 if (!LogFileFP || (level < LL_PERROR) || (level > LogFileLevel))
256 return 0;
257
258 int rc = 0;
259 int err = errno;
260
261 if (!function)
262 function = "UNKNOWN";
263
264 rc += fprintf(LogFileFP, "[%s]<%c> %s() ", timestamp(stamp),
265 LogLevelAbbr[level + 3], function);
266
267 va_list ap;
268 va_start(ap, format);
269 rc += vfprintf(LogFileFP, format, ap);
270 va_end(ap);
271
272 if (level == LL_PERROR)
273 {
274 fprintf(LogFileFP, ": %s\n", strerror(err));
275 }
276 else if (level <= LL_MESSAGE)
277 {
278 fputs("\n", LogFileFP);
279 rc++;
280 }
281
282 return rc;
283}
284
292int log_queue_add(struct LogLine *ll)
293{
294 if (!ll)
295 return -1;
296
297 STAILQ_INSERT_TAIL(&LogQueue, ll, entries);
298
299 if ((LogQueueMax > 0) && (LogQueueCount >= LogQueueMax))
300 {
301 ll = STAILQ_FIRST(&LogQueue);
302 STAILQ_REMOVE_HEAD(&LogQueue, entries);
303 FREE(&ll->message);
304 FREE(&ll);
305 }
306 else
307 {
309 }
310 return LogQueueCount;
311}
312
320{
321 if (size < 0)
322 size = 0;
323 LogQueueMax = size;
324}
325
332{
333 struct LogLine *ll = NULL;
334 struct LogLine *tmp = NULL;
335
336 STAILQ_FOREACH_SAFE(ll, &LogQueue, entries, tmp)
337 {
338 STAILQ_REMOVE(&LogQueue, ll, LogLine, entries);
339 FREE(&ll->message);
340 FREE(&ll);
341 }
342
343 LogQueueCount = 0;
344}
345
354{
355 struct LogLine *ll = NULL;
356 STAILQ_FOREACH(ll, &LogQueue, entries)
357 {
358 disp(ll->time, ll->file, ll->line, ll->function, ll->level, "%s", ll->message);
359 }
360
362}
363
368struct LogLineList log_queue_get(void)
369{
370 return LogQueue;
371}
372
384int log_disp_queue(time_t stamp, const char *file, int line, const char *function,
385 enum LogLevel level, const char *format, ...)
386{
387 char buf[LOG_LINE_MAX_LEN] = { 0 };
388 int err = errno;
389
390 va_list ap;
391 va_start(ap, format);
392 int rc = vsnprintf(buf, sizeof(buf), format, ap);
393 va_end(ap);
394
395 if (level == LL_PERROR)
396 {
397 if ((rc >= 0) && (rc < sizeof(buf)))
398 rc += snprintf(buf + rc, sizeof(buf) - rc, ": %s", strerror(err));
399 level = LL_ERROR;
400 }
401
402 struct LogLine *ll = MUTT_MEM_CALLOC(1, struct LogLine);
403 ll->time = (stamp != 0) ? stamp : mutt_date_now();
404 ll->file = file;
405 ll->line = line;
406 ll->function = function;
407 ll->level = level;
408 ll->message = mutt_str_dup(buf);
409
410 log_queue_add(ll);
411
412 return rc;
413}
414
427int log_disp_terminal(time_t stamp, const char *file, int line, const char *function,
428 enum LogLevel level, const char *format, ...)
429{
430 char buf[LOG_LINE_MAX_LEN] = { 0 };
431
432 va_list ap;
433 va_start(ap, format);
434 int rc = vsnprintf(buf, sizeof(buf), format, ap);
435 va_end(ap);
436
437 log_disp_file(stamp, file, line, function, level, "%s", buf);
438
439 if ((level < LL_PERROR) || (level > LL_MESSAGE))
440 return 0;
441
442 FILE *fp = (level < LL_MESSAGE) ? stderr : stdout;
443 int err = errno;
444 int color = 0;
445 bool tty = (isatty(fileno(fp)) == 1);
446
447 if (tty)
448 {
449 switch (level)
450 {
451 case LL_PERROR:
452 case LL_ERROR:
453 color = 31;
454 break;
455 case LL_WARNING:
456 color = 33;
457 break;
458 case LL_MESSAGE:
459 default:
460 break;
461 }
462 }
463
464 if (color > 0)
465 rc += fprintf(fp, "\033[1;%dm", color); // Escape
466
467 fputs(buf, fp);
468
469 if (level == LL_PERROR)
470 rc += fprintf(fp, ": %s", strerror(err));
471
472 if (color > 0)
473 rc += fprintf(fp, "\033[0m"); // Escape
474
475 rc += fprintf(fp, "\n");
476
477 return rc;
478}
479
488void log_multiline_full(enum LogLevel level, const char *str, const char *file,
489 int line, const char *func)
490{
491 while (str && (str[0] != '\0'))
492 {
493 const char *end = strchr(str, '\n');
494 if (end)
495 {
496 int len = end - str;
497 MuttLogger(0, file, line, func, level, "%.*s\n", len, str);
498 str = end + 1;
499 }
500 else
501 {
502 MuttLogger(0, file, line, func, level, "%s\n", str);
503 break;
504 }
505 }
506}
507
515int log_disp_null(time_t stamp, const char *file, int line, const char *function,
516 enum LogLevel level, const char *format, ...)
517{
518 return 0;
519}
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:384
int log_disp_null(time_t stamp, const char *file, int line, const char *function, enum LogLevel level, const char *format,...)
Discard log lines - Implements log_dispatcher_t -.
Definition logging.c:515
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:252
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:427
log_dispatcher_t MuttLogger
The log dispatcher -.
Definition logging.c:54
#define mutt_message(...)
Definition logging2.h:93
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:71
int log_dispatcher_t MuttLogger
LogLevel
Names for the Logging Levels.
Definition logging2.h:40
@ LL_ERROR
Log error.
Definition logging2.h:42
@ LL_PERROR
Log perror (using errno)
Definition logging2.h:41
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
@ LL_WARNING
Log warning.
Definition logging2.h:43
@ LL_MESSAGE
Log informational message.
Definition logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
@ LL_MAX
Definition logging2.h:52
#define LOG_LINE_MAX_LEN
Log lines longer than this will be truncated.
Definition logging2.h:32
Memory management wrappers.
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
Format localtime.
Definition date.c:952
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:457
int log_file_open(bool verbose)
Start logging to a file.
Definition logging.c:121
static FILE * LogFileFP
Log file handle.
Definition logging.c:56
void log_queue_empty(void)
Free the contents of the queue.
Definition logging.c:331
void log_queue_set_max_size(int size)
Set a upper limit for the queue length.
Definition logging.c:319
int log_file_set_level(enum LogLevel level, bool verbose)
Set the logging level.
Definition logging.c:177
static struct LogLineList LogQueue
In-memory list of log lines.
Definition logging.c:64
bool log_file_running(void)
Is the log file running?
Definition logging.c:236
static char * LogFileVersion
Program version.
Definition logging.c:59
static int LogQueueMax
Maximum number of entries in the log queue.
Definition logging.c:67
static const char * timestamp(time_t stamp)
Create a YYYY-MM-DD HH:MM:SS timestamp.
Definition logging.c:79
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:488
void log_queue_flush(log_dispatcher_t disp)
Replay the log queue.
Definition logging.c:353
static int LogQueueCount
Number of entries currently in the log queue.
Definition logging.c:66
static int LogFileLevel
Log file level.
Definition logging.c:58
static char * LogFileName
Log file name.
Definition logging.c:57
void log_file_close(bool verbose)
Close the log file.
Definition logging.c:100
int log_file_set_filename(const char *file, bool verbose)
Set the filename for the log.
Definition logging.c:151
int log_queue_add(struct LogLine *ll)
Add a LogLine to the queue.
Definition logging.c:292
struct LogLineList log_queue_get(void)
Get the Log Queue.
Definition logging.c:368
const char * LogLevelAbbr
Abbreviations of logging level names.
Definition logging.c:47
void log_file_set_version(const char *version)
Set the program's version number.
Definition logging.c:227
Message logging.
#define _(a)
Definition message.h:28
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
#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:44
A Log line.
Definition logging2.h:80
const char * file
Source file.
Definition logging2.h:82
char * message
Message to be logged.
Definition logging2.h:86
const char * function
C function.
Definition logging2.h:84
int line
Line number in source file.
Definition logging2.h:83
enum LogLevel level
Log level, e.g. LL_DEBUG1.
Definition logging2.h:85
time_t time
Timestamp of the message.
Definition logging2.h:81