The log level of the libraries was not honoring the log level configured in MM, so we would see debug messages reported even if the default log level configured was INFO or MSG. The format of the logs emitted by the libraries was also not following the format of the rest of MM logs, e.g. they would not include timing info in the logs which would make it hard to follow certain event transitions. Make the libraries logging use the ModemManager logging method, to fix all those issues.
394 lines
10 KiB
C
394 lines
10 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details:
|
|
*
|
|
* Copyright (C) 2011-2020 Red Hat, Inc.
|
|
* Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es>
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <syslog.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <ModemManager.h>
|
|
#include <mm-errors-types.h>
|
|
|
|
#if defined WITH_QMI
|
|
#include <libqmi-glib.h>
|
|
#endif
|
|
|
|
#if defined WITH_MBIM
|
|
#include <libmbim-glib.h>
|
|
#endif
|
|
|
|
#if defined WITH_SYSTEMD_JOURNAL
|
|
#define SD_JOURNAL_SUPPRESS_LOCATION
|
|
#include <systemd/sd-journal.h>
|
|
#endif
|
|
|
|
#include "mm-log.h"
|
|
#include "mm-log-object.h"
|
|
|
|
enum {
|
|
TS_FLAG_NONE = 0,
|
|
TS_FLAG_WALL,
|
|
TS_FLAG_REL
|
|
};
|
|
|
|
static gboolean ts_flags = TS_FLAG_NONE;
|
|
static guint32 log_level = MM_LOG_LEVEL_INFO | MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR;
|
|
static GTimeVal rel_start = { 0, 0 };
|
|
static int logfd = -1;
|
|
static gboolean append_log_level_text = TRUE;
|
|
|
|
static void (*log_backend) (const char *loc,
|
|
const char *func,
|
|
int syslog_level,
|
|
const char *message,
|
|
size_t length);
|
|
|
|
typedef struct {
|
|
guint32 num;
|
|
const char *name;
|
|
} LogDesc;
|
|
|
|
static const LogDesc level_descs[] = {
|
|
{ MM_LOG_LEVEL_ERR, "ERR" },
|
|
{ MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR, "WARN" },
|
|
{ MM_LOG_LEVEL_INFO | MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR, "INFO" },
|
|
{ MM_LOG_LEVEL_DEBUG | MM_LOG_LEVEL_INFO | MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR, "DEBUG" },
|
|
};
|
|
|
|
static GString *msgbuf = NULL;
|
|
static gsize msgbuf_once = 0;
|
|
|
|
static int
|
|
mm_to_syslog_priority (MMLogLevel level)
|
|
{
|
|
switch (level) {
|
|
case MM_LOG_LEVEL_ERR:
|
|
return LOG_ERR;
|
|
case MM_LOG_LEVEL_WARN:
|
|
return LOG_WARNING;
|
|
case MM_LOG_LEVEL_INFO:
|
|
return LOG_INFO;
|
|
case MM_LOG_LEVEL_DEBUG:
|
|
return LOG_DEBUG;
|
|
default:
|
|
break;
|
|
}
|
|
g_assert_not_reached ();
|
|
return 0;
|
|
}
|
|
|
|
static MMLogLevel
|
|
glib_level_to_mm_level (GLogLevelFlags level)
|
|
{
|
|
/* if the log was flagged as fatal (e.g. G_DEBUG=fatal-warnings), ignore
|
|
* the fatal flag for logging purposes */
|
|
if (level & G_LOG_FLAG_FATAL)
|
|
level &= ~G_LOG_FLAG_FATAL;
|
|
|
|
switch (level) {
|
|
case G_LOG_LEVEL_ERROR:
|
|
case G_LOG_LEVEL_CRITICAL:
|
|
return MM_LOG_LEVEL_ERR;
|
|
case G_LOG_LEVEL_WARNING:
|
|
return MM_LOG_LEVEL_WARN;
|
|
case G_LOG_LEVEL_MESSAGE:
|
|
case G_LOG_LEVEL_INFO:
|
|
return MM_LOG_LEVEL_INFO;
|
|
case G_LOG_LEVEL_DEBUG:
|
|
return MM_LOG_LEVEL_DEBUG;
|
|
case G_LOG_LEVEL_MASK:
|
|
case G_LOG_FLAG_FATAL:
|
|
case G_LOG_FLAG_RECURSION:
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
log_level_description (MMLogLevel level)
|
|
{
|
|
switch (level) {
|
|
case MM_LOG_LEVEL_ERR:
|
|
return "<error>";
|
|
case MM_LOG_LEVEL_WARN:
|
|
return "<warn> ";
|
|
case MM_LOG_LEVEL_INFO:
|
|
return "<info> ";
|
|
case MM_LOG_LEVEL_DEBUG:
|
|
return "<debug>";
|
|
default:
|
|
break;
|
|
}
|
|
g_assert_not_reached ();
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
log_backend_file (const char *loc,
|
|
const char *func,
|
|
int syslog_level,
|
|
const char *message,
|
|
size_t length)
|
|
{
|
|
ssize_t ign;
|
|
|
|
ign = write (logfd, message, length);
|
|
if (ign) {} /* whatever; really shut up about unused result */
|
|
|
|
fsync (logfd); /* Make sure output is dumped to disk immediately */
|
|
}
|
|
|
|
static void
|
|
log_backend_syslog (const char *loc,
|
|
const char *func,
|
|
int syslog_level,
|
|
const char *message,
|
|
size_t length)
|
|
{
|
|
syslog (syslog_level, "%s", message);
|
|
}
|
|
|
|
#if defined WITH_SYSTEMD_JOURNAL
|
|
static void
|
|
log_backend_systemd_journal (const char *loc,
|
|
const char *func,
|
|
int syslog_level,
|
|
const char *message,
|
|
size_t length)
|
|
{
|
|
const char *line;
|
|
size_t file_length;
|
|
|
|
if (loc == NULL) {
|
|
sd_journal_send ("MESSAGE=%s", message,
|
|
"PRIORITY=%d", syslog_level,
|
|
NULL);
|
|
return;
|
|
}
|
|
|
|
line = strstr (loc, ":");
|
|
if (line) {
|
|
file_length = line - loc;
|
|
line++;
|
|
} else {
|
|
/* This is not supposed to happen but we must be prepared for this */
|
|
line = loc;
|
|
file_length = 0;
|
|
}
|
|
|
|
sd_journal_send ("MESSAGE=%s", message,
|
|
"PRIORITY=%d", syslog_level,
|
|
"CODE_FUNC=%s", func,
|
|
"CODE_FILE=%.*s", file_length, loc,
|
|
"CODE_LINE=%s", line,
|
|
NULL);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
_mm_log (gpointer obj,
|
|
const gchar *module,
|
|
const gchar *loc,
|
|
const gchar *func,
|
|
MMLogLevel level,
|
|
const gchar *fmt,
|
|
...)
|
|
{
|
|
va_list args;
|
|
GTimeVal tv;
|
|
|
|
if (!(log_level & level))
|
|
return;
|
|
|
|
if (g_once_init_enter (&msgbuf_once)) {
|
|
msgbuf = g_string_sized_new (512);
|
|
g_once_init_leave (&msgbuf_once, 1);
|
|
} else
|
|
g_string_truncate (msgbuf, 0);
|
|
|
|
if (append_log_level_text)
|
|
g_string_append_printf (msgbuf, "%s ", log_level_description (level));
|
|
|
|
if (ts_flags == TS_FLAG_WALL) {
|
|
g_get_current_time (&tv);
|
|
g_string_append_printf (msgbuf, "[%09ld.%06ld] ", tv.tv_sec, tv.tv_usec);
|
|
} else if (ts_flags == TS_FLAG_REL) {
|
|
glong secs;
|
|
glong usecs;
|
|
|
|
g_get_current_time (&tv);
|
|
secs = tv.tv_sec - rel_start.tv_sec;
|
|
usecs = tv.tv_usec - rel_start.tv_usec;
|
|
if (usecs < 0) {
|
|
secs--;
|
|
usecs += 1000000;
|
|
}
|
|
|
|
g_string_append_printf (msgbuf, "[%06ld.%06ld] ", secs, usecs);
|
|
}
|
|
|
|
#if defined MM_LOG_FUNC_LOC
|
|
if (loc && func)
|
|
g_string_append_printf (msgbuf, "[%s] %s(): ", loc, func);
|
|
#endif
|
|
|
|
if (obj)
|
|
g_string_append_printf (msgbuf, "[%s] ", mm_log_object_get_id (MM_LOG_OBJECT (obj)));
|
|
if (module)
|
|
g_string_append_printf (msgbuf, "(%s) ", module);
|
|
|
|
va_start (args, fmt);
|
|
g_string_append_vprintf (msgbuf, fmt, args);
|
|
va_end (args);
|
|
|
|
g_string_append_c (msgbuf, '\n');
|
|
|
|
log_backend (loc, func, mm_to_syslog_priority (level), msgbuf->str, msgbuf->len);
|
|
}
|
|
|
|
static void
|
|
log_handler (const gchar *log_domain,
|
|
GLogLevelFlags glib_level,
|
|
const gchar *message,
|
|
gpointer ignored)
|
|
{
|
|
_mm_log (NULL, /* obj */
|
|
NULL, /* module */
|
|
NULL, /* loc */
|
|
NULL, /* func */
|
|
glib_level_to_mm_level (glib_level),
|
|
"%s",
|
|
message);
|
|
}
|
|
|
|
gboolean
|
|
mm_log_set_level (const gchar *level,
|
|
GError **error)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (level_descs); i++) {
|
|
if (!g_ascii_strcasecmp (level_descs[i].name, level)) {
|
|
log_level = level_descs[i].num;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == G_N_ELEMENTS (level_descs)) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
|
"Unknown log level '%s'", level);
|
|
return FALSE;
|
|
}
|
|
|
|
#if defined WITH_QMI
|
|
qmi_utils_set_traces_enabled (log_level & MM_LOG_LEVEL_DEBUG ? TRUE : FALSE);
|
|
#endif
|
|
|
|
#if defined WITH_MBIM
|
|
mbim_utils_set_traces_enabled (log_level & MM_LOG_LEVEL_DEBUG ? TRUE : FALSE);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
mm_log_setup (const gchar *level,
|
|
const gchar *log_file,
|
|
gboolean log_journal,
|
|
gboolean show_timestamps,
|
|
gboolean rel_timestamps,
|
|
gboolean show_personal_info,
|
|
GError **error)
|
|
{
|
|
/* levels */
|
|
if (level && strlen (level) && !mm_log_set_level (level, error))
|
|
return FALSE;
|
|
|
|
if (show_timestamps)
|
|
ts_flags = TS_FLAG_WALL;
|
|
else if (rel_timestamps)
|
|
ts_flags = TS_FLAG_REL;
|
|
|
|
/* Grab start time for relative timestamps */
|
|
g_get_current_time (&rel_start);
|
|
|
|
#if defined WITH_SYSTEMD_JOURNAL
|
|
if (log_journal) {
|
|
log_backend = log_backend_systemd_journal;
|
|
append_log_level_text = FALSE;
|
|
} else
|
|
#endif
|
|
if (!log_file) {
|
|
openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PID | LOG_PERROR, LOG_DAEMON);
|
|
log_backend = log_backend_syslog;
|
|
} else {
|
|
logfd = open (log_file,
|
|
O_CREAT | O_APPEND | O_WRONLY,
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
|
if (logfd < 0) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't open log file: (%d) %s",
|
|
errno, strerror (errno));
|
|
return FALSE;
|
|
}
|
|
log_backend = log_backend_file;
|
|
}
|
|
|
|
g_log_set_handler (G_LOG_DOMAIN,
|
|
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
|
|
log_handler,
|
|
NULL);
|
|
|
|
#if defined WITH_QMI
|
|
g_log_set_handler ("Qmi",
|
|
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
|
|
log_handler,
|
|
NULL);
|
|
#endif
|
|
|
|
#if defined WITH_QRTR
|
|
g_log_set_handler ("Qrtr",
|
|
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
|
|
log_handler,
|
|
NULL);
|
|
#endif
|
|
|
|
#if defined WITH_MBIM
|
|
mbim_utils_set_show_personal_info (show_personal_info);
|
|
g_log_set_handler ("Mbim",
|
|
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
|
|
log_handler,
|
|
NULL);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
mm_log_shutdown (void)
|
|
{
|
|
if (logfd < 0)
|
|
closelog ();
|
|
else
|
|
close (logfd);
|
|
}
|