[exdoc] Handle punctuation better in code formatting.
[mono.git] / mono / utils / mono-logger.c
1 /**
2  * \file
3  */
4
5 #include <string.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <glib.h>
9
10 #include "mono-compiler.h"
11 #include "mono-logger-internals.h"
12
13 typedef struct {
14         GLogLevelFlags  level;
15         MonoTraceMask   mask;
16 } MonoLogLevelEntry;
17
18 GLogLevelFlags mono_internal_current_level      = INT_MAX;
19 MonoTraceMask  mono_internal_current_mask       = MONO_TRACE_ALL;
20 gboolean mono_trace_log_header                  = FALSE;
21
22 static GQueue           *level_stack            = NULL;
23 static const char       *mono_log_domain        = "Mono";
24 static MonoPrintCallback print_callback, printerr_callback;
25
26 static MonoLogCallParm logCallback = {
27         .opener = NULL,
28         .writer = NULL,
29         .closer = NULL,
30         .header = FALSE
31 };
32
33 typedef struct {
34    MonoLogCallback legacy_callback;
35    gpointer user_data;
36 } UserSuppliedLoggerUserData;
37
38 /**
39  * mono_trace_init:
40  *
41  * Initializes the mono tracer.
42  */
43 void 
44 mono_trace_init (void)
45 {
46         if(level_stack == NULL) {
47                 mono_internal_current_level = G_LOG_LEVEL_ERROR;
48                 level_stack = g_queue_new();
49
50                 mono_trace_set_mask_string(g_getenv("MONO_LOG_MASK"));
51                 mono_trace_set_level_string(g_getenv("MONO_LOG_LEVEL"));
52                 mono_trace_set_logheader_string(g_getenv("MONO_LOG_HEADER"));
53                 mono_trace_set_logdest_string(g_getenv("MONO_LOG_DEST"));
54         }
55 }
56
57 /**
58  * mono_trace_cleanup:
59  *
60  * Releases the mono tracer.
61  */
62 void 
63 mono_trace_cleanup (void)
64 {
65         if(level_stack != NULL) {
66                 while(!g_queue_is_empty (level_stack)) {
67                         g_free (g_queue_pop_head (level_stack));
68                 }
69
70                 logCallback.closer();
71                 g_queue_free (level_stack);
72                 level_stack = NULL;
73         }
74 }
75
76 /**
77  * mono_tracev_inner:
78  * \param level Verbose level of the specified message
79  * \param mask Type of the specified message
80  * Traces a new message, depending on the current logging level
81  * and trace mask.
82  */
83 void 
84 mono_tracev_inner (GLogLevelFlags level, MonoTraceMask mask, const char *format, va_list args)
85 {
86         char *log_message;
87         if (level_stack == NULL) {
88                 mono_trace_init ();
89                 if(level > mono_internal_current_level || !(mask & mono_internal_current_mask))
90                         return;
91         }
92
93         g_assert (logCallback.opener); // mono_trace_init should have provided us with one!
94
95         if (g_vasprintf (&log_message, format, args) < 0)
96                 return;
97         logCallback.writer (mono_log_domain, level, logCallback.header, log_message);
98         g_free (log_message);
99 }
100
101 /**
102  * mono_trace_set_level:
103  * \param level Verbose level to set
104  * Sets the current logging level. Every subsequent call to
105  * \c mono_trace will check the visibility of a message against this
106  * value.
107  */
108 void 
109 mono_trace_set_level (GLogLevelFlags level)
110 {
111         if(level_stack == NULL)
112                 mono_trace_init();
113
114         mono_internal_current_level = level;
115 }
116
117 /**
118  * mono_trace_set_mask:
119  * \param mask Mask of visible message types.
120  * Sets the current logging level. Every subsequent call to
121  * \c mono_trace will check the visibility of a message against this
122  * value.
123  */
124 void 
125 mono_trace_set_mask (MonoTraceMask mask)
126 {
127         if(level_stack == NULL)
128                 mono_trace_init();
129
130         mono_internal_current_mask = mask;
131 }
132
133 /**
134  * mono_trace_set_logdest:
135  * \param dest Destination for logging
136  * Sets the current logging destination. This can be a file or, if supported,
137  * syslog.
138  */
139 void 
140 mono_trace_set_logdest_string (const char *dest)
141 {
142         MonoLogCallParm logger;
143
144         if(level_stack == NULL)
145                 mono_trace_init();
146
147 #if PLATFORM_ANDROID
148         logger.opener = mono_log_open_logcat;
149         logger.writer = mono_log_write_logcat;
150         logger.closer = mono_log_close_logcat;
151         logger.dest   = (char*) dest;
152 #elif defined (HOST_IOS)
153         logger.opener = mono_log_open_asl;
154         logger.writer = mono_log_write_asl;
155         logger.closer = mono_log_close_asl;
156         logger.dest   = (char*) dest;
157 #else
158         if ((dest == NULL) || (strcmp("syslog", dest) != 0)) {
159                 logger.opener = mono_log_open_logfile;
160                 logger.writer = mono_log_write_logfile;
161                 logger.closer = mono_log_close_logfile;
162                 logger.dest   = (char *) dest;
163         } else {
164                 logger.opener = mono_log_open_syslog;
165                 logger.writer = mono_log_write_syslog;
166                 logger.closer = mono_log_close_syslog;
167                 logger.dest   = (char *) dest;
168         }
169 #endif
170         mono_trace_set_log_handler_internal(&logger, NULL);
171 }
172
173 /**
174  * mono_trace_set_logheader:
175  * \param head Whether we want pid/date/time header on log messages
176  * Sets the current logging header option.
177  */
178 void 
179 mono_trace_set_logheader_string(const char *head)
180 {
181         if (head == NULL) {
182                 mono_trace_log_header = FALSE;
183         } else {
184                 mono_trace_log_header = TRUE;
185         }
186 }
187
188 /**
189  * mono_trace_push:
190  * \param level Verbose level to set
191  * \param mask Mask of visible message types.
192  * Saves the current values of level and mask then calls \c mono_trace_set
193  * with the specified new values.
194  */
195 void 
196 mono_trace_push (GLogLevelFlags level, MonoTraceMask mask)
197 {
198         if(level_stack == NULL)
199                 g_error("%s: cannot use mono_trace_push without calling mono_trace_init first.", __func__);
200         else {
201                 MonoLogLevelEntry *entry = (MonoLogLevelEntry *) g_malloc(sizeof(MonoLogLevelEntry));
202                 entry->level    = mono_internal_current_level;
203                 entry->mask             = mono_internal_current_mask;
204
205                 g_queue_push_head (level_stack, (gpointer)entry);
206
207                 /* Set the new level and mask
208                  */
209                 mono_internal_current_level = level;
210                 mono_internal_current_mask  = mask;
211         }
212 }
213
214 /**
215  * mono_trace_pop:
216  *
217  * Restores level and mask values saved from a previous call to mono_trace_push.
218  */
219 void 
220 mono_trace_pop (void)
221 {
222         if(level_stack == NULL)
223                 g_error("%s: cannot use mono_trace_pop without calling mono_trace_init first.", __func__);
224         else {
225                 if(!g_queue_is_empty (level_stack)) {
226                         MonoLogLevelEntry *entry = (MonoLogLevelEntry*)g_queue_pop_head (level_stack);
227
228                         /*      Restore previous level and mask
229                          */
230                         mono_internal_current_level = entry->level;
231                         mono_internal_current_mask  = entry->mask;
232
233                         g_free (entry);
234                 }
235         }
236 }
237
238
239 void 
240 mono_trace_set_level_string (const char *value)
241 {
242         int i = 0;
243         const char *valid_vals[] = {"error", "critical", "warning", "message", "info", "debug", NULL};
244         const GLogLevelFlags valid_ids[] = {G_LOG_LEVEL_ERROR, G_LOG_LEVEL_CRITICAL, G_LOG_LEVEL_WARNING,
245                                                                                 G_LOG_LEVEL_MESSAGE, G_LOG_LEVEL_INFO, G_LOG_LEVEL_DEBUG };
246
247         if(!value)
248                 return;
249
250         while(valid_vals[i]) {
251                 if(!strcmp(valid_vals[i], value)){
252                         mono_trace_set_level(valid_ids[i]);
253                         return;
254                 }
255                 i++;
256         }
257
258         if(*value)
259                 g_print("Unknown trace loglevel: %s\n", value);
260 }
261
262 void 
263 mono_trace_set_mask_string (const char *value)
264 {
265         int i;
266         const char *tok;
267         guint32 flags = 0;
268
269         const char *valid_flags[] = {"asm", "type", "dll", "gc", "cfg", "aot", "security", "threadpool", "io-threadpool", "io-layer", "w32handle", "all", NULL};
270         const MonoTraceMask     valid_masks[] = {MONO_TRACE_ASSEMBLY, MONO_TRACE_TYPE, MONO_TRACE_DLLIMPORT,
271                                                  MONO_TRACE_GC, MONO_TRACE_CONFIG, MONO_TRACE_AOT, MONO_TRACE_SECURITY,
272                                                  MONO_TRACE_THREADPOOL, MONO_TRACE_IO_THREADPOOL, MONO_TRACE_IO_LAYER,
273                                                  MONO_TRACE_W32HANDLE, MONO_TRACE_ALL };
274
275         if(!value)
276                 return;
277
278         tok = value;
279
280         while (*tok) {
281                 if (*tok == ',') {
282                         tok++;
283                         continue;
284                 }
285                 for (i = 0; valid_flags[i]; i++) {
286                         size_t len = strlen (valid_flags[i]);
287                         if (strncmp (tok, valid_flags[i], len) == 0 && (tok[len] == 0 || tok[len] == ',')) {
288                                 flags |= valid_masks[i];
289                                 tok += len;
290                                 break;
291                         }
292                 }
293                 if (!valid_flags[i]) {
294                         g_print("Unknown trace flag: %s\n", tok);
295                         break;
296                 }
297         }
298
299         mono_trace_set_mask ((MonoTraceMask) flags);
300 }
301
302 /*
303  * mono_trace_is_traced:
304  *
305  *   Returns whenever a message with @level and @mask will be printed or not.
306  */
307 gboolean
308 mono_trace_is_traced (GLogLevelFlags level, MonoTraceMask mask)
309 {
310         return (level <= mono_internal_current_level && mask & mono_internal_current_mask);
311 }
312
313 /**
314  * log_level_get_name
315  * @log_level severity level
316  *
317  * Convert log level into a string for legacy log handlers
318  */
319 static const char *
320 log_level_get_name (GLogLevelFlags log_level)
321 {
322         switch (log_level & G_LOG_LEVEL_MASK) {
323         case G_LOG_LEVEL_ERROR: return "error";
324         case G_LOG_LEVEL_CRITICAL: return "critical";
325         case G_LOG_LEVEL_WARNING: return "warning";
326         case G_LOG_LEVEL_MESSAGE: return "message";
327         case G_LOG_LEVEL_INFO: return "info";
328         case G_LOG_LEVEL_DEBUG: return "debug";
329         default: return "unknown";
330         }
331 }
332
333 /**
334  * callback_adapter
335  * 
336  *  @log_domain Message prefix
337  *  @log_level Severity
338  *  @message Message to be written
339  *  @fatal Fatal flag - write then abort
340  *  @user_data Argument passed to @callback
341  *
342  * This adapts the old callback writer exposed by MonoCallback to the newer method of
343  * logging. We ignore the header request as legacy handlers never had headers.
344  */
345 static void
346 callback_adapter (const char *domain, GLogLevelFlags level, mono_bool fatal, const char *message)
347 {
348         UserSuppliedLoggerUserData *ll =logCallback.user_data;
349
350         ll->legacy_callback (domain, log_level_get_name(level), message, fatal, ll->user_data);
351 }
352
353 static void
354 eglib_log_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
355 {
356         UserSuppliedLoggerUserData *ll = logCallback.user_data;
357
358         ll->legacy_callback (log_domain, log_level_get_name (log_level), message, log_level & G_LOG_LEVEL_ERROR, ll->user_data);
359 }
360
361 /**
362  * legacy_opener
363  *
364  * Dummy routine for older style loggers
365  */
366 static void
367 legacy_opener(const char *path, void *user_data)
368 {
369   /* nothing to do */
370 }
371
372 /**
373  * legacy_closer
374  *
375  * Cleanup routine for older style loggers
376  */
377 static void
378 legacy_closer(void)
379 {
380         if (logCallback.user_data != NULL) {
381                 g_free (logCallback.user_data); /* This is a UserSuppliedLoggerUserData struct */
382                 logCallback.opener = NULL;      
383                 logCallback.writer = NULL;
384                 logCallback.closer = NULL;
385                 logCallback.user_data = NULL;
386                 logCallback.header = FALSE;
387         }
388 }
389
390 /**
391  *   mono_trace_set_log_handler:
392  *  
393  *  @callback The callback that will replace the default logging handler
394  *  @user_data Argument passed to @callback
395  * 
396  * The log handler replaces the default runtime logger. All logging requests with be routed to it.
397  * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that
398  * execution will not resume after a fatal error.
399  */
400 void
401 mono_trace_set_log_handler (MonoLogCallback callback, void *user_data)
402 {
403         g_assert (callback);
404
405         if (logCallback.closer != NULL)
406                 logCallback.closer();
407         UserSuppliedLoggerUserData *ll = g_malloc (sizeof (UserSuppliedLoggerUserData));
408         ll->legacy_callback = callback;
409         ll->user_data = user_data;
410         logCallback.opener = legacy_opener;
411         logCallback.writer = callback_adapter;
412         logCallback.closer = legacy_closer;
413         logCallback.user_data = ll;
414         logCallback.dest = NULL;
415
416         g_log_set_default_handler (eglib_log_adapter, user_data);
417 }
418
419 static void
420 structured_log_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
421 {
422         logCallback.writer (log_domain, log_level, logCallback.header, message);
423 }
424
425 /**
426  * mono_trace_set_log_handler_internal:
427  * \param callback The callback that will replace the default logging handler
428  * \param user_data Argument passed to \p callback
429  * The log handler replaces the default runtime logger. All logging requests with be routed to it.
430  * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that
431  * execution will not resume after a fatal error.
432  */
433 void
434 mono_trace_set_log_handler_internal (MonoLogCallParm *callback, void *user_data)
435 {
436         g_assert (callback);
437         if (logCallback.closer != NULL)
438                 logCallback.closer();
439         logCallback.opener = callback->opener;
440         logCallback.writer = callback->writer;
441         logCallback.closer = callback->closer;
442         logCallback.header = mono_trace_log_header;
443         logCallback.dest   = callback->dest;
444         logCallback.opener (logCallback.dest, user_data);
445
446         g_log_set_default_handler (structured_log_adapter, user_data);
447 }
448
449 static void
450 print_handler (const char *string)
451 {
452         print_callback (string, TRUE);
453 }
454
455 static void
456 printerr_handler (const char *string)
457 {
458         printerr_callback (string, FALSE);
459 }
460
461 /**
462  * mono_trace_set_print_handler:
463  * \param callback The callback that will replace the default runtime behavior for stdout output.
464  * The print handler replaces the default runtime stdout output handler. This is used by free form output done by the runtime.
465  */
466 void
467 mono_trace_set_print_handler (MonoPrintCallback callback)
468 {
469         g_assert (callback);
470         print_callback = callback;
471         g_set_print_handler (print_handler);
472 }
473
474 /**
475  * mono_trace_set_printerr_handler:
476  * \param callback The callback that will replace the default runtime behavior for stderr output.
477  * The print handler replaces the default runtime stderr output handler. This is used by free form output done by the runtime.
478  */
479 void
480 mono_trace_set_printerr_handler (MonoPrintCallback callback)
481 {
482         g_assert (callback);
483         printerr_callback = callback;
484         g_set_printerr_handler (printerr_handler);
485 }