NeoMutt  2021-10-29-220-g2b1eec
Teaching an old dog new tricks
DOXYGEN
mutt_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 <stdint.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <time.h>
37 #include "mutt/lib.h"
38 #include "config/lib.h"
39 #include "core/lib.h"
40 #include "gui/lib.h"
41 #include "mutt_logging.h"
42 #include "color/lib.h"
43 #include "mutt_globals.h"
44 #include "muttlib.h"
45 #include "options.h"
46 
47 uint64_t LastError = 0;
48 
49 char *CurrentFile = NULL;
50 const int NumOfLogs = 5;
51 
52 #define S_TO_MS 1000L
53 
59 static void error_pause(void)
60 {
61  const short c_sleep_time = cs_subset_number(NeoMutt->sub, "sleep_time");
62  const uint64_t elapsed = mutt_date_epoch_ms() - LastError;
63  const uint64_t sleep = c_sleep_time * S_TO_MS;
64  if ((LastError == 0) || (elapsed >= sleep))
65  return;
66 
67  mutt_refresh();
68  mutt_date_sleep_ms(sleep - elapsed);
69 }
70 
84 static const char *rotate_logs(const char *file, int count)
85 {
86  if (!file)
87  return NULL;
88 
89  struct Buffer *old_file = mutt_buffer_pool_get();
90  struct Buffer *new_file = mutt_buffer_pool_get();
91 
92  /* rotate the old debug logs */
93  for (count -= 2; count >= 0; count--)
94  {
95  mutt_buffer_printf(old_file, "%s%d", file, count);
96  mutt_buffer_printf(new_file, "%s%d", file, count + 1);
97 
98  mutt_buffer_expand_path(old_file);
99  mutt_buffer_expand_path(new_file);
100  (void) rename(mutt_buffer_string(old_file), mutt_buffer_string(new_file));
101  }
102 
103  file = mutt_buffer_strdup(old_file);
104  mutt_buffer_pool_release(&old_file);
105  mutt_buffer_pool_release(&new_file);
106 
107  return file;
108 }
109 
114 {
115  /* Make sure the error message has had time to be read */
116  if (OptMsgErr)
117  error_pause();
118 
119  ErrorBufMessage = false;
120  if (!OptNoCurses)
122 }
123 
127 int log_disp_curses(time_t stamp, const char *file, int line,
128  const char *function, enum LogLevel level, ...)
129 {
130  const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
131  if (level > c_debug_level)
132  return 0;
133 
134  char buf[1024];
135 
136  va_list ap;
137  va_start(ap, level);
138  const char *fmt = va_arg(ap, const char *);
139  int ret = vsnprintf(buf, sizeof(buf), fmt, ap);
140  va_end(ap);
141 
142  if ((level == LL_PERROR) && (ret >= 0) && (ret < sizeof(buf)))
143  {
144  char *buf2 = buf + ret;
145  int len = sizeof(buf) - ret;
146  const char *p = strerror(errno);
147  if (!p)
148  p = _("unknown error");
149 
150  ret += snprintf(buf2, len, ": %s (errno = %d)", p, errno);
151  }
152 
153  const bool dupe = (strcmp(buf, ErrorBuf) == 0);
154  if (!dupe)
155  {
156  /* Only log unique messages */
157  log_disp_file(stamp, file, line, function, level, "%s", buf);
158  if (stamp == 0)
159  log_disp_queue(stamp, file, line, function, level, "%s", buf);
160  }
161 
162  /* Don't display debugging message on screen */
163  if (level > LL_MESSAGE)
164  return 0;
165 
166  /* Only pause if this is a message following an error */
167  if ((level > LL_ERROR) && OptMsgErr && !dupe)
168  error_pause();
169 
170  size_t width = msgwin_get_width();
171  mutt_simple_format(ErrorBuf, sizeof(ErrorBuf), 0, width ? width : sizeof(ErrorBuf),
172  JUSTIFY_LEFT, 0, buf, sizeof(buf), false);
173  ErrorBufMessage = true;
174 
175  if (!OptKeepQuiet)
176  {
177  enum ColorId cid = MT_COLOR_NORMAL;
178  switch (level)
179  {
180  case LL_ERROR:
181  mutt_beep(false);
182  cid = MT_COLOR_ERROR;
183  break;
184  case LL_WARNING:
185  cid = MT_COLOR_WARNING;
186  break;
187  default:
188  cid = MT_COLOR_MESSAGE;
189  break;
190  }
191 
193  }
194 
195  if ((level <= LL_ERROR) && !dupe)
196  {
197  OptMsgErr = true;
199  }
200  else
201  {
202  OptMsgErr = false;
203  LastError = 0;
204  }
205 
206  return ret;
207 }
208 
212 void mutt_log_prep(void)
213 {
214  char ver[64];
215  snprintf(ver, sizeof(ver), "-%s%s", PACKAGE_VERSION, GitVer);
217 }
218 
222 void mutt_log_stop(void)
223 {
224  log_file_close(false);
225  FREE(&CurrentFile);
226 }
227 
236 int mutt_log_set_file(const char *file)
237 {
238  const char *const c_debug_file = cs_subset_path(NeoMutt->sub, "debug_file");
239  if (!mutt_str_equal(CurrentFile, c_debug_file))
240  {
241  const char *name = rotate_logs(c_debug_file, NumOfLogs);
242  if (!name)
243  return -1;
244 
245  log_file_set_filename(name, false);
246  FREE(&name);
247  mutt_str_replace(&CurrentFile, c_debug_file);
248  }
249 
250  cs_subset_str_string_set(NeoMutt->sub, "debug_file", file, NULL);
251 
252  return 0;
253 }
254 
262 int mutt_log_set_level(enum LogLevel level, bool verbose)
263 {
264  if (!CurrentFile)
265  {
266  const char *const c_debug_file = cs_subset_path(NeoMutt->sub, "debug_file");
267  mutt_log_set_file(c_debug_file);
268  }
269 
270  if (log_file_set_level(level, verbose) != 0)
271  return -1;
272 
273  cs_subset_str_native_set(NeoMutt->sub, "debug_level", level, NULL);
274  return 0;
275 }
276 
284 int mutt_log_start(void)
285 {
286  const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
287  if (c_debug_level < 1)
288  return 0;
289 
290  if (log_file_running())
291  return 0;
292 
293  const char *const c_debug_file = cs_subset_path(NeoMutt->sub, "debug_file");
294  mutt_log_set_file(c_debug_file);
295 
296  /* This will trigger the file creation */
297  if (log_file_set_level(c_debug_level, true) < 0)
298  return -1;
299 
300  return 0;
301 }
302 
306 int level_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef,
307  intptr_t value, struct Buffer *err)
308 {
309  if ((value < 0) || (value >= LL_MAX))
310  {
311  mutt_buffer_printf(err, _("Invalid value for option %s: %ld"), cdef->name, value);
312  return CSR_ERR_INVALID;
313  }
314 
315  return CSR_SUCCESS;
316 }
317 
322 {
323  if ((nc->event_type != NT_CONFIG) || !nc->event_data)
324  return -1;
325 
326  struct EventConfig *ev_c = nc->event_data;
327 
328  if (mutt_str_equal(ev_c->name, "debug_file"))
329  {
330  const char *const c_debug_file = cs_subset_path(NeoMutt->sub, "debug_file");
331  mutt_log_set_file(c_debug_file);
332  }
333  else if (mutt_str_equal(ev_c->name, "debug_level"))
334  {
335  const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
336  mutt_log_set_level(c_debug_level, true);
337  }
338  else
339  {
340  return 0;
341  }
342 
343  mutt_debug(LL_DEBUG5, "log done\n");
344  return 0;
345 }
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:432
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Color and attribute parsing.
ColorId
List of all colored objects.
Definition: color.h:35
@ MT_COLOR_MESSAGE
Informational message.
Definition: color.h:51
@ MT_COLOR_ERROR
Error message.
Definition: color.h:46
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:53
@ MT_COLOR_WARNING
Warning messages.
Definition: color.h:74
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:114
void mutt_simple_format(char *buf, size_t buflen, int min_width, int max_width, enum FormatJustify justify, char pad_char, const char *s, size_t n, bool arboreal)
Format a string, like snprintf()
Definition: curs_lib.c:692
void mutt_beep(bool force)
Irritate the user.
Definition: curs_lib.c:104
@ JUSTIFY_LEFT
Left justify the text.
Definition: curs_lib.h:43
void mutt_date_sleep_ms(size_t ms)
Sleep for milliseconds.
Definition: date.c:704
uint64_t mutt_date_epoch_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition: date.c:436
int level_validator(const struct ConfigSet *cs, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
Validate the "debug_level" config variable - Implements ConfigDef::validator() -.
Definition: mutt_logging.c:306
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 - Implements log_dispatcher_t -.
Definition: logging.c:246
int log_disp_curses(time_t stamp, const char *file, int line, const char *function, enum LogLevel level,...)
Display a log line in the message line - Implements log_dispatcher_t -.
Definition: mutt_logging.c:127
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:400
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
int main_log_observer(struct NotifyCallback *nc)
Notification that a Config Variable has changed - Implements observer_t -.
Definition: mutt_logging.c:321
Convenience wrapper for the gui headers.
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
int log_file_set_level(enum LogLevel level, bool verbose)
Set the logging level.
Definition: logging.c:175
bool log_file_running(void)
Is the log file running?
Definition: logging.c:230
void log_file_close(bool verbose)
Close the log file.
Definition: logging.c:98
int log_file_set_filename(const char *file, bool verbose)
Set the filename for the log.
Definition: logging.c:149
void log_file_set_version(const char *version)
Set the program's version number.
Definition: logging.c:221
LogLevel
Names for the Logging Levels.
Definition: logging.h:35
@ LL_ERROR
Log error.
Definition: logging.h:37
@ LL_PERROR
Log perror (using errno)
Definition: logging.h:36
@ LL_DEBUG5
Log at debug level 5.
Definition: logging.h:44
@ LL_WARNING
Log warning.
Definition: logging.h:38
@ LL_MESSAGE
Log informational message.
Definition: logging.h:39
@ LL_MAX
Definition: logging.h:47
#define FREE(x)
Definition: memory.h:40
void msgwin_set_text(enum ColorId cid, const char *text)
Set the text for the Message Window.
Definition: msgwin.c:221
size_t msgwin_get_width(void)
Get the width of the Message Window.
Definition: msgwin.c:260
void msgwin_clear_text(void)
Clear the text in the Message Window.
Definition: msgwin.c:240
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:715
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:257
Hundreds of global variables to back the user variables.
bool ErrorBufMessage
true if the last message was an error
Definition: mutt_globals.h:46
char ErrorBuf[256]
Copy of the last error message.
Definition: mutt_globals.h:47
const char * GitVer
const int NumOfLogs
How many log files to rotate.
Definition: mutt_logging.c:50
int mutt_log_set_level(enum LogLevel level, bool verbose)
Change the logging level.
Definition: mutt_logging.c:262
int mutt_log_set_file(const char *file)
Change the logging file.
Definition: mutt_logging.c:236
static const char * rotate_logs(const char *file, int count)
Rotate a set of numbered files.
Definition: mutt_logging.c:84
uint64_t LastError
Time of the last error message (in milliseconds since the Unix epoch)
Definition: mutt_logging.c:47
#define S_TO_MS
Definition: mutt_logging.c:52
void mutt_log_stop(void)
Close the log file.
Definition: mutt_logging.c:222
int mutt_log_start(void)
Enable file logging.
Definition: mutt_logging.c:284
static void error_pause(void)
Wait for an error message to be read.
Definition: mutt_logging.c:59
void mutt_log_prep(void)
Prepare to log.
Definition: mutt_logging.c:212
char * CurrentFile
The previous log file name.
Definition: mutt_logging.c:49
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
NeoMutt Logging.
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
Some miscellaneous functions.
@ NT_CONFIG
Config has changed, NotifyConfig, EventConfig.
Definition: notify_type.h:42
Handling of global boolean variables.
bool OptKeepQuiet
(pseudo) shut up the message and refresh functions while we are executing an external program
Definition: options.h:44
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:53
bool OptMsgErr
(pseudo) used by mutt_error/mutt_message
Definition: options.h:46
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define CSR_ERR_INVALID
Value hasn't been set.
Definition: set.h:38
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:35
String manipulation buffer.
Definition: buffer.h:34
Definition: set.h:64
const char * name
User-visible name.
Definition: set.h:65
Container for lots of config items.
Definition: set.h:260
A config-change event.
Definition: subset.h:70
const char * name
Name of config item that changed.
Definition: subset.h:72
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Data passed to a notification function.
Definition: observer.h:34
void * event_data
Data from notify_send()
Definition: observer.h:38
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:36
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: subset.c:305
int cs_subset_str_string_set(const struct ConfigSubset *sub, const char *name, const char *value, struct Buffer *err)
Set a config item by string.
Definition: subset.c:408