6ed2db36482450d63fb6bd23f463efc6e5909032
[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
17 static GQueue           *level_stack            = NULL;
18 static const char       *mono_log_domain        = "Mono";
19 static MonoPrintCallback print_callback, printerr_callback;
20
21 static MonoLogCallback logCallback = {
22         .opener = NULL,
23         .writer = NULL,
24         .closer = NULL
25 };
26
27 /**
28  * mono_trace_init:
29  *
30  * Initializes the mono tracer.
31  */
32 void 
33 mono_trace_init (void)
34 {
35         if(level_stack == NULL) {
36                 mono_internal_current_level = G_LOG_LEVEL_ERROR;
37                 level_stack = g_queue_new();
38
39                 mono_trace_set_mask_string(g_getenv("MONO_LOG_MASK"));
40                 mono_trace_set_level_string(g_getenv("MONO_LOG_LEVEL"));
41                 mono_trace_set_logdest_string(g_getenv("MONO_LOG_DEST"));
42         }
43 }
44
45 /**
46  * mono_trace_cleanup:
47  *
48  * Releases the mono tracer.
49  */
50 void 
51 mono_trace_cleanup (void)
52 {
53         if(level_stack != NULL) {
54                 while(!g_queue_is_empty (level_stack)) {
55                         g_free (g_queue_pop_head (level_stack));
56                 }
57
58                 g_queue_free (level_stack);
59                 level_stack = NULL;
60         }
61 }
62
63 /**
64  * mono_tracev:
65  *
66  *      @level: Verbose level of the specified message
67  *      @mask: Type of the specified message
68  *
69  * Traces a new message, depending on the current logging level
70  * and trace mask.
71  */
72 void 
73 mono_tracev_inner (GLogLevelFlags level, MonoTraceMask mask, const char *format, va_list args)
74 {
75         if (level_stack == NULL) {
76                 mono_trace_init ();
77                 if(level > mono_internal_current_level || !(mask & mono_internal_current_mask))
78                         return;
79         }
80
81         if (logCallback.opener == NULL) {
82                 logCallback.opener = mono_log_open_logfile;
83                 logCallback.writer = mono_log_write_logfile;
84                 logCallback.closer = mono_log_close_logfile;
85                 logCallback.opener(NULL, NULL);
86         }
87         logCallback.writer(mono_log_domain, level, format, args);
88 }
89
90 /**
91  * mono_trace_set_level:
92  *
93  *      @level: Verbose level to set
94  *
95  * Sets the current logging level. Every subsequent call to
96  * mono_trace will check the visibility of a message against this
97  * value.
98  */
99 void 
100 mono_trace_set_level (GLogLevelFlags level)
101 {
102         if(level_stack == NULL)
103                 mono_trace_init();
104
105         mono_internal_current_level = level;
106 }
107
108 /**
109  * mono_trace_set_mask:
110  *
111  *      @mask: Mask of visible message types.
112  *
113  * Sets the current logging level. Every subsequent call to
114  * mono_trace will check the visibility of a message against this
115  * value.
116  */
117 void 
118 mono_trace_set_mask (MonoTraceMask mask)
119 {
120         if(level_stack == NULL)
121                 mono_trace_init();
122
123         mono_internal_current_mask      = mask;
124 }
125
126 /**
127  * mono_trace_set_logdest:
128  *
129  *      @dest: Destination for logging
130  *
131  * Sets the current logging destination. This can be a file or, if supported,
132  * syslog.
133  */
134 void 
135 mono_trace_set_logdest_string (const char *dest)
136 {
137         MonoLogCallback logger;
138
139         if(level_stack == NULL)
140                 mono_trace_init();
141
142         if ((dest == NULL) || (strcmp("syslog", dest) != 0)) {
143                 logger.opener = mono_log_open_logfile;
144                 logger.writer = mono_log_write_logfile;
145                 logger.closer = mono_log_close_logfile;
146                 mono_trace_set_log_handler(&logger, dest, NULL);
147         } else {
148                 logger.opener = mono_log_open_syslog;
149                 logger.writer = mono_log_write_syslog;
150                 logger.closer = mono_log_close_syslog;
151                 mono_trace_set_log_handler(&logger, mono_log_domain, NULL);
152         }
153 }
154
155 /**
156  * mono_trace_push:
157  *
158  *      @level: Verbose level to set
159  *      @mask: Mask of visible message types.
160  *
161  * Saves the current values of level and mask then calls mono_trace_set
162  * with the specified new values.
163  */
164 void 
165 mono_trace_push (GLogLevelFlags level, MonoTraceMask mask)
166 {
167         if(level_stack == NULL)
168                 g_error("%s: cannot use mono_trace_push without calling mono_trace_init first.", __func__);
169         else {
170                 MonoLogLevelEntry *entry = (MonoLogLevelEntry *) g_malloc(sizeof(MonoLogLevelEntry));
171                 entry->level    = mono_internal_current_level;
172                 entry->mask             = mono_internal_current_mask;
173
174                 g_queue_push_head (level_stack, (gpointer)entry);
175
176                 /* Set the new level and mask
177                  */
178                 mono_internal_current_level = level;
179                 mono_internal_current_mask  = mask;
180         }
181 }
182
183 /**
184  * mono_trace_pop:
185  *
186  * Restores level and mask values saved from a previous call to mono_trace_push.
187  */
188 void 
189 mono_trace_pop (void)
190 {
191         if(level_stack == NULL)
192                 g_error("%s: cannot use mono_trace_pop without calling mono_trace_init first.", __func__);
193         else {
194                 if(!g_queue_is_empty (level_stack)) {
195                         MonoLogLevelEntry *entry = (MonoLogLevelEntry*)g_queue_pop_head (level_stack);
196
197                         /*      Restore previous level and mask
198                          */
199                         mono_internal_current_level = entry->level;
200                         mono_internal_current_mask  = entry->mask;
201
202                         g_free (entry);
203                 }
204         }
205 }
206
207
208 void 
209 mono_trace_set_level_string (const char *value)
210 {
211         int i = 0;
212         const char *valid_vals[] = {"error", "critical", "warning", "message", "info", "debug", NULL};
213         const GLogLevelFlags valid_ids[] = {G_LOG_LEVEL_ERROR, G_LOG_LEVEL_CRITICAL, G_LOG_LEVEL_WARNING,
214                                                                                 G_LOG_LEVEL_MESSAGE, G_LOG_LEVEL_INFO, G_LOG_LEVEL_DEBUG };
215
216         if(!value)
217                 return;
218
219         while(valid_vals[i]) {
220                 if(!strcmp(valid_vals[i], value)){
221                         mono_trace_set_level(valid_ids[i]);
222                         return;
223                 }
224                 i++;
225         }
226
227         if(*value)
228                 g_print("Unknown trace loglevel: %s\n", value);
229 }
230
231 void 
232 mono_trace_set_mask_string (const char *value)
233 {
234         int i;
235         const char *tok;
236         guint32 flags = 0;
237
238         const char *valid_flags[] = {"asm", "type", "dll", "gc", "cfg", "aot", "security", "threadpool", "io-threadpool", "io-layer", "all", NULL};
239         const MonoTraceMask     valid_masks[] = {MONO_TRACE_ASSEMBLY, MONO_TRACE_TYPE, MONO_TRACE_DLLIMPORT,
240                                                  MONO_TRACE_GC, MONO_TRACE_CONFIG, MONO_TRACE_AOT, MONO_TRACE_SECURITY,
241                                                  MONO_TRACE_THREADPOOL, MONO_TRACE_IO_THREADPOOL, MONO_TRACE_IO_LAYER, MONO_TRACE_ALL };
242
243         if(!value)
244                 return;
245
246         tok = value;
247
248         while (*tok) {
249                 if (*tok == ',') {
250                         tok++;
251                         continue;
252                 }
253                 for (i = 0; valid_flags[i]; i++) {
254                         int len = strlen (valid_flags[i]);
255                         if (strncmp (tok, valid_flags[i], len) == 0 && (tok[len] == 0 || tok[len] == ',')) {
256                                 flags |= valid_masks[i];
257                                 tok += len;
258                                 break;
259                         }
260                 }
261                 if (!valid_flags[i]) {
262                         g_print("Unknown trace flag: %s\n", tok);
263                         break;
264                 }
265         }
266
267         mono_trace_set_mask ((MonoTraceMask) flags);
268 }
269
270 /*
271  * mono_trace_is_traced:
272  *
273  *   Returns whenever a message with @level and @mask will be printed or not.
274  */
275 gboolean
276 mono_trace_is_traced (GLogLevelFlags level, MonoTraceMask mask)
277 {
278         return (level <= mono_internal_current_level && mask & mono_internal_current_mask);
279 }
280
281 /**
282  * mono_trace_set_log_handler:
283  *
284  *  @callback The callback that will replace the default logging handler
285  *  @user_data Argument passed to @callback
286  *
287  * The log handler replaces the default runtime logger. All logging requests with be routed to it.
288  * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that
289  * execution will not resume after a fatal error.
290  */
291 void
292 mono_trace_set_log_handler (MonoLogCallback *callback, const char *dest, void *user_data)
293 {
294         g_assert (callback);
295         logCallback.opener = callback->opener;
296         logCallback.writer = callback->writer;
297         logCallback.closer = callback->closer;
298         logCallback.opener(dest, user_data);
299 }
300
301 static void
302 print_handler (const char *string)
303 {
304         print_callback (string, TRUE);
305 }
306
307 static void
308 printerr_handler (const char *string)
309 {
310         printerr_callback (string, FALSE);
311 }
312
313 /**
314  * mono_trace_set_print_handler:
315  *
316  * @callback The callback that will replace the default runtime behavior for stdout output.
317  *
318  * The print handler replaces the default runtime stdout output handler. This is used by free form output done by the runtime.
319  *
320  */
321 void
322 mono_trace_set_print_handler (MonoPrintCallback callback)
323 {
324         g_assert (callback);
325         print_callback = callback;
326         g_set_print_handler (print_handler);
327 }
328
329 /**
330  * mono_trace_set_printerr_handler:
331  *
332  * @callback The callback that will replace the default runtime behavior for stderr output.
333  *
334  * The print handler replaces the default runtime stderr output handler. This is used by free form output done by the runtime.
335  *
336  */
337 void
338 mono_trace_set_printerr_handler (MonoPrintCallback callback)
339 {
340         g_assert (callback);
341         printerr_callback = callback;
342         g_set_printerr_handler (printerr_handler);
343 }