Merge pull request #4621 from alexanderkyte/strdup_env
[mono.git] / mono / utils / mono-logger.c
index 6ed2db36482450d63fb6bd23f463efc6e5909032..aeaa3f97ceefab0bc79fe685d042ade9a91d5368 100644 (file)
@@ -1,3 +1,7 @@
+/**
+ * \file
+ */
+
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -11,19 +15,26 @@ typedef struct {
        MonoTraceMask   mask;
 } MonoLogLevelEntry;
 
-GLogLevelFlags mono_internal_current_level             = INT_MAX;
-MonoTraceMask  mono_internal_current_mask              = MONO_TRACE_ALL;
+GLogLevelFlags mono_internal_current_level     = INT_MAX;
+MonoTraceMask  mono_internal_current_mask      = MONO_TRACE_ALL;
+gboolean mono_trace_log_header                 = FALSE;
 
 static GQueue          *level_stack            = NULL;
 static const char      *mono_log_domain        = "Mono";
 static MonoPrintCallback print_callback, printerr_callback;
 
-static MonoLogCallback logCallback = {
+static MonoLogCallParm logCallback = {
        .opener = NULL,
        .writer = NULL,
-       .closer = NULL
+       .closer = NULL,
+       .header = FALSE
 };
 
+typedef struct {
+   MonoLogCallback legacy_callback;
+   gpointer user_data;
+} UserSuppliedLoggerUserData;
+
 /**
  * mono_trace_init:
  *
@@ -36,9 +47,20 @@ mono_trace_init (void)
                mono_internal_current_level = G_LOG_LEVEL_ERROR;
                level_stack = g_queue_new();
 
-               mono_trace_set_mask_string(g_getenv("MONO_LOG_MASK"));
-               mono_trace_set_level_string(g_getenv("MONO_LOG_LEVEL"));
-               mono_trace_set_logdest_string(g_getenv("MONO_LOG_DEST"));
+               char *mask = g_getenv ("MONO_LOG_MASK");
+               char *level = g_getenv ("MONO_LOG_LEVEL");
+               char *header = g_getenv ("MONO_LOG_HEADER");
+               char *dest = g_getenv ("MONO_LOG_DEST");
+
+               mono_trace_set_mask_string(mask);
+               mono_trace_set_level_string(level);
+               mono_trace_set_logheader_string(header);
+               mono_trace_set_logdest_string(dest);
+
+               g_free (mask);
+               g_free (level);
+               g_free (header);
+               g_free (dest);
        }
 }
 
@@ -55,45 +77,42 @@ mono_trace_cleanup (void)
                        g_free (g_queue_pop_head (level_stack));
                }
 
+               logCallback.closer();
                g_queue_free (level_stack);
                level_stack = NULL;
        }
 }
 
 /**
- * mono_tracev:
- *
- *     @level: Verbose level of the specified message
- *     @mask: Type of the specified message
- *
+ * mono_tracev_inner:
+ * \param level Verbose level of the specified message
+ * \param mask Type of the specified message
  * Traces a new message, depending on the current logging level
  * and trace mask.
  */
 void 
 mono_tracev_inner (GLogLevelFlags level, MonoTraceMask mask, const char *format, va_list args)
 {
+       char *log_message;
        if (level_stack == NULL) {
                mono_trace_init ();
                if(level > mono_internal_current_level || !(mask & mono_internal_current_mask))
                        return;
        }
 
-       if (logCallback.opener == NULL) {
-                logCallback.opener = mono_log_open_logfile;
-                logCallback.writer = mono_log_write_logfile;
-                logCallback.closer = mono_log_close_logfile;
-               logCallback.opener(NULL, NULL);
-       }
-       logCallback.writer(mono_log_domain, level, format, args);
+       g_assert (logCallback.opener); // mono_trace_init should have provided us with one!
+
+       if (g_vasprintf (&log_message, format, args) < 0)
+               return;
+       logCallback.writer (mono_log_domain, level, logCallback.header, log_message);
+       g_free (log_message);
 }
 
 /**
  * mono_trace_set_level:
- *
- *     @level: Verbose level to set
- *
+ * \param level Verbose level to set
  * Sets the current logging level. Every subsequent call to
- * mono_trace will check the visibility of a message against this
+ * \c mono_trace will check the visibility of a message against this
  * value.
  */
 void 
@@ -107,11 +126,9 @@ mono_trace_set_level (GLogLevelFlags level)
 
 /**
  * mono_trace_set_mask:
- *
- *     @mask: Mask of visible message types.
- *
+ * \param mask Mask of visible message types.
  * Sets the current logging level. Every subsequent call to
- * mono_trace will check the visibility of a message against this
+ * \c mono_trace will check the visibility of a message against this
  * value.
  */
 void 
@@ -120,45 +137,69 @@ mono_trace_set_mask (MonoTraceMask mask)
        if(level_stack == NULL)
                mono_trace_init();
 
-       mono_internal_current_mask      = mask;
+       mono_internal_current_mask = mask;
 }
 
 /**
  * mono_trace_set_logdest:
- *
- *     @dest: Destination for logging
- *
+ * \param dest Destination for logging
  * Sets the current logging destination. This can be a file or, if supported,
  * syslog.
  */
 void 
 mono_trace_set_logdest_string (const char *dest)
 {
-       MonoLogCallback logger;
+       MonoLogCallParm logger;
 
        if(level_stack == NULL)
                mono_trace_init();
 
+#if PLATFORM_ANDROID
+       logger.opener = mono_log_open_logcat;
+       logger.writer = mono_log_write_logcat;
+       logger.closer = mono_log_close_logcat;
+       logger.dest   = (char*) dest;
+#elif defined (HOST_IOS)
+       logger.opener = mono_log_open_asl;
+       logger.writer = mono_log_write_asl;
+       logger.closer = mono_log_close_asl;
+       logger.dest   = (char*) dest;
+#else
        if ((dest == NULL) || (strcmp("syslog", dest) != 0)) {
                logger.opener = mono_log_open_logfile;
                logger.writer = mono_log_write_logfile;
                logger.closer = mono_log_close_logfile;
-               mono_trace_set_log_handler(&logger, dest, NULL);
+               logger.dest   = (char *) dest;
        } else {
                logger.opener = mono_log_open_syslog;
                logger.writer = mono_log_write_syslog;
                logger.closer = mono_log_close_syslog;
-               mono_trace_set_log_handler(&logger, mono_log_domain, NULL);
+               logger.dest   = (char *) dest;
+       }
+#endif
+       mono_trace_set_log_handler_internal(&logger, NULL);
+}
+
+/**
+ * mono_trace_set_logheader:
+ * \param head Whether we want pid/date/time header on log messages
+ * Sets the current logging header option.
+ */
+void 
+mono_trace_set_logheader_string(const char *head)
+{
+       if (head == NULL) {
+               mono_trace_log_header = FALSE;
+       } else {
+               mono_trace_log_header = TRUE;
        }
 }
 
 /**
  * mono_trace_push:
- *
- *     @level: Verbose level to set
- *     @mask: Mask of visible message types.
- *
- * Saves the current values of level and mask then calls mono_trace_set
+ * \param level Verbose level to set
+ * \param mask Mask of visible message types.
+ * Saves the current values of level and mask then calls \c mono_trace_set
  * with the specified new values.
  */
 void 
