rework logging system
reimplement the old mu-log.[ch] into mu-logging.{cc,hh}
If available (and using an appropriately equipped glib), log to the
systemd journal
Only g_criticals have stderr output, all the other g_* go to the log
file / journal.
This commit is contained in:
@ -257,7 +257,8 @@ on_run_maildir_dir (const char* fullpath, gboolean enter,
|
||||
data->_user_data);
|
||||
|
||||
if (err) {
|
||||
MU_WRITE_LOG ("%s: %s", __func__, err->message);
|
||||
g_warning("%s: error handling %s: %s", __func__,
|
||||
fullpath, err->message);
|
||||
g_clear_error(&err);
|
||||
}
|
||||
|
||||
|
||||
@ -119,10 +119,7 @@ mu_maildir_mkdir (const char* path, mode_t mode, gboolean noindex, GError **err)
|
||||
{
|
||||
g_return_val_if_fail (path, FALSE);
|
||||
|
||||
MU_WRITE_LOG ("%s (%s, %o, %s)", __func__,
|
||||
path, mode, noindex ? "TRUE" : "FALSE");
|
||||
|
||||
if (!create_maildir (path, mode, err))
|
||||
if (!create_maildir (path, mode, err))
|
||||
return FALSE;
|
||||
|
||||
if (noindex && !create_noindex (path, err))
|
||||
|
||||
@ -241,7 +241,7 @@ try_requery (MuQuery *self, const char* searchexpr, MuMsgFieldId sortfieldid,
|
||||
/* let's assume that infinite regression is
|
||||
* impossible */
|
||||
self->db().reopen();
|
||||
MU_WRITE_LOG ("reopening db after modification");
|
||||
g_message ("reopening db after modification");
|
||||
return mu_query_run (self, searchexpr, sortfieldid,
|
||||
maxnum, flags, err);
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
#include "mu-runtime.h"
|
||||
#include "utils/mu-util.h"
|
||||
#include "utils/mu-logger.hh"
|
||||
|
||||
#include <locale.h> /* for setlocale() */
|
||||
|
||||
@ -29,24 +30,25 @@ static std::unordered_map<MuRuntimePath, std::string> RuntimePaths;
|
||||
constexpr auto PartsDir = "parts";
|
||||
constexpr auto LogDir = "log";
|
||||
constexpr auto XapianDir = "xapian";
|
||||
constexpr auto Mu = "mu";
|
||||
constexpr auto MuName = "mu";
|
||||
constexpr auto Bookmarks = "bookmarks";
|
||||
|
||||
static const std::string Sepa{G_DIR_SEPARATOR_S};
|
||||
|
||||
|
||||
static void
|
||||
init_paths_xdg ()
|
||||
{
|
||||
RuntimePaths.emplace(MU_RUNTIME_PATH_XAPIANDB, g_get_user_cache_dir() +
|
||||
Sepa + Mu + Sepa + XapianDir);
|
||||
Sepa + MuName + Sepa + XapianDir);
|
||||
RuntimePaths.emplace(MU_RUNTIME_PATH_CACHE, g_get_user_cache_dir() +
|
||||
Sepa + Mu);
|
||||
Sepa + MuName);
|
||||
RuntimePaths.emplace(MU_RUNTIME_PATH_MIMECACHE, g_get_user_cache_dir() +
|
||||
Sepa + Mu + Sepa + PartsDir);
|
||||
Sepa + MuName + Sepa + PartsDir);
|
||||
RuntimePaths.emplace(MU_RUNTIME_PATH_LOGDIR, g_get_user_cache_dir() +
|
||||
Sepa + Mu);
|
||||
Sepa + MuName);
|
||||
RuntimePaths.emplace(MU_RUNTIME_PATH_BOOKMARKS, g_get_user_config_dir() +
|
||||
Sepa + Mu);
|
||||
Sepa + MuName);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -60,7 +62,7 @@ init_paths_muhome (const char *muhome)
|
||||
}
|
||||
|
||||
gboolean
|
||||
mu_runtime_init (const char* muhome, const char *name)
|
||||
mu_runtime_init (const char* muhome, const char *name, gboolean debug)
|
||||
{
|
||||
g_return_val_if_fail (RuntimePaths.empty(), FALSE);
|
||||
g_return_val_if_fail (name, FALSE);
|
||||
@ -92,10 +94,12 @@ mu_runtime_init (const char* muhome, const char *name)
|
||||
const auto log_path = RuntimePaths[MU_RUNTIME_PATH_LOGDIR] +
|
||||
Sepa + name + ".log";
|
||||
|
||||
if (!mu_log_init (log_path.c_str(), MU_LOG_OPTIONS_BACKUP)) {
|
||||
mu_runtime_uninit();
|
||||
return FALSE;
|
||||
}
|
||||
using namespace Mu;
|
||||
LogOptions opts{LogOptions::None};
|
||||
if (debug)
|
||||
opts |= (LogOptions::Debug | LogOptions::None);
|
||||
|
||||
Mu::log_init(log_path, opts);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -104,7 +108,7 @@ void
|
||||
mu_runtime_uninit (void)
|
||||
{
|
||||
RuntimePaths.clear();
|
||||
mu_log_uninit();
|
||||
Mu::log_uninit();
|
||||
}
|
||||
|
||||
const char*
|
||||
|
||||
@ -21,7 +21,6 @@
|
||||
#define __MU_RUNTIME_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <utils/mu-log.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@ -32,10 +31,12 @@ G_BEGIN_DECLS
|
||||
* @param muhome path where to find the mu home directory (typically, ~/.cache/mu)
|
||||
* @param name of the main program, ie. 'mu', 'mug' or
|
||||
* 'procmule'. this influences the name of the e.g. the logfile
|
||||
* @param debug debug-mode
|
||||
*
|
||||
* @return TRUE if succeeded, FALSE in case of error
|
||||
*/
|
||||
gboolean mu_runtime_init (const char *muhome, const char *name);
|
||||
gboolean mu_runtime_init (const char *muhome, const char *name,
|
||||
gboolean debug);
|
||||
|
||||
/**
|
||||
* free all resources
|
||||
@ -47,7 +48,7 @@ void mu_runtime_uninit (void);
|
||||
typedef enum {
|
||||
MU_RUNTIME_PATH_XAPIANDB, /* mu xapian db path */
|
||||
MU_RUNTIME_PATH_BOOKMARKS, /* mu bookmarks file path */
|
||||
MU_RUNTIME_PATH_CACHE, /* mu cache path for attachments etc. */
|
||||
MU_RUNTIME_PATH_CACHE, /* mu cache path */
|
||||
MU_RUNTIME_PATH_MIMECACHE, /* mu cache path for attachments etc. */
|
||||
MU_RUNTIME_PATH_LOGDIR, /* mu path for log files */
|
||||
|
||||
|
||||
@ -49,8 +49,8 @@ libmu_utils_la_SOURCES= \
|
||||
mu-date.c \
|
||||
mu-date.h \
|
||||
mu-error.hh \
|
||||
mu-log.c \
|
||||
mu-log.h \
|
||||
mu-logger.cc \
|
||||
mu-logger.hh \
|
||||
mu-command-parser.cc \
|
||||
mu-command-parser.hh \
|
||||
mu-readline.cc \
|
||||
|
||||
@ -1,319 +0,0 @@
|
||||
/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/
|
||||
|
||||
/*
|
||||
** Copyright (C) 2008-2016 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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 3 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.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include "mu-log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mu-util.h"
|
||||
|
||||
#define MU_MAX_LOG_FILE_SIZE 1000 * 1000 /* 1 MB (SI units) */
|
||||
#define MU_LOG_FILE "mu.log"
|
||||
|
||||
struct _MuLog {
|
||||
int _fd; /* log file descriptor */
|
||||
|
||||
MuLogOptions _opts;
|
||||
|
||||
gboolean _color_stdout; /* whether to use color */
|
||||
gboolean _color_stderr;
|
||||
|
||||
GLogFunc _old_log_func;
|
||||
};
|
||||
typedef struct _MuLog MuLog;
|
||||
|
||||
/* we use globals, because logging is a global operation as it
|
||||
* globally modifies the behaviour of g_warning and friends
|
||||
*/
|
||||
static MuLog* MU_LOG = NULL;
|
||||
static void log_write (const char* domain, GLogLevelFlags level,
|
||||
const gchar *msg);
|
||||
|
||||
static void
|
||||
try_close (int fd)
|
||||
{
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
if (close (fd) < 0)
|
||||
g_printerr ("%s: close() of fd %d failed: %s\n",
|
||||
__func__, fd, strerror(errno));
|
||||
}
|
||||
|
||||
static void
|
||||
silence (void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gboolean
|
||||
mu_log_init_silence (void)
|
||||
{
|
||||
g_return_val_if_fail (!MU_LOG, FALSE);
|
||||
|
||||
MU_LOG = g_new0 (MuLog, 1);
|
||||
MU_LOG->_fd = -1;
|
||||
|
||||
mu_log_options_set (MU_LOG_OPTIONS_NONE);
|
||||
|
||||
MU_LOG->_old_log_func =
|
||||
g_log_set_default_handler ((GLogFunc)silence, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
log_handler (const gchar* log_domain, GLogLevelFlags log_level,
|
||||
const gchar* msg)
|
||||
{
|
||||
if ((log_level & G_LOG_LEVEL_DEBUG) &&
|
||||
!(MU_LOG->_opts & MU_LOG_OPTIONS_DEBUG))
|
||||
return;
|
||||
|
||||
log_write (log_domain ? log_domain : "mu", log_level, msg);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mu_log_options_set (MuLogOptions opts)
|
||||
{
|
||||
g_return_if_fail (MU_LOG);
|
||||
|
||||
MU_LOG->_opts = opts;
|
||||
|
||||
/* when color is, only enable it when output is to a tty */
|
||||
if (MU_LOG->_opts & MU_LOG_OPTIONS_COLOR) {
|
||||
MU_LOG->_color_stdout = isatty(fileno(stdout));
|
||||
MU_LOG->_color_stderr = isatty(fileno(stderr));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MuLogOptions
|
||||
mu_log_options_get (void)
|
||||
{
|
||||
g_return_val_if_fail (MU_LOG, MU_LOG_OPTIONS_NONE);
|
||||
|
||||
return MU_LOG->_opts;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
move_log_file (const char *logfile)
|
||||
{
|
||||
gchar *logfile_old;
|
||||
int rv;
|
||||
|
||||
logfile_old = g_strdup_printf ("%s.old", logfile);
|
||||
rv = rename (logfile, logfile_old);
|
||||
g_free (logfile_old);
|
||||
|
||||
if (rv != 0) {
|
||||
g_warning ("failed to move %s to %s.old: %s",
|
||||
logfile, logfile, strerror(rv));
|
||||
return FALSE;
|
||||
} else
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
log_file_backup_maybe (const char *logfile)
|
||||
{
|
||||
struct stat statbuf;
|
||||
|
||||
if (stat (logfile, &statbuf) != 0) {
|
||||
if (errno == ENOENT)
|
||||
return TRUE; /* it did not exist yet, no problem */
|
||||
else {
|
||||
g_warning ("failed to stat(2) %s: %s",
|
||||
logfile, strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* log file is still below the max size? */
|
||||
if (statbuf.st_size <= MU_MAX_LOG_FILE_SIZE)
|
||||
return TRUE;
|
||||
|
||||
/* log file is too big!; we move it to <logfile>.old, overwriting */
|
||||
return move_log_file (logfile);
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
mu_log_init (const char* logfile, MuLogOptions opts)
|
||||
{
|
||||
int fd;
|
||||
|
||||
/* only init once... */
|
||||
g_return_val_if_fail (!MU_LOG, FALSE);
|
||||
g_return_val_if_fail (logfile, FALSE);
|
||||
|
||||
if (opts & MU_LOG_OPTIONS_BACKUP)
|
||||
if (!log_file_backup_maybe(logfile)) {
|
||||
g_warning ("failed to backup log file");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
fd = open (logfile, O_WRONLY|O_CREAT|O_APPEND, 00600);
|
||||
if (fd < 0) {
|
||||
g_warning ("%s: open() of '%s' failed: %s", __func__,
|
||||
logfile, strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
MU_LOG = g_new0 (MuLog, 1);
|
||||
MU_LOG->_fd = fd;
|
||||
|
||||
mu_log_options_set (opts);
|
||||
|
||||
MU_LOG->_old_log_func =
|
||||
g_log_set_default_handler ((GLogFunc)log_handler, NULL);
|
||||
|
||||
MU_WRITE_LOG ("logging started");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
mu_log_uninit (void)
|
||||
{
|
||||
if (!MU_LOG)
|
||||
return;
|
||||
|
||||
MU_WRITE_LOG ("logging stopped");
|
||||
|
||||
try_close (MU_LOG->_fd);
|
||||
g_free (MU_LOG);
|
||||
|
||||
MU_LOG = NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char*
|
||||
levelstr (GLogLevelFlags level)
|
||||
{
|
||||
switch (level) {
|
||||
case G_LOG_LEVEL_WARNING: return " [WARN] ";
|
||||
case G_LOG_LEVEL_ERROR : return " [ERR ] ";
|
||||
case G_LOG_LEVEL_DEBUG: return " [DBG ] ";
|
||||
case G_LOG_LEVEL_CRITICAL: return " [CRIT] ";
|
||||
case G_LOG_LEVEL_MESSAGE: return " [MSG ] ";
|
||||
case G_LOG_LEVEL_INFO : return " [INFO] ";
|
||||
default: return " [LOG ] ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define color_stdout_maybe(C) \
|
||||
do{if (MU_LOG->_color_stdout) fputs ((C),stdout);} while (0)
|
||||
#define color_stderr_maybe(C) \
|
||||
do{if (MU_LOG->_color_stderr) fputs ((C),stderr);} while (0)
|
||||
|
||||
|
||||
|
||||
static void
|
||||
log_write_fd (GLogLevelFlags level, const gchar *msg)
|
||||
{
|
||||
time_t now;
|
||||
char timebuf [22];
|
||||
const char *mylevel;
|
||||
|
||||
/* get the time/date string */
|
||||
now = time(NULL);
|
||||
strftime (timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S",
|
||||
localtime(&now));
|
||||
|
||||
if (write (MU_LOG->_fd, timebuf, strlen (timebuf)) < 0)
|
||||
goto err;
|
||||
|
||||
mylevel = levelstr (level);
|
||||
if (write (MU_LOG->_fd, mylevel, strlen (mylevel)) < 0)
|
||||
goto err;
|
||||
|
||||
if (write (MU_LOG->_fd, msg, strlen (msg)) < 0)
|
||||
goto err;
|
||||
|
||||
if (write (MU_LOG->_fd, "\n", strlen ("\n")) < 0)
|
||||
goto err;
|
||||
|
||||
return; /* all went well */
|
||||
|
||||
err:
|
||||
fprintf (stderr, "%s: failed to write to log: %s\n",
|
||||
__func__, strerror(errno));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
log_write_stdout_stderr (GLogLevelFlags level, const gchar *msg)
|
||||
{
|
||||
const char *mu;
|
||||
|
||||
mu = MU_LOG->_opts & MU_LOG_OPTIONS_NEWLINE ?
|
||||
"\nmu: " : "mu: ";
|
||||
|
||||
if (!(MU_LOG->_opts & MU_LOG_OPTIONS_QUIET) &&
|
||||
(level & G_LOG_LEVEL_MESSAGE)) {
|
||||
color_stdout_maybe (MU_COLOR_GREEN);
|
||||
fputs (mu, stdout);
|
||||
fputs (msg, stdout);
|
||||
fputs ("\n", stdout);
|
||||
color_stdout_maybe (MU_COLOR_DEFAULT);
|
||||
}
|
||||
|
||||
/* for errors, log them to stderr as well */
|
||||
if (level & G_LOG_LEVEL_ERROR ||
|
||||
level & G_LOG_LEVEL_CRITICAL ||
|
||||
level & G_LOG_LEVEL_WARNING) {
|
||||
color_stderr_maybe (MU_COLOR_RED);
|
||||
fputs (mu, stderr);
|
||||
fputs (msg, stderr);
|
||||
fputs ("\n", stderr);
|
||||
color_stderr_maybe (MU_COLOR_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
log_write (const char* domain, GLogLevelFlags level, const gchar *msg)
|
||||
{
|
||||
g_return_if_fail (MU_LOG);
|
||||
|
||||
log_write_fd (level, msg);
|
||||
log_write_stdout_stderr (level, msg);
|
||||
}
|
||||
@ -1,99 +0,0 @@
|
||||
/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset:
|
||||
8 -*-*/
|
||||
/*
|
||||
** Copyright (C) 2008-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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 3 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.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef __MU_LOG_H__
|
||||
#define __MU_LOG_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/* mu log is the global logging system */
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
enum _MuLogOptions {
|
||||
MU_LOG_OPTIONS_NONE = 0,
|
||||
|
||||
/* when size of log file > MU_MAX_LOG_FILE_SIZE, move the log
|
||||
* file to <log file>.old and start a new one. The .old file will
|
||||
* overwrite existing files of that name */
|
||||
MU_LOG_OPTIONS_BACKUP = 1 << 1,
|
||||
|
||||
/* quiet: don't log non-errors to stdout/stderr */
|
||||
MU_LOG_OPTIONS_QUIET = 1 << 2,
|
||||
|
||||
/* should lines be preceded by \n? useful when errors come
|
||||
* during indexing */
|
||||
MU_LOG_OPTIONS_NEWLINE = 1 << 3,
|
||||
|
||||
/* color in output (iff output is to a tty) */
|
||||
MU_LOG_OPTIONS_COLOR = 1 << 4,
|
||||
|
||||
/* log everything to stderr */
|
||||
MU_LOG_OPTIONS_STDERR = 1 << 5,
|
||||
|
||||
/* debug: debug include debug-level information */
|
||||
MU_LOG_OPTIONS_DEBUG = 1 << 6
|
||||
};
|
||||
typedef enum _MuLogOptions MuLogOptions;
|
||||
|
||||
|
||||
/**
|
||||
* write logging information to a log file
|
||||
*
|
||||
* @param full path to the log file (does not have to exist yet, but
|
||||
* it's directory must)
|
||||
* @param opts logging options
|
||||
*
|
||||
* @return TRUE if initialization succeeds, FALSE otherwise
|
||||
*/
|
||||
gboolean mu_log_init (const char *logfile, MuLogOptions opts)
|
||||
G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* be silent except for runtime errors, which will be written to
|
||||
* stderr.
|
||||
*
|
||||
* @return TRUE if initialization succeeds, FALSE otherwise
|
||||
*/
|
||||
gboolean mu_log_init_silence (void) G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* uninitialize the logging system, and free all resources
|
||||
*/
|
||||
void mu_log_uninit (void);
|
||||
|
||||
/**
|
||||
* set logging options, a logical-OR'd value of MuLogOptions
|
||||
*
|
||||
* @param opts the options (logically OR'd)
|
||||
*/
|
||||
void mu_log_options_set (MuLogOptions opts);
|
||||
|
||||
/**
|
||||
* get logging options, a logical-OR'd value of MuLogOptions
|
||||
*
|
||||
* @param opts the options (logically OR'd)
|
||||
*/
|
||||
MuLogOptions mu_log_options_get (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /*__MU_LOG_H__*/
|
||||
@ -309,20 +309,6 @@ typedef gpointer XapianEnquire;
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* log something in the log file; note, we use G_LOG_LEVEL_INFO
|
||||
* for such messages
|
||||
*/
|
||||
#define MU_WRITE_LOG(...) \
|
||||
G_STMT_START { \
|
||||
g_log (G_LOG_DOMAIN, \
|
||||
G_LOG_LEVEL_INFO, \
|
||||
__VA_ARGS__); \
|
||||
} G_STMT_END
|
||||
|
||||
|
||||
|
||||
#define MU_G_ERROR_CODE(GE) ((GE)&&(*(GE))?(*(GE))->code:MU_ERROR)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user