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