@@ -235,10 +276,11 @@ mono_trace_set_mask_string (const char *value)
        const char *tok;
        guint32 flags = 0;
 
-       const char *valid_flags[] = {"asm", "type", "dll", "gc", "cfg", "aot", "security", "threadpool", "io-threadpool", "io-layer", "all", NULL};
+       const char *valid_flags[] = {"asm", "type", "dll", "gc", "cfg", "aot", "security", "threadpool", "io-threadpool", "io-layer", "w32handle", "all", NULL};
        const MonoTraceMask     valid_masks[] = {MONO_TRACE_ASSEMBLY, MONO_TRACE_TYPE, MONO_TRACE_DLLIMPORT,
                                                 MONO_TRACE_GC, MONO_TRACE_CONFIG, MONO_TRACE_AOT, MONO_TRACE_SECURITY,
-                                                MONO_TRACE_THREADPOOL, MONO_TRACE_IO_THREADPOOL, MONO_TRACE_IO_LAYER, MONO_TRACE_ALL };
+                                                MONO_TRACE_THREADPOOL, MONO_TRACE_IO_THREADPOOL, MONO_TRACE_IO_LAYER,
+                                                MONO_TRACE_W32HANDLE, MONO_TRACE_ALL };
 
        if(!value)
                return;
@@ -251,7 +293,7 @@ mono_trace_set_mask_string (const char *value)
                        continue;
                }
                for (i = 0; valid_flags[i]; i++) {
-                       int len = strlen (valid_flags[i]);
+                       size_t len = strlen (valid_flags[i]);
                        if (strncmp (tok, valid_flags[i], len) == 0 && (tok[len] == 0 || tok[len] == ',')) {
                                flags |= valid_masks[i];
                                tok += len;
@@ -279,23 +321,139 @@ mono_trace_is_traced (GLogLevelFlags level, MonoTraceMask mask)
 }
 
 /**
- * mono_trace_set_log_handler:
+ * log_level_get_name
+ * @log_level severity level
  *
- *  @callback The callback that will replace the default logging handler
+ * Convert log level into a string for legacy log handlers
+ */
+static const char *
+log_level_get_name (GLogLevelFlags log_level)
+{
+        switch (log_level & G_LOG_LEVEL_MASK) {
+        case G_LOG_LEVEL_ERROR: return "error";
+        case G_LOG_LEVEL_CRITICAL: return "critical";
+        case G_LOG_LEVEL_WARNING: return "warning";
+        case G_LOG_LEVEL_MESSAGE: return "message";
+        case G_LOG_LEVEL_INFO: return "info";
+        case G_LOG_LEVEL_DEBUG: return "debug";
+        default: return "unknown";
+        }
+}
+
+/**
+ * callback_adapter
+ * 
+ *  @log_domain Message prefix
+ *  @log_level Severity
+ *  @message Message to be written
+ *  @fatal Fatal flag - write then abort
  *  @user_data Argument passed to @callback
  *
+ * This adapts the old callback writer exposed by MonoCallback to the newer method of
+ * logging. We ignore the header request as legacy handlers never had headers.
+ */
+static void
+callback_adapter (const char *domain, GLogLevelFlags level, mono_bool fatal, const char *message)
+{
+       UserSuppliedLoggerUserData *ll =logCallback.user_data;
+
+       ll->legacy_callback (domain, log_level_get_name(level), message, fatal, ll->user_data);
+}
+
+static void
+eglib_log_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
+{
+       UserSuppliedLoggerUserData *ll = logCallback.user_data;
+
+       ll->legacy_callback (log_domain, log_level_get_name (log_level), message, log_level & G_LOG_LEVEL_ERROR, ll->user_data);
+}
+
+/**
+ * legacy_opener
+ *
+ * Dummy routine for older style loggers
+ */
+static void
+legacy_opener(const char *path, void *user_data)
+{
+  /* nothing to do */
+}
+
+/**
+ * legacy_closer
+ *
+ * Cleanup routine for older style loggers
+ */
+static void
+legacy_closer(void)
+{
+       if (logCallback.user_data != NULL) {
+               g_free (logCallback.user_data); /* This is a UserSuppliedLoggerUserData struct */
+               logCallback.opener = NULL;      
+               logCallback.writer = NULL;
+               logCallback.closer = NULL;
+               logCallback.user_data = NULL;
+               logCallback.header = FALSE;
+       }
+}
+
+/**
+ *   mono_trace_set_log_handler:
+ *  
+ *  @callback The callback that will replace the default logging handler
+ *  @user_data Argument passed to @callback
+ * 
+ * The log handler replaces the default runtime logger. All logging requests with be routed to it.
+ * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that
+ * execution will not resume after a fatal error.
+ */
+void
+mono_trace_set_log_handler (MonoLogCallback callback, void *user_data)
+{
+       g_assert (callback);
+
+       if (logCallback.closer != NULL)
+               logCallback.closer();
+       UserSuppliedLoggerUserData *ll = g_malloc (sizeof (UserSuppliedLoggerUserData));
+       ll->legacy_callback = callback;
+       ll->user_data = user_data;
+       logCallback.opener = legacy_opener;
+       logCallback.writer = callback_adapter;
+       logCallback.closer = legacy_closer;
+       logCallback.user_data = ll;
+       logCallback.dest = NULL;
+
+       g_log_set_default_handler (eglib_log_adapter, user_data);
+}
+
+static void
+structured_log_adapter (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
+{
+       logCallback.writer (log_domain, log_level, logCallback.header, message);
+}
+
+/**
+ * mono_trace_set_log_handler_internal:
+ * \param callback The callback that will replace the default logging handler
+ * \param user_data Argument passed to \p callback
  * The log handler replaces the default runtime logger. All logging requests with be routed to it.
  * If the fatal argument in the callback is true, the callback must abort the current process. The runtime expects that
  * execution will not resume after a fatal error.
  */
 void
-mono_trace_set_log_handler (MonoLogCallback *callback, const char *dest, void *user_data)
+mono_trace_set_log_handler_internal (MonoLogCallParm *callback, void *user_data)
 {
        g_assert (callback);
+       if (logCallback.closer != NULL)
+               logCallback.closer();
        logCallback.opener = callback->opener;
        logCallback.writer = callback->writer;
        logCallback.closer = callback->closer;
-       logCallback.opener(dest, user_data);
+       logCallback.header = mono_trace_log_header;
+       logCallback.dest   = callback->dest;
+       logCallback.opener (logCallback.dest, user_data);
+
+       g_log_set_default_handler (structured_log_adapter, user_data);
 }
 
 static void
@@ -312,11 +470,8 @@ printerr_handler (const char *string)
 
 /**
  * mono_trace_set_print_handler:
- *
- * @callback The callback that will replace the default runtime behavior for stdout output.
- *
+ * \param callback The callback that will replace the default runtime behavior for stdout output.
  * The print handler replaces the default runtime stdout output handler. This is used by free form output done by the runtime.
- *
  */
 void
 mono_trace_set_print_handler (MonoPrintCallback callback)
@@ -328,11 +483,8 @@ mono_trace_set_print_handler (MonoPrintCallback callback)
 
 /**
  * mono_trace_set_printerr_handler:
- *
- * @callback The callback that will replace the default runtime behavior for stderr output.
- *
+ * \param callback The callback that will replace the default runtime behavior for stderr output.
  * The print handler replaces the default runtime stderr output handler. This is used by free form output done by the runtime.
- *
  */
 void
 mono_trace_set_printerr_handler (MonoPrintCallback callback)