[coop] Refactor/reuse mono_value_box_handle/mono_value_box_checked and reduce raw...
[mono-project.git] / mono / utils / mono-logger.c
blobfdceebeb26b725afb12b259690189ef603615553
1 /**
2 * \file
3 */
5 #include <string.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <glib.h>
10 #include "mono-compiler.h"
11 #include "mono-logger-internals.h"
13 typedef struct {
14 GLogLevelFlags level;
15 MonoTraceMask mask;
16 } MonoLogLevelEntry;
18 GLogLevelFlags mono_internal_current_level = (GLogLevelFlags)INT_MAX;
19 MonoTraceMask mono_internal_current_mask = (MonoTraceMask)~0;
20 gboolean mono_trace_log_header = FALSE;
22 static GQueue *level_stack = NULL;
23 static const char *mono_log_domain = "Mono";
24 static MonoPrintCallback print_callback, printerr_callback;
26 static MonoLogCallParm logCallback;
28 typedef struct {
29 MonoLogCallback legacy_callback;
30 gpointer user_data;
31 } UserSuppliedLoggerUserData;
33 /**
34 * mono_trace_init:
36 * Initializes the mono tracer.
38 void
39 mono_trace_init (void)
41 if(level_stack == NULL) {
42 mono_internal_current_level = G_LOG_LEVEL_ERROR;
43 level_stack = g_queue_new();
45 char *mask = g_getenv ("MONO_LOG_MASK");
46 char *level = g_getenv ("MONO_LOG_LEVEL");
47 char *header = g_getenv ("MONO_LOG_HEADER");
48 char *dest = g_getenv ("MONO_LOG_DEST");
50 mono_trace_set_mask_string(mask);
51 mono_trace_set_level_string(level);
52 mono_trace_set_logheader_string(header);
53 mono_trace_set_logdest_string(dest);
55 g_free (mask);
56 g_free (level);
57 g_free (header);
58 g_free (dest);
62 /**
63 * mono_trace_cleanup:
65 * Releases the mono tracer.
67 void
68 mono_trace_cleanup (void)
70 if(level_stack != NULL) {
71 while(!g_queue_is_empty (level_stack)) {
72 g_free (g_queue_pop_head (level_stack));
75 logCallback.closer();
76 g_queue_free (level_stack);
77 level_stack = NULL;
81 /**
82 * mono_tracev_inner:
83 * \param level Verbose level of the specified message
84 * \param mask Type of the specified message
85 * Traces a new message, depending on the current logging level
86 * and trace mask.
88 void
89 mono_tracev_inner (GLogLevelFlags level, MonoTraceMask mask, const char *format, va_list args)
91 char *log_message;
92 if (level_stack == NULL) {
93 mono_trace_init ();
94 if(level > mono_internal_current_level || !(mask & mono_internal_current_mask))
95 return;
98 g_assert (logCallback.opener); // mono_trace_init should have provided us with one!
100 if (g_vasprintf (&log_message, format, args) < 0)
101 return;
102 logCallback.writer (mono_log_domain, level, logCallback.header, log_message);
103 g_free (log_message);
107 * mono_trace_set_level:
108 * \param level Verbose level to set
109 * Sets the current logging level. Every subsequent call to
110 * \c mono_trace will check the visibility of a message against this
111 * value.
113 void
114 mono_trace_set_level (GLogLevelFlags level)
116 if(level_stack == NULL)
117 mono_trace_init();
119 mono_internal_current_level = level;
123 * mono_trace_set_mask:
124 * \param mask Mask of visible message types.
125 * Sets the current logging level. Every subsequent call to
126 * \c mono_trace will check the visibility of a message against this
127 * value.
129 void
130 mono_trace_set_mask (MonoTraceMask mask)
132 if(level_stack == NULL)
133 mono_trace_init();
135 mono_internal_current_mask = mask;
139 * mono_trace_set_logdest:
140 * \param dest Destination for logging
141 * Sets the current logging destination. This can be a file or, if supported,
142 * syslog.
144 void
145 mono_trace_set_logdest_string (const char *dest)
147 MonoLogCallParm logger;
149 if(level_stack == NULL)
150 mono_trace_init();
152 #if HOST_ANDROID
153 logger.opener = mono_log_open_logcat;
154 logger.writer = mono_log_write_logcat;
155 logger.closer = mono_log_close_logcat;
156 logger.dest = (char*) dest;
157 #elif defined (HOST_IOS)
158 logger.opener = mono_log_open_asl;
159 logger.writer = mono_log_write_asl;
160 logger.closer = mono_log_close_asl;
161 logger.dest = (char*) dest;
162 #else
163 if (dest && !strcmp("flight-recorder", dest)) {
164 logger.opener = mono_log_open_recorder;
165 logger.writer = mono_log_write_recorder;
166 logger.closer = mono_log_close_recorder;
167 logger.dest = (char *) dest;
169 // Increase log level with flight recorder
170 if (mono_internal_current_level == G_LOG_LEVEL_ERROR || mono_internal_current_level == G_LOG_LEVEL_CRITICAL)
171 mono_trace_set_level (G_LOG_LEVEL_WARNING);
173 } else if (dest && !strcmp("syslog", dest)) {
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 } else {
179 logger.opener = mono_log_open_logfile;
180 logger.writer = mono_log_write_logfile;
181 logger.closer = mono_log_close_logfile;
182 logger.dest = (char *) dest;
184 #endif
186 mono_trace_set_log_handler_internal(&logger, NULL);
190 * mono_trace_set_logheader:
191 * \param head Whether we want pid/date/time header on log messages
192 * Sets the current logging header option.
194 void
195 mono_trace_set_logheader_string(const char *head)
197 if (head == NULL) {
198 mono_trace_log_header = FALSE;
199 } else {
200 mono_trace_log_header = TRUE;
205 * mono_trace_push:
206 * \param level Verbose level to set
207 * \param mask Mask of visible message types.
208 * Saves the current values of level and mask then calls \c mono_trace_set
209 * with the specified new values.
211 void
212 mono_trace_push (GLogLevelFlags level, MonoTraceMask mask)
214 if(level_stack == NULL)
215 g_error("%s: cannot use mono_trace_push without calling mono_trace_init first.", __func__);
216 else {
217 MonoLogLevelEntry *entry = (MonoLogLevelEntry *) g_malloc(sizeof(MonoLogLevelEntry));
218 entry->level = mono_internal_current_level;
219 entry->mask = mono_internal_current_mask;
221 g_queue_push_head (level_stack, (gpointer)entry);
223 /* Set the new level and mask
225 mono_internal_current_level = level;
226 mono_internal_current_mask = mask;
231 * mono_trace_pop:
233 * Restores level and mask values saved from a previous call to mono_trace_push.
235 void
236 mono_trace_pop (void)
238 if(level_stack == NULL)
239 g_error("%s: cannot use mono_trace_pop without calling mono_trace_init first.", __func__);
240 else {
241 if(!g_queue_is_empty (level_stack)) {
242 MonoLogLevelEntry *entry = (MonoLogLevelEntry*)g_queue_pop_head (level_stack);
244 /* Restore previous level and mask
246 mono_internal_current_level = entry->level;
247 mono_internal_current_mask = entry->mask;
249 g_free (entry);
255 void
256 mono_trace_set_level_string (const char *value)
258 int i = 0;
259 const char *valid_vals[] = {"error", "critical", "warning", "message", "info", "debug", NULL};
260 const GLogLevelFlags valid_ids[] = {G_LOG_LEVEL_ERROR, G_LOG_LEVEL_CRITICAL, G_LOG_LEVEL_WARNING,
261 G_LOG_LEVEL_MESSAGE, G_LOG_LEVEL_INFO, G_LOG_LEVEL_DEBUG };
263 if(!value)
264 return;
266 while(valid_vals[i]) {
267 if(!strcmp(valid_vals[i], value)){
268 mono_trace_set_level(valid_ids[i]);
269 return;
271 i++;
274 if(*value)
275 g_print("Unknown trace loglevel: %s\n", value);
278 void
279 mono_trace_set_mask_string (const char *value)
281 int i;
282 const char *tok;
283 guint32 flags = 0;
285 static const struct { const char * const flag; const MonoTraceMask mask; } flag_mask_map[] = {
286 { "asm", MONO_TRACE_ASSEMBLY },
287 { "type", MONO_TRACE_TYPE },
288 { "dll", MONO_TRACE_DLLIMPORT },
289 { "gc", MONO_TRACE_GC },
290 { "cfg", MONO_TRACE_CONFIG },
291 { "aot", MONO_TRACE_AOT },
292 { "security", MONO_TRACE_SECURITY },
293 { "threadpool", MONO_TRACE_THREADPOOL },
294 { "io-threadpool", MONO_TRACE_IO_SELECTOR },
295 { "io-selector", MONO_TRACE_IO_SELECTOR },
296 { "io-layer-process", MONO_TRACE_IO_LAYER_PROCESS },
297 { "io-layer-socket", MONO_TRACE_IO_LAYER_SOCKET },
298 { "io-layer-file", MONO_TRACE_IO_LAYER_FILE },
299 { "io-layer-console", MONO_TRACE_IO_LAYER_FILE },
300 { "io-layer-pipe", MONO_TRACE_IO_LAYER_FILE },
301 { "io-layer-event", MONO_TRACE_IO_LAYER_EVENT },
302 { "io-layer-semaphore", MONO_TRACE_IO_LAYER_SEMAPHORE },
303 { "io-layer-mutex", MONO_TRACE_IO_LAYER_MUTEX },
304 { "io-layer-handle", MONO_TRACE_IO_LAYER_HANDLE },
305 { "io-layer", (MonoTraceMask)(MONO_TRACE_IO_LAYER_PROCESS
306 | MONO_TRACE_IO_LAYER_SOCKET
307 | MONO_TRACE_IO_LAYER_FILE
308 | MONO_TRACE_IO_LAYER_EVENT
309 | MONO_TRACE_IO_LAYER_SEMAPHORE
310 | MONO_TRACE_IO_LAYER_MUTEX
311 | MONO_TRACE_IO_LAYER_HANDLE) },
312 { "w32handle", MONO_TRACE_IO_LAYER_HANDLE },
313 { "tailcall", MONO_TRACE_TAILCALL },
314 { "profiler", MONO_TRACE_PROFILER },
315 { "all", (MonoTraceMask)~0 }, // FIXMEcxx there is a better way -- operator overloads of enums
316 { NULL, (MonoTraceMask)0 },
319 if(!value)
320 return;
322 tok = value;
324 while (*tok) {
325 if (*tok == ',') {
326 tok++;
327 continue;
329 for (i = 0; flag_mask_map[i].flag; i++) {
330 size_t len = strlen (flag_mask_map[i].flag);
331 if (strncmp (tok, flag_mask_map[i].flag, len) == 0 && (tok[len] == 0 || tok[len] == ',')) {
332 flags |= flag_mask_map[i].mask;
333 tok += len;
334 break;
337 if (!flag_mask_map[i].flag) {
338 g_print("Unknown trace flag: %s\n", tok);
339 break;
343 mono_trace_set_mask ((MonoTraceMask) flags);
347 * mono_trace_is_traced:
349 * Returns whenever a message with @level and @mask will be printed or not.
351 gboolean
352 mono_trace_is_traced (GLogLevelFlags level, MonoTraceMask mask)
354 return MONO_TRACE_IS_TRACED (level, mask);
358 * log_level_get_name
359 * @log_level severity level
361 * Convert log level into a string for legacy log handlers
363 static const char *
364 log_level_get_name (GLogLevelFlags log_level)
366 switch (log_level & G_LOG_LEVEL_MASK) {
367 case G_LOG_LEVEL_ERROR: return "error";
368 case G_LOG_LEVEL_CRITICAL: return "critical";
369 case G_LOG_LEVEL_WARNING: return "warning";
370 case G_LOG_LEVEL_MESSAGE: return "message";
371 case G_LOG_LEVEL_INFO: return "info";
372 case G_LOG_LEVEL_DEBUG: return "debug";
373 default: return "unknown";
378 * callback_adapter
380 * @log_domain Message prefix
381 * @log_level Severity
382 * @message Message to be written
383 * @fatal Fatal flag - write then abort
384 * @user_data Argument passed to @callback
386 * This adapts the old callback writer exposed by MonoCallback to the newer method of
387 * logging. We ignore the header request as legacy handlers never had headers.
389 static void
390 callback_adapter (const char *domain, GLogLevelFlags level, mono_bool fatal, const char *message)
392 UserSuppliedLoggerUserData *ll = (UserSuppliedLoggerUserData*)logCallback.user_data;
394 ll->legacy_callback (domain, log_level_get_name(level), message, fatal, ll->user_data);
397 static void
398 eglib_log_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
400 UserSuppliedLoggerUserData *ll = (UserSuppliedLoggerUserData*)logCallback.user_data;
402 ll->legacy_callback (log_domain, log_level_get_name (log_level), message, log_level & G_LOG_LEVEL_ERROR, ll->user_data);
406 * legacy_opener
408 * Dummy routine for older style loggers
410 static void
411 legacy_opener(const char *path, void *user_data)
413 /* nothing to do */
417 * legacy_closer
419 * Cleanup routine for older style loggers
421 static void
422 legacy_closer(void)
424 if (logCallback.user_data != NULL) {
425 g_free (logCallback.user_data); /* This is a UserSuppliedLoggerUserData struct */
426 logCallback.opener = NULL;
427 logCallback.writer = NULL;
428 logCallback.closer = NULL;
429 logCallback.user_data = NULL;
430 logCallback.header = FALSE;
435 * mono_trace_set_log_handler:
437 * @callback The callback that will replace the default logging handler
438 * @user_data Argument passed to @callback
440 * The log handler replaces the default runtime logger. All logging requests with be routed to it.
441 * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that
442 * execution will not resume after a fatal error.
444 void
445 mono_trace_set_log_handler (MonoLogCallback callback, void *user_data)
447 g_assert (callback);
449 if (logCallback.closer != NULL)
450 logCallback.closer();
451 UserSuppliedLoggerUserData *ll = (UserSuppliedLoggerUserData*)g_malloc (sizeof (UserSuppliedLoggerUserData));
452 ll->legacy_callback = callback;
453 ll->user_data = user_data;
454 logCallback.opener = legacy_opener;
455 logCallback.writer = callback_adapter;
456 logCallback.closer = legacy_closer;
457 logCallback.user_data = ll;
458 logCallback.dest = NULL;
460 g_log_set_default_handler (eglib_log_adapter, user_data);
463 static void
464 structured_log_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
466 logCallback.writer (log_domain, log_level, logCallback.header, message);
470 * mono_trace_set_log_handler_internal:
471 * \param callback The callback that will replace the default logging handler
472 * \param user_data Argument passed to \p callback
473 * The log handler replaces the default runtime logger. All logging requests with be routed to it.
474 * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that
475 * execution will not resume after a fatal error.
477 void
478 mono_trace_set_log_handler_internal (MonoLogCallParm *callback, void *user_data)
480 g_assert (callback);
481 if (logCallback.closer != NULL)
482 logCallback.closer();
483 logCallback.opener = callback->opener;
484 logCallback.writer = callback->writer;
485 logCallback.closer = callback->closer;
486 logCallback.header = mono_trace_log_header;
487 logCallback.dest = callback->dest;
488 logCallback.opener (logCallback.dest, user_data);
490 g_log_set_default_handler (structured_log_adapter, user_data);
493 static void
494 print_handler (const char *string)
496 print_callback (string, TRUE);
499 static void
500 printerr_handler (const char *string)
502 printerr_callback (string, FALSE);
506 * mono_trace_set_print_handler:
507 * \param callback The callback that will replace the default runtime behavior for stdout output.
508 * The print handler replaces the default runtime stdout output handler. This is used by free form output done by the runtime.
510 void
511 mono_trace_set_print_handler (MonoPrintCallback callback)
513 g_assert (callback);
514 print_callback = callback;
515 g_set_print_handler (print_handler);
519 * mono_trace_set_printerr_handler:
520 * \param callback The callback that will replace the default runtime behavior for stderr output.
521 * The print handler replaces the default runtime stderr output handler. This is used by free form output done by the runtime.
523 void
524 mono_trace_set_printerr_handler (MonoPrintCallback callback)
526 g_assert (callback);
527 printerr_callback = callback;
528 g_set_printerr_handler (printerr_handler);