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