[Asterisk-code-review] logger: Add custom logging capabilities (asterisk[18])
Kevin Harwell
asteriskteam at digium.com
Tue Sep 21 12:10:00 CDT 2021
Kevin Harwell has submitted this change. ( https://gerrit.asterisk.org/c/asterisk/+/16508 )
Change subject: logger: Add custom logging capabilities
......................................................................
logger: Add custom logging capabilities
Adds the ability for users to log to custom log levels
by providing custom log level names in logger.conf. Also
adds a logger show levels CLI command.
ASTERISK-29529
Change-Id: If082703cf81a436ae5a565c75225fa8c0554b702
---
M apps/app_verbose.c
M configs/samples/logger.conf.sample
A doc/CHANGES-staging/logger.txt
M include/asterisk/logger.h
M main/logger.c
5 files changed, 183 insertions(+), 34 deletions(-)
Approvals:
George Joseph: Looks good to me, approved
Kevin Harwell: Approved for Submit
diff --git a/apps/app_verbose.c b/apps/app_verbose.c
index 5b78e18..a888ba0 100644
--- a/apps/app_verbose.c
+++ b/apps/app_verbose.c
@@ -33,6 +33,7 @@
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
+#include "asterisk/logger.h"
static char *app_verbose = "Verbose";
static char *app_log = "Log";
@@ -61,7 +62,8 @@
<syntax>
<parameter name="level" required="true">
<para>Level must be one of <literal>ERROR</literal>, <literal>WARNING</literal>, <literal>NOTICE</literal>,
- <literal>DEBUG</literal>, <literal>VERBOSE</literal> or <literal>DTMF</literal>.</para>
+ <literal>DEBUG</literal>, <literal>VERBOSE</literal>, <literal>DTMF</literal>, or
+ the name of a custom dynamic logging level.</para>
</parameter>
<parameter name="message" required="true">
<para>Output text message.</para>
@@ -135,7 +137,7 @@
} else if (!strcasecmp(args.level, "DTMF")) {
lnum = __LOG_DTMF;
} else {
- ast_log(LOG_ERROR, "Unknown log level: '%s'\n", args.level);
+ lnum = ast_logger_get_dynamic_level(args.level);
}
if (lnum > -1) {
@@ -143,6 +145,9 @@
snprintf(extension, sizeof(extension), "Ext. %s", ast_channel_exten(chan));
ast_log(lnum, extension, ast_channel_priority(chan), context, "%s\n", args.msg);
+ } else {
+ ast_log(LOG_ERROR, "Unknown log level: '%s'\n", args.level);
+ return 0;
}
return 0;
diff --git a/configs/samples/logger.conf.sample b/configs/samples/logger.conf.sample
index 9e2fb35..777f803 100644
--- a/configs/samples/logger.conf.sample
+++ b/configs/samples/logger.conf.sample
@@ -85,6 +85,11 @@
; The default is 1000
;logger_queue_limit = 250
;
+; Any custom logging levels you may want to use, which can then
+; be sent to logging channels. The maximum number of custom
+; levels is 16, but not all of these may be available if modules
+; in Asterisk define their own.
+;custom_levels = foobar,important,compliance
;
[logfiles]
;
@@ -130,6 +135,7 @@
; dtmf
; fax
; security
+; <customlevel>
;
; Verbose takes an optional argument, in the form of an integer level. The
; verbose level can be set per logfile. Verbose messages with higher levels
@@ -176,3 +182,5 @@
;
;syslog.local0 => notice,warning,error
;
+; A log level defined in 'custom_levels' above
+;important.log = important
diff --git a/doc/CHANGES-staging/logger.txt b/doc/CHANGES-staging/logger.txt
new file mode 100644
index 0000000..d09ebcc
--- /dev/null
+++ b/doc/CHANGES-staging/logger.txt
@@ -0,0 +1,5 @@
+Subject: logger
+
+Added the ability to define custom log levels in logger.conf
+and use them in the Log dialplan application. Also adds a
+logger show levels CLI command.
diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h
index d823ed4..4234262 100644
--- a/include/asterisk/logger.h
+++ b/include/asterisk/logger.h
@@ -330,6 +330,14 @@
int ast_logger_register_level(const char *name);
/*!
+ * \brief Retrieve dynamic logging level id
+ * \param name The name of the level
+ * \retval The unique integer id for the given level
+ * \retval -1 if level name not found
+ */
+int ast_logger_get_dynamic_level(const char *name);
+
+/*!
* \brief Unregister a previously registered logger level
* \param name The name of the level to be unregistered
* \return nothing
diff --git a/main/logger.c b/main/logger.c
index 457a6fb..6b0e76f 100644
--- a/main/logger.c
+++ b/main/logger.c
@@ -74,6 +74,10 @@
/*** DOCUMENTATION
***/
+static int logger_register_level(const char *name);
+static int logger_unregister_level(const char *name);
+static int logger_get_dynamic_level(const char *name);
+
static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */
static char queue_log_name[256] = QUEUELOG;
@@ -211,6 +215,15 @@
"DTMF",
};
+/*! \brief Custom dynamic logging levels added by the user
+ *
+ * The first 16 levels are reserved for system usage, and the remaining
+ * levels are reserved for usage by dynamic levels registered via
+ * ast_logger_register_level.
+ */
+
+static char *custom_dynamic_levels[NUMLOGLEVELS];
+
/*! \brief Colors used in the console for logging */
static const int colors[NUMLOGLEVELS] = {
COLOR_BRGREEN,
@@ -697,6 +710,26 @@
}
/*!
+ * \brief Checks if level exists in array of level names
+ * \param levels Array of level names
+ * \param level Name to search for
+ * \len Size of levels
+ *
+ * \retval 1 Found
+ * \retval 0 Not Found
+ */
+static int custom_level_still_exists(char **levels, char *level, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ if (!strcmp(levels[i], level)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*!
* \brief Read config, setup channels.
* \param altconf Alternate configuration file to read.
*
@@ -809,6 +842,39 @@
}
}
+ /* Custom dynamic logging levels defined by user */
+ if ((s = ast_variable_retrieve(cfg, "general", "custom_levels"))) {
+ char *customlogs = ast_strdupa(s);
+ char *logfile;
+ char *new_custom_levels[16] = { };
+ unsigned int level, new_level = 0;
+
+ /* get the custom levels we need to register or reload */
+ while ((logfile = strsep(&customlogs, ","))) {
+ new_custom_levels[new_level++] = logfile;
+ }
+
+ /* unregister existing custom levels, if they're not still
+ specified in customlogs, to make room for new levels */
+ for (level = 16; level < ARRAY_LEN(levels); level++) {
+ if (levels[level] && custom_dynamic_levels[level] &&
+ !custom_level_still_exists(new_custom_levels, levels[level], ARRAY_LEN(new_custom_levels))) {
+ logger_unregister_level(levels[level]);
+ custom_dynamic_levels[level] = 0;
+ }
+ }
+
+ new_level = 0;
+ while ((logfile = new_custom_levels[new_level++])) {
+ /* Lock already held, so directly register the level,
+ unless it's already registered (as during reload) */
+ if (logger_get_dynamic_level(logfile) == -1) {
+ int custom_level = logger_register_level(logfile);
+ custom_dynamic_levels[custom_level] = logfile;
+ }
+ }
+ }
+
var = ast_variable_browse(cfg, "logfiles");
for (; var; var = var->next) {
chan = make_logchannel(var->name, var->value, var->lineno, 0);
@@ -1403,6 +1469,35 @@
return CLI_SUCCESS;
}
+/*! \brief CLI command to show logging levels */
+static char *handle_logger_show_levels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMATL2 "%5s %s\n"
+ unsigned int level;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "logger show levels";
+ e->usage =
+ "Usage: logger show levels\n"
+ " List configured logger levels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ ast_cli(a->fd, FORMATL2, "Level", "Name");
+ ast_cli(a->fd, FORMATL2, "-----", "----");
+ AST_RWLIST_RDLOCK(&logchannels);
+ for (level = 0; level < ARRAY_LEN(levels); level++) {
+ if (levels[level]) {
+ ast_cli(a->fd, "%5d %s\n", level, levels[level]);
+ }
+ }
+ AST_RWLIST_UNLOCK(&logchannels);
+ ast_cli(a->fd, "\n");
+
+ return CLI_SUCCESS;
+}
+
int ast_logger_create_channel(const char *log_channel, const char *components)
{
struct logchannel *chan;
@@ -1545,6 +1640,7 @@
static struct ast_cli_entry cli_logger[] = {
AST_CLI_DEFINE(handle_logger_show_channels, "List configured log channels"),
+ AST_CLI_DEFINE(handle_logger_show_levels, "List configured log levels"),
AST_CLI_DEFINE(handle_logger_reload, "Reopens the log files"),
AST_CLI_DEFINE(handle_logger_rotate, "Rotates and reopens the log files"),
AST_CLI_DEFINE(handle_logger_set_level, "Enables/Disables a specific logging level for this console"),
@@ -2348,19 +2444,14 @@
{
struct logchannel *cur;
- AST_RWLIST_WRLOCK(&logchannels);
-
global_logmask = 0;
AST_RWLIST_TRAVERSE(&logchannels, cur, list) {
make_components(cur);
global_logmask |= cur->logmask;
}
-
- AST_RWLIST_UNLOCK(&logchannels);
}
-
#ifdef AST_DEVMODE
AST_THREADSTORAGE_RAW(trace_indent);
@@ -2452,13 +2543,12 @@
}
#endif
-int ast_logger_register_level(const char *name)
+/* Lock should be held before calling this function */
+static int logger_register_level(const char *name)
{
unsigned int level;
unsigned int available = 0;
- AST_RWLIST_WRLOCK(&logchannels);
-
for (level = 0; level < ARRAY_LEN(levels); level++) {
if ((level >= 16) && !available && !levels[level]) {
available = level;
@@ -2469,7 +2559,6 @@
ast_log(LOG_WARNING,
"Unable to register dynamic logger level '%s': a standard logger level uses that name.\n",
name);
- AST_RWLIST_UNLOCK(&logchannels);
return -1;
}
@@ -2479,15 +2568,12 @@
ast_log(LOG_WARNING,
"Unable to register dynamic logger level '%s'; maximum number of levels registered.\n",
name);
- AST_RWLIST_UNLOCK(&logchannels);
return -1;
}
levels[available] = ast_strdup(name);
- AST_RWLIST_UNLOCK(&logchannels);
-
ast_debug(1, "Registered dynamic logger level '%s' with index %u.\n", name, available);
update_logchannels();
@@ -2495,42 +2581,79 @@
return available;
}
-void ast_logger_unregister_level(const char *name)
+int ast_logger_register_level(const char *name)
{
- unsigned int found = 0;
- unsigned int x;
+ int available = 0;
AST_RWLIST_WRLOCK(&logchannels);
+ available = logger_register_level(name);
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ return available;
+}
+
+static int logger_get_dynamic_level(const char *name)
+{
+ int level = -1;
+ unsigned int x;
for (x = 16; x < ARRAY_LEN(levels); x++) {
if (!levels[x]) {
continue;
}
-
- if (strcasecmp(levels[x], name)) {
- continue;
+ if (!strcasecmp(levels[x], name)) {
+ level = x;
+ break;
}
-
- found = 1;
- break;
}
- if (found) {
- /* take this level out of the global_logmask, to ensure that no new log messages
- * will be queued for it
- */
+ return level;
+}
- global_logmask &= ~(1 << x);
+int ast_logger_get_dynamic_level(const char *name)
+{
+ int level = -1;
- ast_free(levels[x]);
- levels[x] = NULL;
- AST_RWLIST_UNLOCK(&logchannels);
+ AST_RWLIST_RDLOCK(&logchannels);
- ast_debug(1, "Unregistered dynamic logger level '%s' with index %u.\n", name, x);
+ level = logger_get_dynamic_level(name);
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ return level;
+}
+
+static int logger_unregister_level(const char *name) {
+ unsigned int x;
+
+ x = logger_get_dynamic_level(name);
+ if (x == -1) {
+ return 0;
+ }
+ /* take this level out of the global_logmask, to ensure that no new log messages
+ * will be queued for it
+ */
+ global_logmask &= ~(1 << x);
+ ast_free(levels[x]);
+ levels[x] = NULL;
+ return x;
+}
+
+void ast_logger_unregister_level(const char *name)
+{
+ int x;
+
+ AST_RWLIST_WRLOCK(&logchannels);
+ x = logger_unregister_level(name);
+
+ if (x) {
update_logchannels();
- } else {
- AST_RWLIST_UNLOCK(&logchannels);
+ }
+
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ if (x) {
+ ast_debug(1, "Unregistered dynamic logger level '%s' with index %u.\n", name, x);
}
}
--
To view, visit https://gerrit.asterisk.org/c/asterisk/+/16508
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings
Gerrit-Project: asterisk
Gerrit-Branch: 18
Gerrit-Change-Id: If082703cf81a436ae5a565c75225fa8c0554b702
Gerrit-Change-Number: 16508
Gerrit-PatchSet: 2
Gerrit-Owner: N A <mail at interlinked.x10host.com>
Gerrit-Reviewer: Friendly Automation
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20210921/0772baf0/attachment-0001.html>
More information about the asterisk-code-review
mailing list