28 #include <glib/gstdio.h> 41 # warning "<unistd.h> required." 50 #define G_LOG_DOMAIN "qof.log" 54 #include <string_view> 59 #define QOF_LOG_MAX_CHARS 50 60 #define QOF_LOG_MAX_CHARS_WITH_ALLOWANCE 100 61 #define QOF_LOG_INDENT_WIDTH 4 64 static FILE *fout =
nullptr;
65 static gchar* function_buffer =
nullptr;
66 static gint qof_log_num_spaces = 0;
67 static GLogFunc previous_handler =
nullptr;
68 static gchar* qof_logger_format =
nullptr;
69 static QofLogModule log_module =
"qof";
71 using StrVec = std::vector<std::string>;
74 using ModuleEntryPtr = std::unique_ptr<ModuleEntry>;
75 using MEVec = std::vector<ModuleEntryPtr>;
77 static constexpr
int parts = 4;
78 static constexpr QofLogLevel default_level = QOF_LOG_WARNING;
79 static QofLogLevel current_max{default_level};
83 ModuleEntry(
const std::string& name, QofLogLevel level) :
84 m_name{name}, m_level{level} {
85 m_children.reserve(parts);
93 static ModuleEntryPtr _modules =
nullptr;
99 _modules = std::make_unique<ModuleEntry>(
"", default_level);
100 return _modules.get();
104 split_domain (
const std::string_view domain)
107 domain_parts.reserve(parts);
109 auto pos = domain.find(
".");
110 if (pos == std::string_view::npos)
112 domain_parts.emplace_back(domain);
116 while (pos != std::string_view::npos)
118 auto part_name{domain.substr(start, pos - start)};
119 domain_parts.emplace_back(part_name);
121 pos = domain.find(
".", start);
123 auto part_name{domain.substr(start, pos)};
124 domain_parts.emplace_back(part_name);
132 qof_log_num_spaces += QOF_LOG_INDENT_WIDTH;
139 = (qof_log_num_spaces < QOF_LOG_INDENT_WIDTH)
141 : qof_log_num_spaces - QOF_LOG_INDENT_WIDTH;
162 log4glib_handler(
const gchar *log_domain,
163 GLogLevelFlags log_level,
164 const gchar *message,
167 QofLogLevel level =
static_cast<QofLogLevel
>(log_level);
172 char timestamp_buf[10];
175 const char *format_24hour =
182 const char *level_str = qof_log_level_to_string(level);
187 fprintf(fout, qof_logger_format,
190 (log_domain ==
nullptr ?
"" : log_domain),
191 qof_log_num_spaces,
"",
193 (g_str_has_suffix(message,
"\n") ?
"" :
"\n"));
209 gboolean warn_about_missing_permission = FALSE;
210 auto modules = get_modules();
212 if (!qof_logger_format)
213 qof_logger_format = g_strdup (
"* %s %*s <%s> %*s%s%s");
220 if (fout !=
nullptr && fout != stderr && fout != stdout)
223 fname = g_strconcat(log_filename,
".XXXXXX.log",
nullptr);
225 if ((fd = g_mkstemp(fname)) != -1)
227 #if PLATFORM(WINDOWS) 230 fout = g_fopen(fname,
"wb");
233 g_assert(g_strcmp0(log_filename,
"/dev/null") != 0);
237 g_rename(fname, log_filename);
238 fout = fdopen(fd,
"w");
241 warn_about_missing_permission = TRUE;
245 warn_about_missing_permission = TRUE;
254 if (previous_handler ==
nullptr)
255 previous_handler = g_log_set_default_handler(log4glib_handler, modules);
257 if (warn_about_missing_permission)
259 g_critical(
"Cannot open log output file \"%s\", using stderr.", log_filename);
266 if (fout && fout != stderr && fout != stdout)
274 g_free(function_buffer);
275 function_buffer =
nullptr;
278 if (_modules !=
nullptr)
283 if (previous_handler !=
nullptr)
285 g_log_set_default_handler(previous_handler,
nullptr);
286 previous_handler =
nullptr;
293 if (!log_module || level == QOF_LOG_FATAL)
296 if (level > current_max)
299 auto module_parts = split_domain(log_module);
300 auto module = get_modules();
301 for (
auto part : module_parts)
303 auto iter = std::find_if(module->m_children.begin(),
304 module->m_children.end(),
306 return child && part == child->m_name;
308 if (iter == module->m_children.end())
310 auto child = std::make_unique<ModuleEntry>(part, default_level);
311 module->m_children.emplace_back(std::move(child));
312 module = module->m_children.back().get();
316 module = iter->get();
319 module->m_level = level;
327 if (level > current_max)
329 if (level <= default_level)
331 auto module = get_modules();
333 if (level <= module->m_level)
339 auto domain_vec = split_domain(domain);
341 for (
const auto& part : domain_vec)
343 auto iter = std::find_if(module->m_children.begin(),
344 module->m_children.end(),
345 [part](
auto& child) {
346 return child && part == child->m_name; });
348 if (iter == module->m_children.end())
351 if (level <= (*iter)->m_level)
354 module = iter->get();
362 gchar *p, *buffer, *begin;
374 buffer = g_strndup(name, QOF_LOG_MAX_CHARS_WITH_ALLOWANCE - 1);
375 length = strlen(buffer);
376 p = g_strstr_len (buffer, length,
"(");
378 begin = g_strrstr (buffer,
"*");
379 if (begin ==
nullptr)
380 begin = g_strrstr (buffer,
" ");
381 else if (* (begin + 1) ==
' ')
383 if (begin !=
nullptr)
389 g_free(function_buffer);
390 function_buffer = g_strdup(p);
392 return function_buffer;
398 if (g_ascii_strcasecmp(
"stderr", log_to_filename) == 0)
403 else if (g_ascii_strcasecmp(
"stdout", log_to_filename) == 0)
417 const gchar *levels_group =
"levels", *output_group =
"output";
418 GError *err =
nullptr;
419 GKeyFile *conf = g_key_file_new();
421 if (!g_key_file_load_from_file(conf, filename, G_KEY_FILE_NONE, &err))
423 g_warning(
"unable to parse [%s]: %s", filename, err->message);
428 DEBUG(
"parsing log config from [%s]", filename);
429 if (g_key_file_has_group(conf, levels_group))
432 unsigned int key_idx;
434 gint logger_max_name_length = 12;
435 gchar *str =
nullptr;
437 levels = g_key_file_get_keys(conf, levels_group, &num_levels,
nullptr);
439 for (key_idx = 0; key_idx < num_levels && levels[key_idx] !=
nullptr; key_idx++)
442 gchar *logger_name =
nullptr, *level_str =
nullptr;
444 logger_name = g_strdup(levels[key_idx]);
445 logger_max_name_length = MAX (logger_max_name_length, (gint) strlen (logger_name));
446 level_str = g_key_file_get_string(conf, levels_group, logger_name,
nullptr);
447 level = qof_log_level_from_string(level_str);
449 DEBUG(
"setting log [%s] to level [%s=%d]", logger_name, level_str, level);
456 str = g_strdup_printf (
"%d", logger_max_name_length);
457 if (qof_logger_format)
458 g_free (qof_logger_format);
459 qof_logger_format = g_strconcat (
"* %s %*s <%-", str,
".", str,
"s> %*s%s%s",
nullptr);
465 if (g_key_file_has_group(conf, output_group))
468 unsigned int output_idx;
471 outputs = g_key_file_get_keys(conf, output_group, &num_outputs,
nullptr);
472 for (output_idx = 0; output_idx < num_outputs && outputs[output_idx] !=
nullptr; output_idx++)
474 gchar *key = outputs[output_idx];
477 if (g_ascii_strcasecmp(
"to", key) != 0)
479 g_warning(
"unknown key [%s] in [outputs], skipping", key);
483 value = g_key_file_get_string(conf, output_group, key,
nullptr);
484 DEBUG(
"setting [output].to=[%s]", value);
491 g_key_file_free(conf);
495 qof_log_level_to_string(QofLogLevel log_level)
497 const char *level_str;
506 case QOF_LOG_WARNING:
509 case QOF_LOG_MESSAGE:
526 qof_log_level_from_string(
const gchar *str)
528 if (g_ascii_strncasecmp(
"error", str, 5) == 0)
return QOF_LOG_FATAL;
529 if (g_ascii_strncasecmp(
"crit", str, 4) == 0)
return QOF_LOG_ERROR;
530 if (g_ascii_strncasecmp(
"warn", str, 4) == 0)
return QOF_LOG_WARNING;
531 if (g_ascii_strncasecmp(
"mess", str, 4) == 0)
return QOF_LOG_MESSAGE;
532 if (g_ascii_strncasecmp(
"info", str, 4) == 0)
return QOF_LOG_INFO;
533 if (g_ascii_strncasecmp(
"debug", str, 5) == 0)
return QOF_LOG_DEBUG;
534 return QOF_LOG_DEBUG;
gsize qof_strftime(gchar *buf, gsize max, const gchar *format, const struct tm *tm)
qof_strftime calls qof_format_time to print a given time and afterwards tries to put the result into ...
void qof_log_set_level(QofLogModule log_module, QofLogLevel level)
Set the logging level of the given log_module.
void qof_log_dedent(void)
De-dent one level, capped at 0; see LEAVE macro.
#define DEBUG(format, args...)
Print a debugging message.
void qof_log_init(void)
Initialize the error logging subsystem.
void qof_log_shutdown(void)
Be nice, close the logfile if possible.
struct tm * gnc_localtime_r(const time64 *secs, struct tm *time)
fill out a time struct from a 64-bit time value adjusted for the current time zone.
void qof_log_init_filename_special(const char *log_to_filename)
If log_to_filename is "stderr" or "stdout" (exactly, case-insensitive), then those special files are ...
gboolean qof_log_check(QofLogModule domain, QofLogLevel level)
Check to see if the given log_module is configured to log at the given log_level. ...
const gchar * qof_log_prettify(const gchar *name)
Cleans up subroutine names.
void qof_log_init_filename(const gchar *log_filename)
Specify a filename for log output.
void qof_log_indent(void)
Indents one level; see ENTER macro.
time64 gnc_time(time64 *tbuf)
get the current time
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
void qof_log_parse_log_config(const char *filename)
Parse a log-configuration file.
void qof_log_set_file(FILE *outfile)
Specify an alternate log output, to pipe or file.