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