wslog: Add support for domain filtering

A domain filter can be given in the environment variable
'WS_LOG_DOMAINS' or in a command-line options "--log-domains".

The filter is specified as a comma separated case insensitive list,
for example:

    ./tshark  --log-domains=main,capture

Domain data type switches from an enum to a string. There is no
constaint on adding new domains, neither in code or at runtime.
The string format is arbitrary, only positive matches will produce
output.
This commit is contained in:
João Valverde 2021-06-11 13:39:16 +01:00
parent 82739fc4f5
commit 5a662ba3fb
16 changed files with 153 additions and 96 deletions

View File

@ -150,7 +150,7 @@ capture_opts_cleanup(capture_options *capture_opts)
/* log content of capture_opts */
void
capture_opts_log(enum ws_log_domain log_domain, enum ws_log_level log_level, capture_options *capture_opts) {
capture_opts_log(const char *log_domain, enum ws_log_level log_level, capture_options *capture_opts) {
guint i;
ws_log(log_domain, log_level, "CAPTURE OPTIONS :");

View File

@ -339,7 +339,7 @@ capture_opts_add_opt(capture_options *capture_opts, int opt, const char *optarg)
/* log content of capture_opts */
extern void
capture_opts_log(enum ws_log_domain domain, enum ws_log_level level, capture_options *capture_opts);
capture_opts_log(const char *domain, enum ws_log_level level, capture_options *capture_opts);
enum caps_query {
CAPS_QUERY_LINK_TYPES = 0x1,

View File

@ -227,7 +227,6 @@ libwsutil.so.0 libwsutil0 #MINVER#
ws_init_sockets@Base 3.1.0
ws_log@Base 3.5.0
ws_log_add_custom_file@Base 3.5.0
ws_log_domain_to_string@Base 3.5.0
ws_log_fprint@Base 3.5.0
ws_log_full@Base 3.5.0
ws_log_get_level@Base 3.5.0
@ -235,6 +234,8 @@ libwsutil.so.0 libwsutil0 #MINVER#
ws_log_init_with_data@Base 3.5.0
ws_log_level_is_active@Base 3.5.0
ws_log_level_to_string@Base 3.5.0
ws_log_set_domain_filter_args@Base 3.5.0
ws_log_set_domain_filter_str@Base 3.5.0
ws_log_set_level@Base 3.5.0
ws_log_set_level_args@Base 3.5.0
ws_log_set_level_str@Base 3.5.0

View File

@ -329,7 +329,7 @@ static gboolean need_timeout_workaround;
static void
dumpcap_log_writer(const char *format, va_list ap,
const char *prefix,
enum ws_log_domain domain,
const char *domain,
enum ws_log_level level,
void *user_data);
@ -4859,6 +4859,7 @@ main(int argc, char *argv[])
cmdarg_err("Invalid log level \"%s\"", opt_err_val);
exit (1);
}
ws_log_set_domain_filter_args(&argc, argv);
#ifdef _WIN32
create_app_running_mutex();
@ -5572,7 +5573,7 @@ main(int argc, char *argv[])
static void
dumpcap_log_writer(const char *format, va_list ap,
const char *prefix,
enum ws_log_domain domain _U_,
const char *domain _U_,
enum ws_log_level level _U_,
void *user_data _U_)
{

View File

@ -2540,6 +2540,7 @@ int main(int argc, char *argv[]) {
cmdarg_err("Invalid log level \"%s\"", opt_err_val);
return EXIT_FAILURE;
}
ws_log_set_domain_filter_args(&argc, argv);
/*
* Get credential information for later use.

View File

@ -163,6 +163,7 @@ int main(int argc, char *argv[])
cmdarg_err("Invalid log level \"%s\"", opt_err_val);
return EXIT_FAILURE;
}
ws_log_set_domain_filter_args(&argc, argv);
/*
* Get credential information for later use.

View File

@ -459,6 +459,7 @@ main(int argc, char *argv[])
cmdarg_err("Invalid log level \"%s\"", opt_err_val);
return INVALID_OPTION;
}
ws_log_set_domain_filter_args(&argc, argv);
/* Initialize the version information. */
ws_init_version_info("Rawshark (Wireshark)", NULL,

View File

@ -126,6 +126,7 @@ main(int argc, char *argv[])
cmdarg_err("Invalid log level \"%s\"", opt_err_val);
return INIT_FAILED;
}
ws_log_set_domain_filter_args(&argc, argv);
/*
* Get credential information for later use, and drop privileges

View File

@ -359,6 +359,7 @@ main(int argc, char *argv[])
cmdarg_err("Invalid log level \"%s\"", opt_err_val);
return INVALID_OPTION;
}
ws_log_set_domain_filter_args(&argc, argv);
#ifdef _WIN32
create_app_running_mutex();

View File

@ -784,6 +784,7 @@ main(int argc, char *argv[])
cmdarg_err("Invalid log level \"%s\"", opt_err_val);
return INVALID_OPTION;
}
ws_log_set_domain_filter_args(&argc, argv);
ws_debug("tshark started with %d args", argc);

View File

@ -20,7 +20,7 @@
void
console_log_writer(const char *format, va_list ap,
const char *prefix, enum ws_log_domain domain _U_,
const char *prefix, const char *domain _U_,
enum ws_log_level level _U_, void *ptr _U_)
{
gboolean fatal = level == LOG_LEVEL_ERROR;

View File

@ -27,7 +27,7 @@ extern "C" {
*/
void
console_log_writer(const char *format, va_list ap,
const char *prefix, enum ws_log_domain domain,
const char *prefix, const char *domain,
enum ws_log_level level, void *ptr);
#ifdef __cplusplus

View File

@ -587,6 +587,7 @@ int main(int argc, char *qt_argv[])
cmdarg_err("Invalid log level \"%s\"", opt_err_val);
exit_application(INVALID_OPTION);
}
ws_log_set_domain_filter_args(&argc, argv);
/*
* Get credential information for later use, and drop privileges

View File

@ -11,22 +11,21 @@
#ifndef __WS_LOG_DOMAINS_H__
#define __WS_LOG_DOMAINS_H__
enum ws_log_domain {
/* Null domain */
#define LOG_DOMAIN_NONE "(notset)"
/* Default domain */
LOG_DOMAIN_DEFAULT,
#define LOG_DOMAIN_DEFAULT "Default"
/* Main execution domain (wireshark, tshark, etc) */
LOG_DOMAIN_MAIN,
#define LOG_DOMAIN_MAIN "Main"
/* Capture domain (except for capture child, see below) */
LOG_DOMAIN_CAPTURE,
#define LOG_DOMAIN_CAPTURE "Capture"
/* Capture child domain (the capture child might also contain
* file domain messages!) */
LOG_DOMAIN_CAPCHILD,
LOG_DOMAIN_WIRETAP,
LOG_DOMAIN_EPAN,
LOG_DOMAIN_WSUTIL,
LOG_DOMAIN_QTUI,
_LOG_DOMAIN_LAST
};
#define LOG_DOMAIN_CAPCHILD "Capchild"
#define LOG_DOMAIN_WIRETAP "Wiretap"
#define LOG_DOMAIN_EPAN "Epan"
#define LOG_DOMAIN_WSUTIL "WSUtil"
#define LOG_DOMAIN_QTUI "GUI"
#endif /* __WS_LOG_DOMAINS_H__ */

View File

@ -20,13 +20,16 @@
#define PREFIX_BUFSIZE 128
#define LOGENVVAR "WS_LOG_LEVEL"
#define _ENV_LEVEL "WS_LOG_LEVEL"
#define _ENV_DOMAINS "WS_LOG_DOMAINS"
/* TODO: Add filtering by domain. */
static enum ws_log_level current_log_level = LOG_LEVEL_MESSAGE;
GPtrArray *domain_filter = NULL;
static ws_log_writer_cb *registered_log_writer = NULL;
static void *registered_log_writer_data = NULL;
@ -73,37 +76,33 @@ const char *ws_log_level_to_string(enum ws_log_level level)
}
const char *ws_log_domain_to_string(enum ws_log_domain domain)
{
switch (domain) {
case LOG_DOMAIN_DEFAULT:
return "Default";
case LOG_DOMAIN_MAIN:
return "Main";
case LOG_DOMAIN_CAPTURE:
return "Capture";
case LOG_DOMAIN_CAPCHILD:
return "CapChild";
case LOG_DOMAIN_WIRETAP:
return "Wiretap";
case LOG_DOMAIN_EPAN:
return "Epan";
case LOG_DOMAIN_WSUTIL:
return "Util";
case LOG_DOMAIN_QTUI:
return "GUI";
default:
return "(BOGUS LOG DOMAIN)";
}
}
gboolean ws_log_level_is_active(enum ws_log_level level)
{
return level <= current_log_level;
}
gboolean ws_log_domain_is_active(const char *domain)
{
if (domain_filter == NULL)
return TRUE;
for (guint i = 0; i < domain_filter->len; i++) {
if (g_ascii_strcasecmp(domain_filter->pdata[i], domain) == 0) {
return TRUE;
}
}
return FALSE;
}
static gboolean log_drop_message(const char *domain, enum ws_log_level level)
{
return !ws_log_level_is_active(level) || !ws_log_domain_is_active(domain);
}
enum ws_log_level ws_log_get_level(void)
{
return current_log_level;
@ -146,14 +145,11 @@ enum ws_log_level ws_log_set_level_str(const char *str_level)
}
static const char *set_level_and_prune_argv(int count, char **ptr, int prune_extra,
static const char *log_prune_argv(int count, char **ptr, int prune_extra,
const char *optarg, int *ret_argc)
{
if (optarg && ws_log_set_level_str(optarg) != LOG_LEVEL_NONE)
optarg = NULL; /* success */
/*
* We found a "--log-level" option. We will remove it from
* 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.
@ -165,21 +161,20 @@ static const char *set_level_and_prune_argv(int count, char **ptr, int prune_ext
return optarg;
}
const char *ws_log_set_level_args(int *argc_ptr, char *argv[])
const char *log_parse_args(int *argc_ptr, char *argv[], const char *optstr)
{
char **p;
int c;
const char *opt = "--log-level";
size_t len = strlen(opt);
size_t optlen = strlen(optstr);
const char *optarg;
for (p = argv, c = *argc_ptr; *p != NULL; p++, c--) {
if (strncmp(*p, opt, len) == 0) {
optarg = *p + len;
if (strncmp(*p, optstr, optlen) == 0) {
optarg = *p + optlen;
/* Two possibilities:
* --log_level <level>
* --<option> <value>
* or
* --log-level=<level>
* --<option>=<value>
*/
if (optarg[0] == '\0') {
/* value is separated with blank space */
@ -188,14 +183,14 @@ const char *ws_log_set_level_args(int *argc_ptr, char *argv[])
/* If the option value after the blank is missing or stars with '-' just ignore it.
* But we should probably signal an error (missing required value). */
if (optarg == NULL || !*optarg || *optarg == '-') {
return set_level_and_prune_argv(c, p, 0, NULL, argc_ptr);
return log_prune_argv(c, p, 0, NULL, argc_ptr);
}
return set_level_and_prune_argv(c, p, 1, optarg, argc_ptr);
return log_prune_argv(c, p, 1, optarg, argc_ptr);
}
else if (optarg[0] == '=') {
/* value is after equals */
optarg += 1;
return set_level_and_prune_argv(c, p, 0, optarg, argc_ptr);
return log_prune_argv(c, p, 0, optarg, argc_ptr);
}
/* we didn't find what we want */
}
@ -204,17 +199,72 @@ const char *ws_log_set_level_args(int *argc_ptr, char *argv[])
}
const char *ws_log_set_level_args(int *argc_ptr, char *argv[])
{
const char *optval = NULL;
enum ws_log_level level;
optval = log_parse_args(argc_ptr, argv, "--log-level");
if (optval == NULL)
return NULL;
level = ws_log_set_level_str(optval);
if (level == LOG_LEVEL_NONE)
return optval;
return NULL;
}
void ws_log_set_domain_filter_str(const char *str_filter)
{
char *tok;
const char *sep = ",;";
char *str;
if (domain_filter != NULL)
g_ptr_array_free(domain_filter, TRUE);
domain_filter = g_ptr_array_new_with_free_func(g_free);
str = g_strdup(str_filter);
for (tok = strtok(str, sep); tok != NULL; tok = strtok(NULL, sep)) {
g_ptr_array_add(domain_filter, g_strdup(tok));
}
g_free(str);
}
void ws_log_set_domain_filter_args(int *argc_ptr, char *argv[])
{
const char *optval = NULL;
optval = log_parse_args(argc_ptr, argv, "--log-domains");
if (optval == NULL)
return;
ws_log_set_domain_filter_str(optval);
}
void ws_log_init(ws_log_writer_cb *_writer)
{
if (_writer) {
registered_log_writer = _writer;
}
const char *env = g_getenv(LOGENVVAR);
const char *env;
env = g_getenv(_ENV_LEVEL);
if (env && ws_log_set_level_str(env) == LOG_LEVEL_NONE) {
fprintf(stderr, "Ignoring invalid environment value %s=\"%s\"\n", LOGENVVAR, env);
fprintf(stderr, "Ignoring invalid environment value %s=\"%s\"\n", _ENV_LEVEL, env);
}
env = g_getenv(_ENV_DOMAINS);
if (env)
ws_log_set_domain_filter_str(env);
atexit(ws_log_cleanup);
}
@ -244,23 +294,6 @@ static inline const char *_lvl_to_str(enum ws_log_level level)
}
static inline const char *_dom_to_str(enum ws_log_domain domain)
{
switch (domain) {
case LOG_DOMAIN_DEFAULT: return "Dflt";
case LOG_DOMAIN_MAIN: return "Main";
case LOG_DOMAIN_CAPTURE: return "Capt";
case LOG_DOMAIN_CAPCHILD: return "CChd";
case LOG_DOMAIN_WIRETAP: return "Wtap";
case LOG_DOMAIN_EPAN: return "Epan";
case LOG_DOMAIN_WSUTIL: return "Util";
case LOG_DOMAIN_QTUI: return "Qtui";
default:
return "(BOGUS LOG DOMAIN)";
}
}
struct logstr {
char buffer[PREFIX_BUFSIZE];
char *ptr;
@ -328,12 +361,12 @@ static void create_log_time(struct logstr *str)
static void logstr_prefix_print(struct logstr *str,
enum ws_log_domain domain, enum ws_log_level level,
const char *domain, enum ws_log_level level,
const char *file, int line, const char *func)
{
create_log_time(str);
logstr_snprintf(str, " [%s-%s]", _dom_to_str(domain), _lvl_to_str(level));
logstr_snprintf(str, " [%s-%s]", domain, _lvl_to_str(level));
if (func)
logstr_snprintf(str, " %s()", func);
@ -346,7 +379,7 @@ static void logstr_prefix_print(struct logstr *str,
}
static void log_internal_write(enum ws_log_domain domain, enum ws_log_level level,
static void log_internal_write(const char *domain, enum ws_log_level level,
const char *file, int line, const char *func,
const char *user_format, va_list user_ap)
{
@ -377,22 +410,22 @@ static void log_internal_write(enum ws_log_domain domain, enum ws_log_level leve
}
void ws_logv(enum ws_log_domain domain, enum ws_log_level level,
void ws_logv(const char *domain, enum ws_log_level level,
const char *format, va_list ap)
{
if (!ws_log_level_is_active(level))
if (log_drop_message(domain, level))
return;
log_internal_write(domain, level, NULL, -1, NULL, format, ap);
}
void ws_log(enum ws_log_domain domain, enum ws_log_level level,
void ws_log(const char *domain, enum ws_log_level level,
const char *format, ...)
{
va_list ap;
if (!ws_log_level_is_active(level))
if (log_drop_message(domain, level))
return;
va_start(ap, format);
@ -401,13 +434,13 @@ void ws_log(enum ws_log_domain domain, enum ws_log_level level,
}
void ws_log_full(enum ws_log_domain domain, enum ws_log_level level,
void ws_log_full(const char *domain, enum ws_log_level level,
const char *file, int line, const char *func,
const char *format, ...)
{
va_list ap;
if (!ws_log_level_is_active(level))
if (log_drop_message(domain, level))
return;
va_start(ap, format);
@ -426,6 +459,10 @@ static void ws_log_cleanup(void)
fclose(custom_log);
custom_log = NULL;
}
if (domain_filter) {
g_ptr_array_free(domain_filter, TRUE);
domain_filter = NULL;
}
}

View File

@ -43,7 +43,7 @@ enum ws_log_level {
/** Callback for registering a log writer. */
typedef void (ws_log_writer_cb)(const char *format, va_list ap,
const char *prefix,
enum ws_log_domain domain,
const char *domain,
enum ws_log_level level,
void *user_data);
@ -63,11 +63,6 @@ WS_DLL_PUBLIC
const char *ws_log_level_to_string(enum ws_log_level level);
/** Convert a numerical domain to its string representation. */
WS_DLL_PUBLIC
const char *ws_log_domain_to_string(enum ws_log_domain domain);
/** Checks if the active log level would discard a message for the given
* log domain.
*
@ -108,6 +103,23 @@ WS_DLL_PUBLIC
const char *ws_log_set_level_args(int *argcp, char **argv);
/** Set a domain filter from a string.
*
* Domain filter is a case insensitive list separated by ',' or ';'. Only
* the domains in the filter will generate output; the others will be muted.
*/
WS_DLL_PUBLIC
void ws_log_set_domain_filter_str(const char *domain_filter);
/** Set the active domain from an argv vector.
*
* Same as above but parses the filter from the command line arguments.
*/
WS_DLL_PUBLIC
void ws_log_set_domain_filter_args(int *argcp, char **argv);
/** Initializes the logging code.
*
* Must be called at startup before using the log API. If provided the
@ -134,7 +146,7 @@ void ws_log_init_with_data(ws_log_writer_cb *writer, void *user_data,
* Takes a format string and a variable number of arguments.
*/
WS_DLL_PUBLIC
void ws_log(enum ws_log_domain domain, enum ws_log_level level,
void ws_log(const char *domain, enum ws_log_level level,
const char *format, ...) G_GNUC_PRINTF(3,4);
/** This function is called to output a message to the log.
@ -142,7 +154,7 @@ void ws_log(enum ws_log_domain domain, enum ws_log_level level,
* Takes a format string and a 'va_list'.
*/
WS_DLL_PUBLIC
void ws_logv(enum ws_log_domain domain, enum ws_log_level level,
void ws_logv(const char *domain, enum ws_log_level level,
const char *format, va_list ap);
@ -152,7 +164,7 @@ void ws_logv(enum ws_log_domain domain, enum ws_log_level level,
* information. 'func' may be NULL.
*/
WS_DLL_PUBLIC
void ws_log_full(enum ws_log_domain domain, enum ws_log_level level,
void ws_log_full(const char *domain, enum ws_log_level level,
const char *file, int line, const char *func,
const char *format, ...) G_GNUC_PRINTF(6,7);