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