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