+/**
+ * \file
+ */
+
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
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:
*
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);
}
}
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
/**
* 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
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
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;
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;
}
/**
- * 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
/**
* 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)
/**
* 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)