2021-06-06 15:43:27 +00:00
|
|
|
/*
|
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
|
|
* Copyright 2021 Gerald Combs
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "wslog.h"
|
|
|
|
|
2021-06-08 01:46:52 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2021-06-14 17:41:34 +00:00
|
|
|
#include <errno.h>
|
2021-06-08 01:46:52 +00:00
|
|
|
#include <time.h>
|
2021-06-06 15:43:27 +00:00
|
|
|
#include <stdarg.h>
|
2021-06-12 11:41:50 +00:00
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
2021-06-16 00:07:37 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <process.h>
|
|
|
|
#endif
|
2021-06-08 01:46:52 +00:00
|
|
|
#include <ws_attributes.h>
|
|
|
|
|
|
|
|
#include <wsutil/ws_assert.h>
|
2021-06-14 17:41:34 +00:00
|
|
|
#include <wsutil/file_util.h>
|
2021-06-06 15:43:27 +00:00
|
|
|
|
|
|
|
|
2021-06-16 02:50:45 +00:00
|
|
|
/* Runtime log level. */
|
2021-06-12 01:27:43 +00:00
|
|
|
#define ENV_VAR_LEVEL "WIRESHARK_LOG_LEVEL"
|
2021-06-16 02:50:45 +00:00
|
|
|
|
|
|
|
/* Log domains enabled/disabled. */
|
2021-06-12 01:27:43 +00:00
|
|
|
#define ENV_VAR_DOMAINS "WIRESHARK_LOG_DOMAINS"
|
2021-06-08 01:46:52 +00:00
|
|
|
|
2021-06-16 02:50:45 +00:00
|
|
|
/* Log level that generates a trap and aborts. Can be "critical"
|
|
|
|
* or "warning". */
|
|
|
|
#define ENV_VAR_FATAL "WIRESHARK_LOG_FATAL"
|
|
|
|
|
2021-06-16 03:20:15 +00:00
|
|
|
/* Domains that will produce debug output, regardless of log level or
|
|
|
|
* domain filter. */
|
|
|
|
#define ENV_VAR_DEBUG "WIRESHARK_LOG_DEBUG"
|
|
|
|
|
2021-06-16 05:54:23 +00:00
|
|
|
/* Domains that will produce noisy output, regardless of log level or
|
|
|
|
* domain filter. */
|
|
|
|
#define ENV_VAR_NOISY "WIRESHARK_LOG_NOISY"
|
|
|
|
|
2021-06-12 01:27:43 +00:00
|
|
|
#define DEFAULT_LOG_LEVEL LOG_LEVEL_MESSAGE
|
2021-06-08 01:46:52 +00:00
|
|
|
|
2021-06-19 18:44:58 +00:00
|
|
|
#define DEFAULT_PROGNAME "PID"
|
2021-06-19 00:57:09 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
#define DOMAIN_UNDEFED(domain) ((domain) == NULL || *(domain) == '\0')
|
|
|
|
#define DOMAIN_DEFINED(domain) (!DOMAIN_UNDEFED(domain))
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note: I didn't measure it but I assume using a string array is faster than
|
|
|
|
* a GHashTable for small number N of domains.
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
char **domainv;
|
|
|
|
gboolean positive; /* positive or negative match */
|
2021-06-19 04:30:29 +00:00
|
|
|
enum ws_log_level min_level; /* for level filters */
|
2021-06-19 21:03:31 +00:00
|
|
|
} log_filter_t;
|
2021-06-17 02:58:12 +00:00
|
|
|
|
2021-06-08 01:46:52 +00:00
|
|
|
|
2021-06-12 01:27:43 +00:00
|
|
|
static enum ws_log_level current_log_level = LOG_LEVEL_NONE;
|
2021-06-08 01:46:52 +00:00
|
|
|
|
2021-06-12 21:29:22 +00:00
|
|
|
static gboolean color_enabled = FALSE;
|
|
|
|
|
2021-06-19 18:44:58 +00:00
|
|
|
static const char *registered_progname = DEFAULT_PROGNAME;
|
2021-06-12 11:41:50 +00:00
|
|
|
|
2021-06-15 23:43:25 +00:00
|
|
|
/* List of domains to filter. */
|
2021-06-19 21:03:31 +00:00
|
|
|
static log_filter_t *domain_filter = NULL;
|
2021-06-11 12:39:16 +00:00
|
|
|
|
2021-06-16 03:20:15 +00:00
|
|
|
/* List of domains to output debug level unconditionally. */
|
2021-06-19 21:03:31 +00:00
|
|
|
static log_filter_t *debug_filter = NULL;
|
2021-06-16 03:20:15 +00:00
|
|
|
|
2021-06-16 05:54:23 +00:00
|
|
|
/* List of domains to output noisy level unconditionally. */
|
2021-06-19 21:03:31 +00:00
|
|
|
static log_filter_t *noisy_filter = NULL;
|
2021-06-16 19:32:07 +00:00
|
|
|
|
2021-06-10 21:13:11 +00:00
|
|
|
static ws_log_writer_cb *registered_log_writer = NULL;
|
2021-06-08 01:46:52 +00:00
|
|
|
|
2021-06-10 21:13:11 +00:00
|
|
|
static void *registered_log_writer_data = NULL;
|
2021-06-08 01:46:52 +00:00
|
|
|
|
2021-06-10 21:13:11 +00:00
|
|
|
static ws_log_writer_free_data_cb *registered_log_writer_data_free = NULL;
|
2021-06-08 01:46:52 +00:00
|
|
|
|
|
|
|
static FILE *custom_log = NULL;
|
|
|
|
|
2021-06-16 02:50:45 +00:00
|
|
|
static enum ws_log_level fatal_log_level = LOG_LEVEL_ERROR;
|
|
|
|
|
2021-06-19 18:44:58 +00:00
|
|
|
#ifndef WS_DISABLE_DEBUG
|
|
|
|
static gboolean init_complete = FALSE;
|
|
|
|
#endif
|
|
|
|
|
2021-06-08 01:46:52 +00:00
|
|
|
|
|
|
|
static void ws_log_cleanup(void);
|
|
|
|
|
|
|
|
|
|
|
|
const char *ws_log_level_to_string(enum ws_log_level level)
|
|
|
|
{
|
|
|
|
switch (level) {
|
|
|
|
case LOG_LEVEL_NONE:
|
2021-06-19 00:41:22 +00:00
|
|
|
return "(zero)";
|
2021-06-08 01:46:52 +00:00
|
|
|
case LOG_LEVEL_ERROR:
|
|
|
|
return "ERROR";
|
|
|
|
case LOG_LEVEL_CRITICAL:
|
|
|
|
return "CRITICAL";
|
|
|
|
case LOG_LEVEL_WARNING:
|
|
|
|
return "WARNING";
|
|
|
|
case LOG_LEVEL_MESSAGE:
|
|
|
|
return "MESSAGE";
|
|
|
|
case LOG_LEVEL_INFO:
|
|
|
|
return "INFO";
|
|
|
|
case LOG_LEVEL_DEBUG:
|
|
|
|
return "DEBUG";
|
2021-06-16 05:54:23 +00:00
|
|
|
case LOG_LEVEL_NOISY:
|
|
|
|
return "NOISY";
|
2021-06-08 01:46:52 +00:00
|
|
|
default:
|
|
|
|
return "(BOGUS LOG LEVEL)";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-16 02:50:45 +00:00
|
|
|
static enum ws_log_level string_to_log_level(const char *str_level)
|
|
|
|
{
|
|
|
|
if (!str_level)
|
|
|
|
return LOG_LEVEL_NONE;
|
|
|
|
|
2021-06-16 05:54:23 +00:00
|
|
|
if (g_ascii_strcasecmp(str_level, "noisy") == 0)
|
|
|
|
return LOG_LEVEL_NOISY;
|
|
|
|
else if (g_ascii_strcasecmp(str_level, "debug") == 0)
|
2021-06-16 02:50:45 +00:00
|
|
|
return LOG_LEVEL_DEBUG;
|
|
|
|
else if (g_ascii_strcasecmp(str_level, "info") == 0)
|
|
|
|
return LOG_LEVEL_INFO;
|
|
|
|
else if (g_ascii_strcasecmp(str_level, "message") == 0)
|
|
|
|
return LOG_LEVEL_MESSAGE;
|
|
|
|
else if (g_ascii_strcasecmp(str_level, "warning") == 0)
|
|
|
|
return LOG_LEVEL_WARNING;
|
|
|
|
else if (g_ascii_strcasecmp(str_level, "critical") == 0)
|
|
|
|
return LOG_LEVEL_CRITICAL;
|
|
|
|
else if (g_ascii_strcasecmp(str_level, "error") == 0)
|
|
|
|
return LOG_LEVEL_ERROR;
|
|
|
|
else
|
|
|
|
return LOG_LEVEL_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-19 00:41:22 +00:00
|
|
|
WS_RETNONNULL
|
|
|
|
static inline const char *domain_to_string(const char *domain)
|
|
|
|
{
|
|
|
|
return (domain == NULL) ? "(none)" : domain;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
static inline gboolean filter_contains(log_filter_t *filter, const char *domain)
|
2021-06-16 03:20:15 +00:00
|
|
|
{
|
2021-06-19 21:03:31 +00:00
|
|
|
ws_assert(filter);
|
|
|
|
ws_assert(domain);
|
|
|
|
char **domv;
|
2021-06-17 02:58:12 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
for (domv = filter->domainv; *domv != NULL; domv++) {
|
|
|
|
if (g_ascii_strcasecmp(*domv, domain) == 0) {
|
2021-06-16 03:20:15 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
static gboolean level_filter_matches(log_filter_t *filter,
|
|
|
|
const char *domain,
|
2021-06-16 19:32:07 +00:00
|
|
|
enum ws_log_level level,
|
2021-06-19 21:03:31 +00:00
|
|
|
gboolean *active)
|
2021-06-08 01:46:52 +00:00
|
|
|
{
|
2021-06-19 21:03:31 +00:00
|
|
|
ws_assert(filter);
|
2021-06-19 04:30:29 +00:00
|
|
|
ws_assert(filter->min_level != LOG_LEVEL_NONE);
|
2021-06-19 21:03:31 +00:00
|
|
|
ws_assert(domain != NULL);
|
|
|
|
ws_assert(level != LOG_LEVEL_NONE);
|
|
|
|
ws_assert(active != NULL);
|
2021-06-16 05:54:23 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
if (filter_contains(filter, domain) == FALSE)
|
|
|
|
return FALSE;
|
2021-06-12 01:27:43 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
if (filter->positive) {
|
2021-06-19 04:30:29 +00:00
|
|
|
*active = level >= filter->min_level;
|
2021-06-19 21:03:31 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
2021-06-16 19:32:07 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
/* negative match */
|
2021-06-19 04:30:29 +00:00
|
|
|
if (level <= filter->min_level) {
|
2021-06-19 21:03:31 +00:00
|
|
|
*active = FALSE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
2021-06-16 19:32:07 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2021-06-16 19:32:07 +00:00
|
|
|
|
|
|
|
|
2021-06-19 21:26:58 +00:00
|
|
|
gboolean ws_log_msg_is_active(const char *domain, enum ws_log_level level)
|
2021-06-16 19:32:07 +00:00
|
|
|
{
|
2021-06-19 02:26:48 +00:00
|
|
|
/*
|
2021-06-19 04:30:29 +00:00
|
|
|
* Higher numerical levels have higher priority. Critical and above
|
2021-06-19 02:26:48 +00:00
|
|
|
* are always enabled.
|
|
|
|
*/
|
2021-06-19 04:30:29 +00:00
|
|
|
if (level >= LOG_LEVEL_CRITICAL)
|
2021-06-19 02:26:48 +00:00
|
|
|
return TRUE;
|
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
/*
|
|
|
|
* The debug/noisy filter overrides the other parameters.
|
|
|
|
*/
|
|
|
|
if (DOMAIN_DEFINED(domain)) {
|
|
|
|
gboolean active;
|
|
|
|
if (noisy_filter && level_filter_matches(noisy_filter, domain, level, &active))
|
|
|
|
return active;
|
|
|
|
if (debug_filter && level_filter_matches(debug_filter, domain, level, &active))
|
|
|
|
return active;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the priority is lower than the current minimum drop the
|
|
|
|
* message.
|
|
|
|
*/
|
2021-06-19 04:30:29 +00:00
|
|
|
if (level < current_log_level)
|
2021-06-19 21:03:31 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we don't have domain filtering enabled we are done.
|
|
|
|
*/
|
|
|
|
if (domain_filter == NULL)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have a filter but we don't use it with the undefined domain,
|
|
|
|
* pretty much every permanent call to ws_log should be using a
|
|
|
|
* chosen domain.
|
|
|
|
*/
|
|
|
|
if (DOMAIN_UNDEFED(domain))
|
|
|
|
return TRUE;
|
2021-06-16 19:32:07 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
/* Check if the domain filter matches. */
|
|
|
|
if (filter_contains(domain_filter, domain))
|
|
|
|
return domain_filter->positive;
|
2021-06-16 19:32:07 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
/* We have a domain filter but it didn't match. */
|
|
|
|
return !domain_filter->positive;
|
2021-06-08 01:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum ws_log_level ws_log_get_level(void)
|
|
|
|
{
|
|
|
|
return current_log_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum ws_log_level ws_log_set_level(enum ws_log_level log_level)
|
|
|
|
{
|
|
|
|
ws_assert(log_level > LOG_LEVEL_NONE && log_level < _LOG_LEVEL_LAST);
|
|
|
|
|
|
|
|
current_log_level = log_level;
|
|
|
|
return current_log_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum ws_log_level ws_log_set_level_str(const char *str_level)
|
|
|
|
{
|
|
|
|
enum ws_log_level level;
|
|
|
|
|
2021-06-16 02:50:45 +00:00
|
|
|
level = string_to_log_level(str_level);
|
|
|
|
if (level == LOG_LEVEL_NONE)
|
2021-06-08 01:46:52 +00:00
|
|
|
return LOG_LEVEL_NONE;
|
|
|
|
|
|
|
|
current_log_level = level;
|
|
|
|
return current_log_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-14 15:51:19 +00:00
|
|
|
static const char *opt_level = "--log-level";
|
|
|
|
static const char *opt_domains = "--log-domains";
|
2021-06-14 17:41:34 +00:00
|
|
|
static const char *opt_file = "--log-file";
|
2021-06-16 02:50:45 +00:00
|
|
|
static const char *opt_fatal = "--log-fatal";
|
2021-06-16 03:20:15 +00:00
|
|
|
static const char *opt_debug = "--log-debug";
|
2021-06-16 05:54:23 +00:00
|
|
|
static const char *opt_noisy = "--log-noisy";
|
2021-06-08 01:46:52 +00:00
|
|
|
|
2021-06-12 01:27:43 +00:00
|
|
|
|
2021-06-19 18:44:58 +00:00
|
|
|
static void print_err(void (*log_args_print_err)(const char *, va_list ap),
|
|
|
|
int log_args_exit_failure,
|
|
|
|
const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (log_args_print_err == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
log_args_print_err(fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
if (log_args_exit_failure >= 0)
|
|
|
|
exit(log_args_exit_failure);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int ws_log_parse_args(int *argc_ptr, char *argv[],
|
|
|
|
void (*vcmdarg_err)(const char *, va_list ap),
|
|
|
|
int exit_failure)
|
2021-06-08 01:46:52 +00:00
|
|
|
{
|
2021-06-14 15:51:19 +00:00
|
|
|
char **ptr = argv;
|
|
|
|
int count = *argc_ptr;
|
|
|
|
int ret = 0;
|
|
|
|
size_t optlen;
|
|
|
|
const char *option, *value;
|
|
|
|
int prune_extra;
|
|
|
|
|
|
|
|
while (*ptr != NULL) {
|
|
|
|
if (g_str_has_prefix(*ptr, opt_level)) {
|
|
|
|
option = opt_level;
|
|
|
|
optlen = strlen(opt_level);
|
|
|
|
}
|
|
|
|
else if (g_str_has_prefix(*ptr, opt_domains)) {
|
|
|
|
option = opt_domains;
|
|
|
|
optlen = strlen(opt_domains);
|
|
|
|
}
|
2021-06-14 17:41:34 +00:00
|
|
|
else if (g_str_has_prefix(*ptr, opt_file)) {
|
|
|
|
option = opt_file;
|
|
|
|
optlen = strlen(opt_file);
|
|
|
|
}
|
2021-06-16 02:50:45 +00:00
|
|
|
else if (g_str_has_prefix(*ptr, opt_fatal)) {
|
|
|
|
option = opt_fatal;
|
|
|
|
optlen = strlen(opt_fatal);
|
|
|
|
}
|
2021-06-16 03:20:15 +00:00
|
|
|
else if (g_str_has_prefix(*ptr, opt_debug)) {
|
|
|
|
option = opt_debug;
|
|
|
|
optlen = strlen(opt_debug);
|
|
|
|
}
|
2021-06-16 05:54:23 +00:00
|
|
|
else if (g_str_has_prefix(*ptr, opt_noisy)) {
|
|
|
|
option = opt_noisy;
|
|
|
|
optlen = strlen(opt_noisy);
|
|
|
|
}
|
2021-06-14 15:51:19 +00:00
|
|
|
else {
|
|
|
|
ptr += 1;
|
|
|
|
count -= 1;
|
|
|
|
continue;
|
2021-06-08 01:46:52 +00:00
|
|
|
}
|
|
|
|
|
2021-06-14 15:51:19 +00:00
|
|
|
value = *ptr + optlen;
|
|
|
|
/* Two possibilities:
|
|
|
|
* --<option> <value>
|
|
|
|
* or
|
|
|
|
* --<option>=<value>
|
|
|
|
*/
|
|
|
|
if (value[0] == '\0') {
|
|
|
|
/* value is separated with blank space */
|
|
|
|
value = *(ptr + 1);
|
|
|
|
prune_extra = 1;
|
|
|
|
|
|
|
|
if (value == NULL || !*value || *value == '-') {
|
2021-06-14 18:10:57 +00:00
|
|
|
/* If the option value after the blank starts with '-' assume
|
|
|
|
* it is another option. */
|
2021-06-19 18:44:58 +00:00
|
|
|
print_err(vcmdarg_err, exit_failure,
|
|
|
|
"Option \"%s\" requires a value.\n", *ptr);
|
2021-06-14 15:51:19 +00:00
|
|
|
option = NULL;
|
|
|
|
prune_extra = 0;
|
2021-06-14 18:10:57 +00:00
|
|
|
ret += 1;
|
2021-06-14 15:51:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (value[0] == '=') {
|
|
|
|
/* value is after equals */
|
|
|
|
value += 1;
|
|
|
|
prune_extra = 0;
|
|
|
|
}
|
|
|
|
else {
|
2021-06-14 18:10:57 +00:00
|
|
|
/* Option isn't known. */
|
2021-06-14 15:51:19 +00:00
|
|
|
ptr += 1;
|
|
|
|
count -= 1;
|
|
|
|
continue;
|
|
|
|
}
|
2021-06-11 12:39:16 +00:00
|
|
|
|
2021-06-14 15:51:19 +00:00
|
|
|
if (option == opt_level) {
|
|
|
|
if (ws_log_set_level_str(value) == LOG_LEVEL_NONE) {
|
2021-06-19 18:44:58 +00:00
|
|
|
print_err(vcmdarg_err, exit_failure,
|
|
|
|
"Invalid log level \"%s\".\n", value);
|
2021-06-14 15:51:19 +00:00
|
|
|
ret += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (option == opt_domains) {
|
2021-06-16 05:54:23 +00:00
|
|
|
ws_log_set_domain_filter(value);
|
2021-06-14 15:51:19 +00:00
|
|
|
}
|
2021-06-14 17:41:34 +00:00
|
|
|
else if (option == opt_file) {
|
|
|
|
FILE *fp = ws_fopen(value, "w");
|
|
|
|
if (fp == NULL) {
|
2021-06-19 18:44:58 +00:00
|
|
|
print_err(vcmdarg_err, exit_failure,
|
|
|
|
"Error opening file '%s' for writing: %s.\n",
|
|
|
|
value, g_strerror(errno));
|
2021-06-14 17:41:34 +00:00
|
|
|
ret += 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ws_log_add_custom_file(fp);
|
|
|
|
}
|
|
|
|
}
|
2021-06-16 02:50:45 +00:00
|
|
|
else if (option == opt_fatal) {
|
|
|
|
if (ws_log_set_fatal_str(value) == LOG_LEVEL_NONE) {
|
2021-06-19 18:44:58 +00:00
|
|
|
print_err(vcmdarg_err, exit_failure,
|
|
|
|
"Fatal log level must be \"critical\" or "
|
|
|
|
"\"warning\", not \"%s\".\n", value);
|
2021-06-16 02:50:45 +00:00
|
|
|
ret += 1;
|
|
|
|
}
|
|
|
|
}
|
2021-06-16 03:20:15 +00:00
|
|
|
else if (option == opt_debug) {
|
2021-06-16 05:54:23 +00:00
|
|
|
ws_log_set_debug_filter(value);
|
|
|
|
}
|
|
|
|
else if (option == opt_noisy) {
|
|
|
|
ws_log_set_noisy_filter(value);
|
2021-06-16 03:20:15 +00:00
|
|
|
}
|
2021-06-14 18:10:57 +00:00
|
|
|
else {
|
|
|
|
/* Option value missing or invalid, do nothing. */
|
|
|
|
}
|
2021-06-11 12:39:16 +00:00
|
|
|
|
2021-06-14 15:51:19 +00:00
|
|
|
/*
|
|
|
|
* We found a log option. We will remove it from
|
|
|
|
* the argv by moving up the other strings in the array. This is
|
|
|
|
* so that it doesn't generate an unrecognized option
|
|
|
|
* error further along in the initialization process.
|
|
|
|
*/
|
|
|
|
/* Include the terminating NULL in the memmove. */
|
|
|
|
memmove(ptr, ptr + 1 + prune_extra, (count - prune_extra) * sizeof(*ptr));
|
|
|
|
/* No need to increment ptr here. */
|
|
|
|
count -= (1 + prune_extra);
|
|
|
|
*argc_ptr -= (1 + prune_extra);
|
|
|
|
}
|
2021-06-11 12:39:16 +00:00
|
|
|
|
2021-06-14 15:51:19 +00:00
|
|
|
return ret;
|
2021-06-11 12:39:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
static void free_log_filter(log_filter_t **filter_ptr)
|
2021-06-11 12:39:16 +00:00
|
|
|
{
|
2021-06-19 21:03:31 +00:00
|
|
|
ws_assert(filter_ptr);
|
|
|
|
if (*filter_ptr == NULL)
|
|
|
|
return;
|
|
|
|
g_strfreev((*filter_ptr)->domainv);
|
|
|
|
g_free(*filter_ptr);
|
|
|
|
*filter_ptr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void tokenize_filter_str(log_filter_t **filter_ptr, const char *str_filter,
|
2021-06-19 04:30:29 +00:00
|
|
|
enum ws_log_level min_level)
|
2021-06-19 21:03:31 +00:00
|
|
|
{
|
|
|
|
char *tok, *str;
|
2021-06-11 12:39:16 +00:00
|
|
|
const char *sep = ",;";
|
2021-06-19 21:03:31 +00:00
|
|
|
GPtrArray *ptr;
|
2021-06-16 19:32:07 +00:00
|
|
|
gboolean negated = FALSE;
|
2021-06-19 21:03:31 +00:00
|
|
|
log_filter_t *filter;
|
2021-06-11 12:39:16 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
ws_assert(filter_ptr);
|
|
|
|
ws_assert(*filter_ptr == NULL);
|
2021-06-11 12:39:16 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
if (str_filter == NULL)
|
|
|
|
return;
|
2021-06-15 23:43:25 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
if (str_filter[0] == '!') {
|
2021-06-16 19:32:07 +00:00
|
|
|
negated = TRUE;
|
2021-06-19 21:03:31 +00:00
|
|
|
str_filter += 1;
|
2021-06-15 23:43:25 +00:00
|
|
|
}
|
2021-06-19 21:03:31 +00:00
|
|
|
if (*str_filter == '\0')
|
|
|
|
return;
|
|
|
|
|
|
|
|
ptr = g_ptr_array_new_with_free_func(g_free);
|
|
|
|
str = g_strdup(str_filter);
|
2021-06-11 12:39:16 +00:00
|
|
|
|
|
|
|
for (tok = strtok(str, sep); tok != NULL; tok = strtok(NULL, sep)) {
|
2021-06-19 21:03:31 +00:00
|
|
|
g_ptr_array_add(ptr, g_strdup(tok));
|
2021-06-11 12:39:16 +00:00
|
|
|
}
|
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
g_free(str);
|
|
|
|
if (ptr->len == 0) {
|
|
|
|
g_ptr_array_free(ptr, TRUE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
g_ptr_array_add(ptr, NULL);
|
2021-06-16 03:20:15 +00:00
|
|
|
|
2021-06-19 21:03:31 +00:00
|
|
|
filter = g_new(log_filter_t, 1);
|
|
|
|
filter->domainv = (void *)g_ptr_array_free(ptr, FALSE);
|
|
|
|
filter->positive = !negated;
|
2021-06-19 04:30:29 +00:00
|
|
|
filter->min_level = min_level;
|
2021-06-19 21:03:31 +00:00
|
|
|
*filter_ptr = filter;
|
2021-06-16 03:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-16 05:54:23 +00:00
|
|
|
void ws_log_set_domain_filter(const char *str_filter)
|
2021-06-16 03:20:15 +00:00
|
|
|
{
|
2021-06-19 21:03:31 +00:00
|
|
|
free_log_filter(&domain_filter);
|
|
|
|
tokenize_filter_str(&domain_filter, str_filter, LOG_LEVEL_NONE);
|
2021-06-16 03:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-16 05:54:23 +00:00
|
|
|
void ws_log_set_debug_filter(const char *str_filter)
|
2021-06-16 03:20:15 +00:00
|
|
|
{
|
2021-06-19 21:03:31 +00:00
|
|
|
free_log_filter(&debug_filter);
|
|
|
|
tokenize_filter_str(&debug_filter, str_filter, LOG_LEVEL_DEBUG);
|
2021-06-11 12:39:16 +00:00
|
|
|
}
|
|
|
|
|
2021-06-12 01:27:43 +00:00
|
|
|
|
2021-06-16 05:54:23 +00:00
|
|
|
void ws_log_set_noisy_filter(const char *str_filter)
|
|
|
|
{
|
2021-06-19 21:03:31 +00:00
|
|
|
free_log_filter(&noisy_filter);
|
|
|
|
tokenize_filter_str(&noisy_filter, str_filter, LOG_LEVEL_NOISY);
|
2021-06-16 05:54:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-16 02:50:45 +00:00
|
|
|
enum ws_log_level ws_log_set_fatal(enum ws_log_level log_level)
|
|
|
|
{
|
|
|
|
ws_assert(log_level > LOG_LEVEL_NONE);
|
|
|
|
|
2021-06-19 04:30:29 +00:00
|
|
|
/* Not possible to set lower priority than "warning" to fatal. */
|
|
|
|
if (log_level < LOG_LEVEL_WARNING)
|
2021-06-16 02:50:45 +00:00
|
|
|
return LOG_LEVEL_NONE;
|
|
|
|
|
|
|
|
fatal_log_level = log_level;
|
|
|
|
return fatal_log_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum ws_log_level ws_log_set_fatal_str(const char *str_level)
|
|
|
|
{
|
|
|
|
enum ws_log_level level = string_to_log_level(str_level);
|
|
|
|
return ws_log_set_fatal(level);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-19 18:44:58 +00:00
|
|
|
void ws_log_init(const char *progname, ws_log_writer_cb *writer)
|
2021-06-08 01:46:52 +00:00
|
|
|
{
|
2021-06-12 21:29:22 +00:00
|
|
|
const char *env;
|
|
|
|
|
2021-06-19 18:44:58 +00:00
|
|
|
if (progname != NULL) {
|
|
|
|
registered_progname = progname;
|
|
|
|
g_set_prgname(progname);
|
|
|
|
}
|
2021-06-12 11:41:50 +00:00
|
|
|
|
2021-06-14 18:53:59 +00:00
|
|
|
if (writer)
|
|
|
|
registered_log_writer = writer;
|
2021-06-08 01:46:52 +00:00
|
|
|
|
2021-06-14 21:29:15 +00:00
|
|
|
#if GLIB_CHECK_VERSION(2,50,0)
|
|
|
|
color_enabled = g_log_writer_supports_color(ws_fileno(stderr));
|
|
|
|
#elif !defined(_WIN32)
|
|
|
|
/* We assume every non-Windows console supports color. */
|
|
|
|
color_enabled = (ws_isatty(ws_fileno(stderr)) == 1);
|
|
|
|
#else
|
|
|
|
/* Our Windows build version of GLib is pretty recent, we are probably
|
|
|
|
* fine here, unless we want to do better than GLib. */
|
|
|
|
color_enabled = FALSE;
|
2021-06-12 21:29:22 +00:00
|
|
|
#endif
|
2021-06-11 12:39:16 +00:00
|
|
|
|
2021-06-12 01:27:43 +00:00
|
|
|
current_log_level = DEFAULT_LOG_LEVEL;
|
|
|
|
|
|
|
|
env = g_getenv(ENV_VAR_LEVEL);
|
2021-06-16 02:50:45 +00:00
|
|
|
if (env != NULL && ws_log_set_level_str(env) == LOG_LEVEL_NONE)
|
|
|
|
fprintf(stderr, "Ignoring invalid environment value %s=\"%s\".\n", ENV_VAR_LEVEL, env);
|
2021-06-08 01:46:52 +00:00
|
|
|
|
2021-06-12 01:27:43 +00:00
|
|
|
env = g_getenv(ENV_VAR_DOMAINS);
|
|
|
|
if (env != NULL)
|
2021-06-16 05:54:23 +00:00
|
|
|
ws_log_set_domain_filter(env);
|
2021-06-11 12:39:16 +00:00
|
|
|
|
2021-06-16 02:50:45 +00:00
|
|
|
env = g_getenv(ENV_VAR_FATAL);
|
|
|
|
if (env != NULL && ws_log_set_fatal_str(env) == LOG_LEVEL_NONE)
|
|
|
|
fprintf(stderr, "Ignoring invalid environment value %s=\"%s\".\n", ENV_VAR_FATAL, env);
|
|
|
|
|
2021-06-16 03:20:15 +00:00
|
|
|
env = g_getenv(ENV_VAR_DEBUG);
|
|
|
|
if (env != NULL)
|
2021-06-16 05:54:23 +00:00
|
|
|
ws_log_set_debug_filter(env);
|
|
|
|
|
|
|
|
env = g_getenv(ENV_VAR_NOISY);
|
|
|
|
if (env != NULL)
|
|
|
|
ws_log_set_noisy_filter(env);
|
2021-06-16 03:20:15 +00:00
|
|
|
|
2021-06-08 01:46:52 +00:00
|
|
|
atexit(ws_log_cleanup);
|
2021-06-19 18:44:58 +00:00
|
|
|
|
|
|
|
#ifndef WS_DISABLE_DEBUG
|
|
|
|
init_complete = TRUE;
|
|
|
|
#endif
|
2021-06-08 01:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-19 18:44:58 +00:00
|
|
|
void ws_log_init_with_data(const char *progname, ws_log_writer_cb *writer,
|
|
|
|
void *user_data,
|
|
|
|
ws_log_writer_free_data_cb *free_user_data)
|
2021-06-08 01:46:52 +00:00
|
|
|
{
|
2021-06-10 21:13:11 +00:00
|
|
|
registered_log_writer_data = user_data;
|
|
|
|
registered_log_writer_data_free = free_user_data;
|
2021-06-19 18:44:58 +00:00
|
|
|
ws_log_init(progname, writer);
|
2021-06-08 01:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-20 19:29:46 +00:00
|
|
|
#define MAGENTA "\033[35m"
|
|
|
|
#define BLUE "\033[34m"
|
|
|
|
#define CYAN "\033[36m"
|
|
|
|
#define GREEN "\033[32m"
|
|
|
|
#define YELLOW "\033[33m"
|
|
|
|
#define RED "\033[31m"
|
|
|
|
#define RESET "\033[0m"
|
|
|
|
|
|
|
|
static inline const char *msg_color_on(gboolean enable, enum ws_log_level level)
|
2021-06-14 21:29:15 +00:00
|
|
|
{
|
2021-06-20 19:29:46 +00:00
|
|
|
if (!enable)
|
|
|
|
return "";
|
|
|
|
|
|
|
|
if (level <= LOG_LEVEL_NOISY)
|
|
|
|
return MAGENTA;
|
|
|
|
else if (level <= LOG_LEVEL_DEBUG)
|
|
|
|
return GREEN;
|
|
|
|
else if (level <= LOG_LEVEL_INFO)
|
|
|
|
return CYAN;
|
|
|
|
else if (level <= LOG_LEVEL_MESSAGE)
|
|
|
|
return BLUE;
|
|
|
|
else if (level <= LOG_LEVEL_WARNING)
|
|
|
|
return YELLOW;
|
|
|
|
else if (level <= LOG_LEVEL_ERROR)
|
|
|
|
return RED;
|
|
|
|
else
|
|
|
|
ws_assert_not_reached();
|
2021-06-14 21:29:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline const char *color_off(gboolean enable)
|
|
|
|
{
|
2021-06-20 19:29:46 +00:00
|
|
|
return enable ? RESET : "";
|
2021-06-14 21:29:15 +00:00
|
|
|
}
|
|
|
|
|
2021-06-12 21:29:22 +00:00
|
|
|
static void log_write_do_work(FILE *fp, gboolean use_color, const char *timestamp,
|
2021-06-11 12:39:16 +00:00
|
|
|
const char *domain, enum ws_log_level level,
|
2021-06-14 18:53:59 +00:00
|
|
|
const char *file, int line, const char *func,
|
|
|
|
const char *user_format, va_list user_ap)
|
2021-06-10 21:13:11 +00:00
|
|
|
{
|
2021-06-19 00:41:22 +00:00
|
|
|
const char *domain_str = domain_to_string(domain);
|
2021-06-14 14:02:23 +00:00
|
|
|
const char *level_str = ws_log_level_to_string(level);
|
2021-06-19 00:57:09 +00:00
|
|
|
gboolean doextra = (level != DEFAULT_LOG_LEVEL);
|
2021-06-14 14:02:23 +00:00
|
|
|
|
2021-06-19 18:44:58 +00:00
|
|
|
#ifndef WS_DISABLE_DEBUG
|
|
|
|
if (!init_complete) {
|
|
|
|
fprintf(fp, " ** (noinit)");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-06-20 19:29:46 +00:00
|
|
|
/* Process name */
|
|
|
|
fprintf(fp, " ** (%s:%ld) ", registered_progname, (long)getpid());
|
2021-06-10 21:13:11 +00:00
|
|
|
|
2021-06-20 19:29:46 +00:00
|
|
|
/* Timestamp */
|
|
|
|
if (timestamp != NULL) {
|
2021-06-16 00:07:37 +00:00
|
|
|
fputs(timestamp, fp);
|
|
|
|
fputc(' ', fp);
|
|
|
|
}
|
2021-06-12 21:29:22 +00:00
|
|
|
|
2021-06-20 19:29:46 +00:00
|
|
|
/* Message priority (domain/level) */
|
|
|
|
fprintf(fp, "[%s%s%s %s] ", msg_color_on(use_color, level),
|
|
|
|
level_str,
|
|
|
|
color_off(use_color),
|
|
|
|
domain_str);
|
2021-06-10 21:13:11 +00:00
|
|
|
|
2021-06-20 19:29:46 +00:00
|
|
|
/* File/line */
|
|
|
|
if (doextra && file != NULL && line >= 0)
|
|
|
|
fprintf(fp, "%s:%d ", file, line);
|
|
|
|
else if (doextra && file != NULL)
|
|
|
|
fprintf(fp, "%s ", file);
|
|
|
|
|
|
|
|
fputs("-- ", fp);
|
|
|
|
|
|
|
|
/* Function name */
|
|
|
|
if (doextra && func != NULL)
|
|
|
|
fprintf(fp, "%s(): ", func);
|
2021-06-14 21:04:02 +00:00
|
|
|
|
2021-06-20 19:29:46 +00:00
|
|
|
/* User message */
|
2021-06-14 18:53:59 +00:00
|
|
|
vfprintf(fp, user_format, user_ap);
|
|
|
|
fputc('\n', fp);
|
|
|
|
fflush(fp);
|
2021-06-10 21:13:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-14 18:53:59 +00:00
|
|
|
static void log_write_dispatch(const char *domain, enum ws_log_level level,
|
2021-06-10 21:13:11 +00:00
|
|
|
const char *file, int line, const char *func,
|
|
|
|
const char *user_format, va_list user_ap)
|
|
|
|
{
|
2021-06-15 02:41:42 +00:00
|
|
|
GDateTime *now;
|
|
|
|
char *tstamp = NULL;
|
2021-06-10 21:13:11 +00:00
|
|
|
|
2021-06-15 02:41:42 +00:00
|
|
|
now = g_date_time_new_now_local();
|
|
|
|
if (now) {
|
|
|
|
tstamp = g_date_time_format(now, "%H:%M:%S.%f");
|
|
|
|
g_date_time_unref(now);
|
|
|
|
}
|
2021-06-10 21:13:11 +00:00
|
|
|
|
|
|
|
if (registered_log_writer) {
|
2021-06-14 18:53:59 +00:00
|
|
|
registered_log_writer(domain, level, tstamp, file, line, func,
|
|
|
|
user_format, user_ap, registered_log_writer_data);
|
2021-06-10 21:13:11 +00:00
|
|
|
}
|
|
|
|
else {
|
2021-06-12 21:29:22 +00:00
|
|
|
log_write_do_work(stderr, color_enabled, tstamp, domain, level, file, line, func,
|
2021-06-14 18:53:59 +00:00
|
|
|
user_format, user_ap);
|
2021-06-10 21:13:11 +00:00
|
|
|
}
|
2021-06-08 01:46:52 +00:00
|
|
|
|
|
|
|
if (custom_log) {
|
2021-06-12 21:29:22 +00:00
|
|
|
log_write_do_work(custom_log, FALSE, tstamp, domain, level, file, line, func,
|
2021-06-14 18:53:59 +00:00
|
|
|
user_format, user_ap);
|
2021-06-08 01:46:52 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 02:41:42 +00:00
|
|
|
g_free(tstamp);
|
|
|
|
|
2021-06-16 02:50:45 +00:00
|
|
|
ws_assert(level != LOG_LEVEL_NONE);
|
2021-06-19 04:30:29 +00:00
|
|
|
if (level >= fatal_log_level) {
|
2021-06-08 01:46:52 +00:00
|
|
|
G_BREAKPOINT();
|
|
|
|
ws_assert_not_reached();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-11 12:39:16 +00:00
|
|
|
void ws_logv(const char *domain, enum ws_log_level level,
|
2021-06-08 01:46:52 +00:00
|
|
|
const char *format, va_list ap)
|
|
|
|
{
|
2021-06-14 22:10:09 +00:00
|
|
|
|
2021-06-19 21:26:58 +00:00
|
|
|
if (ws_log_msg_is_active(domain, level) == FALSE)
|
2021-06-08 01:46:52 +00:00
|
|
|
return;
|
|
|
|
|
2021-06-14 18:53:59 +00:00
|
|
|
log_write_dispatch(domain, level, NULL, -1, NULL, format, ap);
|
2021-06-08 01:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-14 23:01:25 +00:00
|
|
|
void ws_logv_full(const char *domain, enum ws_log_level level,
|
|
|
|
const char *file, int line, const char *func,
|
|
|
|
const char *format, va_list ap)
|
|
|
|
{
|
2021-06-19 21:26:58 +00:00
|
|
|
if (ws_log_msg_is_active(domain, level) == FALSE)
|
2021-06-14 23:01:25 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
log_write_dispatch(domain, level, file, line, func, format, ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-11 12:39:16 +00:00
|
|
|
void ws_log(const char *domain, enum ws_log_level level,
|
2021-06-08 01:46:52 +00:00
|
|
|
const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
2021-06-19 21:26:58 +00:00
|
|
|
if (ws_log_msg_is_active(domain, level) == FALSE)
|
2021-06-08 01:46:52 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
va_start(ap, format);
|
2021-06-14 18:53:59 +00:00
|
|
|
log_write_dispatch(domain, level, NULL, -1, NULL, format, ap);
|
2021-06-08 01:46:52 +00:00
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-11 12:39:16 +00:00
|
|
|
void ws_log_full(const char *domain, enum ws_log_level level,
|
2021-06-06 15:43:27 +00:00
|
|
|
const char *file, int line, const char *func,
|
|
|
|
const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
2021-06-08 01:46:52 +00:00
|
|
|
|
2021-06-19 21:26:58 +00:00
|
|
|
if (ws_log_msg_is_active(domain, level) == FALSE)
|
2021-06-08 01:46:52 +00:00
|
|
|
return;
|
|
|
|
|
2021-06-06 15:43:27 +00:00
|
|
|
va_start(ap, format);
|
2021-06-14 18:53:59 +00:00
|
|
|
log_write_dispatch(domain, level, file, line, func, format, ap);
|
2021-06-06 15:43:27 +00:00
|
|
|
va_end(ap);
|
2021-06-08 01:46:52 +00:00
|
|
|
}
|
2021-06-06 15:43:27 +00:00
|
|
|
|
2021-06-08 01:46:52 +00:00
|
|
|
|
2021-06-14 18:53:59 +00:00
|
|
|
void ws_log_default_writer(const char *domain, enum ws_log_level level,
|
|
|
|
const char *timestamp,
|
|
|
|
const char *file, int line, const char *func,
|
|
|
|
const char *user_format, va_list user_ap,
|
|
|
|
void *user_data _U_)
|
|
|
|
{
|
2021-06-12 21:29:22 +00:00
|
|
|
log_write_do_work(stderr, color_enabled, timestamp, domain, level, file, line, func, user_format, user_ap);
|
2021-06-14 18:53:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-08 01:46:52 +00:00
|
|
|
static void ws_log_cleanup(void)
|
|
|
|
{
|
2021-06-10 21:13:11 +00:00
|
|
|
if (registered_log_writer_data_free) {
|
|
|
|
registered_log_writer_data_free(registered_log_writer_data);
|
|
|
|
registered_log_writer_data = NULL;
|
2021-06-08 01:46:52 +00:00
|
|
|
}
|
|
|
|
if (custom_log) {
|
|
|
|
fclose(custom_log);
|
|
|
|
custom_log = NULL;
|
|
|
|
}
|
2021-06-19 21:03:31 +00:00
|
|
|
free_log_filter(&domain_filter);
|
|
|
|
free_log_filter(&debug_filter);
|
|
|
|
free_log_filter(&noisy_filter);
|
2021-06-08 01:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ws_log_add_custom_file(FILE *fp)
|
|
|
|
{
|
|
|
|
if (custom_log != NULL) {
|
|
|
|
fclose(custom_log);
|
|
|
|
}
|
2021-06-14 17:41:34 +00:00
|
|
|
custom_log = fp;
|
2021-06-06 15:43:27 +00:00
|
|
|
}
|
2021-06-15 21:50:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
void ws_log_print_usage(FILE *fp)
|
|
|
|
{
|
|
|
|
fprintf(fp, "Diagnostic output:\n");
|
2021-06-21 21:39:54 +00:00
|
|
|
fprintf(fp, " --log-level <level> one of \"critical\", \"warning\", \"message\", "
|
2021-06-15 21:50:46 +00:00
|
|
|
"\"info\", \"debug\" or \"noisy\"\n");
|
2021-06-21 21:39:54 +00:00
|
|
|
fprintf(fp, " --log-fatal <level> one of \"critical\" or \"warning\", causes level "
|
|
|
|
"to abort the program\n");
|
|
|
|
fprintf(fp, " --log-domains <[!]list> comma separated list of the active log domains\n");
|
|
|
|
fprintf(fp, " --log-debug <[!]list> comma separated list of domains with \"debug\" level\n");
|
|
|
|
fprintf(fp, " --log-noisy <[!]list> comma separated list of domains with \"noisy\" level\n");
|
|
|
|
fprintf(fp, " --log-file <path> path of file to output messages to (in addition to stderr)\n");
|
2021-06-15 21:50:46 +00:00
|
|
|
}
|