Merge pull request #3199 from lambdageek/dev/proxy-setter
[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 MonoLogCallParm logCallback = {
23         .opener = NULL,
24         .writer = NULL,
25         .closer = NULL,
26         .header = FALSE
27 };
28
29 typedef struct {
30    MonoLogCallback legacy_callback;
31    gpointer user_data;
32 } legacyLoggerUserData;
33
34 /**
35  * mono_trace_init:
36  *
37  * Initializes the mono tracer.
38  */
39 void 
40 mono_trace_init (void)
41 {
42         if(level_stack == NULL) {
43                 mono_internal_current_level = G_LOG_LEVEL_ERROR;
44                 level_stack = g_queue_new();
45
46                 mono_trace_set_mask_string(g_getenv("MONO_LOG_MASK"));
47                 mono_trace_set_level_string(g_getenv("MONO_LOG_LEVEL"));
48                 mono_trace_set_logheader_string(g_getenv("MONO_LOG_HEADER"));
49                 mono_trace_set_logdest_string(g_getenv("MONO_LOG_DEST"));
50         }
51 }
52
53 /**
54  * mono_trace_cleanup:
55  *
56  * Releases the mono tracer.
57  */
58 void 
59 mono_trace_cleanup (void)
60 {
61         if(level_stack != NULL) {
62                 while(!g_queue_is_empty (level_stack)) {
63                         g_free (g_queue_pop_head (level_stack));
64                 }
65
66                 logCallback.closer();
67                 g_queue_free (level_stack);
68                 level_stack = NULL;
69         }
70 }
71
72 /**
73  * mono_tracev:
74  *
75  *      @level: Verbose level of the specified message
76  *      @mask: Type of the specified message
77  *
78  * Traces a new message, depending on the current logging level
79  * and trace mask.
80  */
81 void 
82 mono_tracev_inner (GLogLevelFlags level, MonoTraceMask mask, const char *format, va_list args)
83 {
84         if (level_stack == NULL) {
85                 mono_trace_init ();
86                 if(level > mono_internal_current_level || !(mask & mono_internal_current_mask))
87                         return;
88         }
89
90         if (logCallback.opener == NULL) {
91                 logCallback.opener = mono_log_open_logfile;
92                 logCallback.writer = mono_log_write_logfile;
93                 logCallback.closer = mono_log_close_logfile;
94                 logCallback.opener(NULL, NULL);
95         }
96         logCallback.writer(mono_log_domain, level, logCallback.header, format, args);
97 }
98
99 /**
100  * mono_trace_set_level:
101  *
102  *      @level: Verbose level to set
103  *
104  * Sets the current logging level. Every subsequent call to
105  * mono_trace will check the visibility of a message against this
106  * value.
107  */
108 void 
109 mono_trace_set_level (GLogLevelFlags level)
110 {
111         if(level_stack == NULL)
112                 mono_trace_init();
113
114         mono_internal_current_level = level;
115 }
116
117 /**
118  * mono_trace_set_mask:
119  *
120  *      @mask: Mask of visible message types.
121  *
122  * Sets the current logging level. Every subsequent call to
123  * mono_trace will check the visibility of a message against this
124  * value.
125  */
126 void 
127 mono_trace_set_mask (MonoTraceMask mask)
128 {
129         if(level_stack == NULL)
130                 mono_trace_init();
131
132         mono_internal_current_mask = mask;
133 }
134
135 /**
136  * mono_trace_set_logdest:
137  *
138  *      @dest: Destination for logging
139  *
140  * Sets the current logging destination. This can be a file or, if supported,
141  * syslog.
142  */
143 void 
144 mono_trace_set_logdest_string (const char *dest)
145 {
146         MonoLogCallParm logger;
147
148         if(level_stack == NULL)
149                 mono_trace_init();
150
151         if ((dest == NULL) || (strcmp("syslog", dest) != 0)) {
152                 logger.opener = mono_log_open_logfile;
153                 logger.writer = mono_log_write_logfile;
154                 logger.closer = mono_log_close_logfile;
155                 logger.dest   = (char *) dest;
156                 mono_trace_set_log_handler_internal(&logger, NULL);
157         } else {
158                 logger.opener = mono_log_open_syslog;
159                 logger.writer = mono_log_write_syslog;
160                 logger.closer = mono_log_close_syslog;
161                 logger.dest   = (char *) dest;
162                 mono_trace_set_log_handler_internal(&logger, NULL);
163         }
164 }
165
166 /**
167  * mono_trace_set_logheader:
168  *
169  *      @head: Whether we want pid/date/time header on log messages
170  *
171  * Sets the current logging header option.
172  */
173 void 
174 mono_trace_set_logheader_string(const char *head)
175 {
176         if (head == NULL) {
177                 mono_trace_log_header = FALSE;
178         } else {
179                 mono_trace_log_header = TRUE;
180         }
181 }
182
183 /**
184  * mono_trace_push:
185  *
186  *      @level: Verbose level to set
187  *      @mask: Mask of visible message types.
188  *
189  * Saves the current values of level and mask then calls mono_trace_set
190  * with the specified new values.
191  */
192 void 
193 mono_trace_push (GLogLevelFlags level, MonoTraceMask mask)
194 {
195         if(level_stack == NULL)
196                 g_error("%s: cannot use mono_trace_push without calling mono_trace_init first.", __func__);
197         else {
198                 MonoLogLevelEntry *entry = (MonoLogLevelEntry *) g_malloc(sizeof(MonoLogLevelEntry));
199                 entry->level    = mono_internal_current_level;
200                 entry->mask             = mono_internal_current_mask;
201
202                 g_queue_push_head (level_stack, (gpointer)entry);
203
204                 /* Set the new level and mask
205                  */
206                 mono_internal_current_level = level;
207                 mono_internal_current_mask  = mask;
208         }
209 }
210
211 /**
212  * mono_trace_pop:
213  *
214  * Restores level and mask values saved from a previous call to mono_trace_push.
215  */
216 void 
217 mono_trace_pop (void)
218 {
219         if(level_stack == NULL)
220                 g_error("%s: cannot use mono_trace_pop without calling mono_trace_init first.", __func__);
221         else {
222                 if(!g_queue_is_empty (level_stack)) {
223                         MonoLogLevelEntry *entry = (MonoLogLevelEntry*)g_queue_pop_head (level_stack);
224
225                         /*      Restore previous level and mask
226                          */
227                         mono_internal_current_level = entry->level;
228                         mono_internal_current_mask  = entry->mask;
229
230                         g_free (entry);
231                 }
232         }
233 }
234
235
236 void 
237 mono_trace_set_level_string (const char *value)
238 {
239         int i = 0;
240         const char *valid_vals[] = {"error", "critical", "warning", "message", "info", "debug", NULL};
241         const GLogLevelFlags valid_ids[] = {G_LOG_LEVEL_ERROR, G_LOG_LEVEL_CRITICAL, G_LOG_LEVEL_WARNING,
242                                                                                 G_LOG_LEVEL_MESSAGE, G_LOG_LEVEL_INFO, G_LOG_LEVEL_DEBUG };
243
244         if(!value)
245                 return;
246
247         while(valid_vals[i]) {
248                 if(!strcmp(valid_vals[i], value)){
249                         mono_trace_set_level(valid_ids[i]);
250                         return;
251                 }
252                 i++;
253         }
254
255         if(*value)
256                 g_print("Unknown trace loglevel: %s\n", value);
257 }
258
259 void 
260 mono_trace_set_mask_string (const char *value)
261 {
262         int i;
263         const char *tok;
264         guint32 flags = 0;
265
266         const char *valid_flags[] = {"asm", "type", "dll", "gc", "cfg", "aot", "security", "threadpool", "io-threadpool", "io-layer", "w32handle", "all", NULL};
267         const MonoTraceMask     valid_masks[] = {MONO_TRACE_ASSEMBLY, MONO_TRACE_TYPE, MONO_TRACE_DLLIMPORT,
268                                                  MONO_TRACE_GC, MONO_TRACE_CONFIG, MONO_TRACE_AOT, MONO_TRACE_SECURITY,
269                                                  MONO_TRACE_THREADPOOL, MONO_TRACE_IO_THREADPOOL, MONO_TRACE_IO_LAYER,
270                                                  MONO_TRACE_W32HANDLE, MONO_TRACE_ALL };
271
272         if(!value)
273                 return;
274
275         tok = value;
276
277         while (*tok) {
278                 if (*tok == ',') {
279                         tok++;
280                         continue;
281                 }
282                 for (i = 0; valid_flags[i]; i++) {
283                         int len = strlen (valid_flags[i]);
284                         if (strncmp (tok, valid_flags[i], len) == 0 && (tok[len] == 0 || tok[len] == ',')) {
285                                 flags |= valid_masks[i];
286                                 tok += len;
287                                 break;
288                         }
289                 }
290                 if (!valid_flags[i]) {
291                         g_print("Unknown trace flag: %s\n", tok);
292                         break;
293                 }
294         }
295
296         mono_trace_set_mask ((MonoTraceMask) flags);
297 }
298
299 /*
300  * mono_trace_is_traced:
301  *
302  *   Returns whenever a message with @level and @mask will be printed or not.
303  */
304 gboolean
305 mono_trace_is_traced (GLogLevelFlags level, MonoTraceMask mask)
306 {
307         return (level <= mono_internal_current_level && mask & mono_internal_current_mask);
308 }
309
310 /**
311  * log_level_get_name
312  * @log_level severity level
313  *
314  * Convert log level into a string for legacy log handlers
315  */
316 static const char *
317 log_level_get_name (GLogLevelFlags log_level)
318 {
319         switch (log_level & G_LOG_LEVEL_MASK) {
320         case G_LOG_LEVEL_ERROR: return "error";
321         case G_LOG_LEVEL_CRITICAL: return "critical";
322         case G_LOG_LEVEL_WARNING: return "warning";
323         case G_LOG_LEVEL_MESSAGE: return "message";
324         case G_LOG_LEVEL_INFO: return "info";
325         case G_LOG_LEVEL_DEBUG: return "debug";
326         default: return "unknown";
327         }
328 }
329
330 /**
331  * callback_adapter
332  * 
333  *  @log_domain Message prefix
334  *  @log_level Severity
335  *  @message Message to be written
336  *  @fatal Fatal flag - write then abort
337  *  @user_data Argument passed to @callback
338  *
339  * This adapts the old callback writer exposed by MonoCallback to the newer method of
340  * logging. We ignore the header request as legacy handlers never had headers.
341  */
342 static void
343 callback_adapter(const char *domain, GLogLevelFlags level, mono_bool fatal, const char *fmt, va_list args)
344 {
345         legacyLoggerUserData *ll = (legacyLoggerUserData *) logCallback.user_data;
346         const char *msg = g_strdup_vprintf (fmt, args);
347
348         ll->legacy_callback (domain, log_level_get_name(level), msg, fatal, ll->user_data);
349         g_free ((void *) msg);
350 }
351
352 /**
353  * legacy_opener
354  *
355  * Dummy routine for older style loggers
356  */
357 static void
358 legacy_opener(const char *path, void *user_data)
359 {
360   /* nothing to do */
361 }
362
363 /**
364  * legacy_closer
365  *
366  * Cleanup routine for older style loggers
367  */
368 static void
369 legacy_closer()
370 {
371         if (logCallback.user_data != NULL) {
372                 g_free (logCallback.user_data); /* This is a LegacyLoggerUserData struct */
373                 logCallback.opener = NULL;      
374                 logCallback.writer = NULL;
375                 logCallback.closer = NULL;
376                 logCallback.user_data = NULL;
377                 logCallback.header = FALSE;
378         }
379 }
380
381 /**
382  *   mono_trace_set_log_handler:
383  *  
384  *  @callback The callback that will replace the default logging handler
385  *  @user_data Argument passed to @callback
386  * 
387  * The log handler replaces the default runtime logger. All logging requests with be routed to it.
388  * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that
389  * execution will not resume after a fatal error. This is for "old-style" or legacy log handers.
390  */
391 void
392 mono_trace_set_log_handler (MonoLogCallback callback, void *user_data)
393 {
394         g_assert (callback);
395         if (logCallback.closer != NULL)
396                 logCallback.closer();
397         legacyLoggerUserData *ll = g_malloc (sizeof (legacyLoggerUserData));
398         ll->legacy_callback = callback;
399         ll->user_data = user_data;
400         logCallback.opener = legacy_opener;
401         logCallback.writer = callback_adapter;
402         logCallback.closer = legacy_closer;
403         logCallback.user_data = ll;
404         logCallback.dest = NULL;
405 }
406
407 /**
408  * mono_trace_set_log_handler_internal:
409  *
410  *  @callback The callback that will replace the default logging handler
411  *  @user_data Argument passed to @callback
412  *
413  * The log handler replaces the default runtime logger. All logging requests with be routed to it.
414  * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that
415  * execution will not resume after a fatal error.
416  */
417 void
418 mono_trace_set_log_handler_internal (MonoLogCallParm *callback, void *user_data)
419 {
420         g_assert (callback);
421         if (logCallback.closer != NULL)
422                 logCallback.closer();
423         logCallback.opener = callback->opener;
424         logCallback.writer = callback->writer;
425         logCallback.closer = callback->closer;
426         logCallback.header = mono_trace_log_header;
427         logCallback.dest   = callback->dest;
428         logCallback.opener(logCallback.dest, user_data);
429 }
430
431 static void
432 print_handler (const char *string)
433 {
434         print_callback (string, TRUE);
435 }
436
437 static void
438 printerr_handler (const char *string)
439 {
440         printerr_callback (string, FALSE);
441 }
442
443 /**
444  * mono_trace_set_print_handler:
445  *
446  * @callback The callback that will replace the default runtime behavior for stdout output.
447  *
448  * The print handler replaces the default runtime stdout output handler. This is used by free form output done by the runtime.
449  *
450  */
451 void
452 mono_trace_set_print_handler (MonoPrintCallback callback)
453 {
454         g_assert (callback);
455         print_callback = callback;
456         g_set_print_handler (print_handler);
457 }
458
459 /**
460  * mono_trace_set_printerr_handler:
461  *
462  * @callback The callback that will replace the default runtime behavior for stderr output.
463  *
464  * The print handler replaces the default runtime stderr output handler. This is used by free form output done by the runtime.
465  *
466  */
467 void
468 mono_trace_set_printerr_handler (MonoPrintCallback callback)
469 {
470         g_assert (callback);
471         printerr_callback = callback;
472         g_set_printerr_handler (printerr_handler);
473 }