[sgen] Add debug option for valloc limit
[mono-project.git] / mono / utils / mono-logger.c
blob4b32dfb0334b17fe02402fde748554536f770b1b
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 = INT_MAX;
19 MonoTraceMask mono_internal_current_mask = MONO_TRACE_ALL;
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 = {
27 .opener = NULL,
28 .writer = NULL,
29 .closer = NULL,
30 .header = FALSE
33 typedef struct {
34 MonoLogCallback legacy_callback;
35 gpointer user_data;
36 } UserSuppliedLoggerUserData;
38 /**
39 * mono_trace_init:
41 * Initializes the mono tracer.
43 void
44 mono_trace_init (void)
46 if(level_stack == NULL) {
47 mono_internal_current_level = G_LOG_LEVEL_ERROR;
48 level_stack = g_queue_new();
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"));
57 /**
58 * mono_trace_cleanup:
60 * Releases the mono tracer.
62 void
63 mono_trace_cleanup (void)
65 if(level_stack != NULL) {
66 while(!g_queue_is_empty (level_stack)) {
67 g_free (g_queue_pop_head (level_stack));
70 logCallback.closer();
71 g_queue_free (level_stack);
72 level_stack = NULL;
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.
83 void
84 mono_tracev_inner (GLogLevelFlags level, MonoTraceMask mask, const char *format, va_list args)
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;
93 g_assert (logCallback.opener); // mono_trace_init should have provided us with one!
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);
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.
108 void
109 mono_trace_set_level (GLogLevelFlags level)
111 if(level_stack == NULL)
112 mono_trace_init();
114 mono_internal_current_level = level;
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.
124 void
125 mono_trace_set_mask (MonoTraceMask mask)
127 if(level_stack == NULL)
128 mono_trace_init();
130 mono_internal_current_mask = mask;
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.
139 void
140 mono_trace_set_logdest_string (const char *dest)
142 MonoLogCallParm logger;
144 if(level_stack == NULL)
145 mono_trace_init();
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;
169 #endif
170 mono_trace_set_log_handler_internal(&logger, NULL);
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.
178 void
179 mono_trace_set_logheader_string(const char *head)
181 if (head == NULL) {
182 mono_trace_log_header = FALSE;
183 } else {
184 mono_trace_log_header = TRUE;
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.
195 void
196 mono_trace_push (GLogLevelFlags level, MonoTraceMask mask)
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;
205 g_queue_push_head (level_stack, (gpointer)entry);
207 /* Set the new level and mask
209 mono_internal_current_level = level;
210 mono_internal_current_mask = mask;
215 * mono_trace_pop:
217 * Restores level and mask values saved from a previous call to mono_trace_push.
219 void
220 mono_trace_pop (void)
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);
228 /* Restore previous level and mask
230 mono_internal_current_level = entry->level;
231 mono_internal_current_mask = entry->mask;
233 g_free (entry);
239 void
240 mono_trace_set_level_string (const char *value)
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 };
247 if(!value)
248 return;
250 while(valid_vals[i]) {
251 if(!strcmp(valid_vals[i], value)){
252 mono_trace_set_level(valid_ids[i]);
253 return;
255 i++;
258 if(*value)
259 g_print("Unknown trace loglevel: %s\n", value);
262 void
263 mono_trace_set_mask_string (const char *value)
265 int i;
266 const char *tok;
267 guint32 flags = 0;
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 };
275 if(!value)
276 return;
278 tok = value;
280 while (*tok) {
281 if (*tok == ',') {
282 tok++;
283 continue;
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;
293 if (!valid_flags[i]) {
294 g_print("Unknown trace flag: %s\n", tok);
295 break;
299 mono_trace_set_mask ((MonoTraceMask) flags);
303 * mono_trace_is_traced:
305 * Returns whenever a message with @level and @mask will be printed or not.
307 gboolean
308 mono_trace_is_traced (GLogLevelFlags level, MonoTraceMask mask)
310 return (level <= mono_internal_current_level && mask & mono_internal_current_mask);
314 * log_level_get_name
315 * @log_level severity level
317 * Convert log level into a string for legacy log handlers
319 static const char *
320 log_level_get_name (GLogLevelFlags log_level)
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";
334 * callback_adapter
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
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.
345 static void
346 callback_adapter (const char *domain, GLogLevelFlags level, mono_bool fatal, const char *message)
348 UserSuppliedLoggerUserData *ll =logCallback.user_data;
350 ll->legacy_callback (domain, log_level_get_name(level), message, fatal, ll->user_data);
353 static void
354 eglib_log_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
356 UserSuppliedLoggerUserData *ll = logCallback.user_data;
358 ll->legacy_callback (log_domain, log_level_get_name (log_level), message, log_level & G_LOG_LEVEL_ERROR, ll->user_data);
362 * legacy_opener
364 * Dummy routine for older style loggers
366 static void
367 legacy_opener(const char *path, void *user_data)
369 /* nothing to do */
373 * legacy_closer
375 * Cleanup routine for older style loggers
377 static void
378 legacy_closer(void)
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;
391 * mono_trace_set_log_handler:
393 * @callback The callback that will replace the default logging handler
394 * @user_data Argument passed to @callback
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.
400 void
401 mono_trace_set_log_handler (MonoLogCallback callback, void *user_data)
403 g_assert (callback);
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;
416 g_log_set_default_handler (eglib_log_adapter, user_data);
419 static void
420 structured_log_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
422 logCallback.writer (log_domain, log_level, logCallback.header, message);
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.
433 void
434 mono_trace_set_log_handler_internal (MonoLogCallParm *callback, void *user_data)
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);
446 g_log_set_default_handler (structured_log_adapter, user_data);
449 static void
450 print_handler (const char *string)
452 print_callback (string, TRUE);
455 static void
456 printerr_handler (const char *string)
458 printerr_callback (string, FALSE);
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.
466 void
467 mono_trace_set_print_handler (MonoPrintCallback callback)
469 g_assert (callback);
470 print_callback = callback;
471 g_set_print_handler (print_handler);
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.
479 void
480 mono_trace_set_printerr_handler (MonoPrintCallback callback)
482 g_assert (callback);
483 printerr_callback = callback;
484 g_set_printerr_handler (printerr_handler);