Skip to content

Commit 89709e2

Browse files
logger: Add channel-based filtering.
This adds the ability to filter console logging by channel or groups of channels. This can be useful on busy systems where an administrator would like to analyze certain calls in detail. A dialplan function is also included for the purpose of assigning a channel to a group (e.g. by tenant, or some other metric). ASTERISK-30483 #close Resolves: #242 UserNote: The console log can now be filtered by channels or groups of channels, using the logger filter CLI commands.
1 parent fb74ef1 commit 89709e2

1 file changed

Lines changed: 290 additions & 1 deletion

File tree

main/logger.c

Lines changed: 290 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ struct logmsg {
176176
int line;
177177
int lwp;
178178
ast_callid callid;
179+
unsigned int hidecli:1; /*!< Whether to suppress log message from CLI output (but log normally to other log channels */
179180
AST_DECLARE_STRING_FIELDS(
180181
AST_STRING_FIELD(date);
181182
AST_STRING_FIELD(file);
@@ -1645,6 +1646,271 @@ static char *handle_logger_remove_channel(struct ast_cli_entry *e, int cmd, stru
16451646
}
16461647
}
16471648

1649+
/* Call ID filtering */
1650+
1651+
AST_THREADSTORAGE(callid_group_name);
1652+
1653+
/*! \brief map call ID to group */
1654+
struct chan_group_lock {
1655+
AST_RWLIST_ENTRY(chan_group_lock) entry;
1656+
char name[0];
1657+
};
1658+
1659+
AST_RWLIST_HEAD_STATIC(chan_group_lock_list, chan_group_lock);
1660+
1661+
static int callid_filtering = 0;
1662+
1663+
static const char *get_callid_group(void)
1664+
{
1665+
char **callid_group;
1666+
callid_group = ast_threadstorage_get(&callid_group_name, sizeof(*callid_group));
1667+
return callid_group ? *callid_group : NULL;
1668+
}
1669+
1670+
static int callid_set_chanloggroup(const char *group)
1671+
{
1672+
/* Use threadstorage for constant time access, rather than a linked list */
1673+
ast_callid callid;
1674+
char **callid_group;
1675+
1676+
callid = ast_read_threadstorage_callid();
1677+
if (!callid) {
1678+
/* Should never be called on non-PBX threads */
1679+
ast_assert(0);
1680+
return -1;
1681+
}
1682+
1683+
callid_group = ast_threadstorage_get(&callid_group_name, sizeof(*callid_group));
1684+
1685+
if (!group) {
1686+
/* Remove from list */
1687+
if (!*callid_group) {
1688+
return 0; /* Wasn't in any group to begin with */
1689+
}
1690+
ast_free(*callid_group);
1691+
return 0; /* Set Call ID group for the first time */
1692+
}
1693+
/* Existing group */
1694+
ast_free(*callid_group);
1695+
*callid_group = ast_strdup(group);
1696+
if (!*callid_group) {
1697+
return -1;
1698+
}
1699+
return 0; /* Set Call ID group for the first time */
1700+
}
1701+
1702+
static int callid_group_remove_filters(void)
1703+
{
1704+
int i = 0;
1705+
struct chan_group_lock *cgl;
1706+
1707+
AST_RWLIST_WRLOCK(&chan_group_lock_list);
1708+
while ((cgl = AST_RWLIST_REMOVE_HEAD(&chan_group_lock_list, entry))) {
1709+
ast_free(cgl);
1710+
i++;
1711+
}
1712+
callid_filtering = 0;
1713+
AST_RWLIST_UNLOCK(&chan_group_lock_list);
1714+
return i;
1715+
}
1716+
1717+
static int callid_group_set_filter(const char *group, int enabled)
1718+
{
1719+
struct chan_group_lock *cgl;
1720+
1721+
AST_RWLIST_WRLOCK(&chan_group_lock_list);
1722+
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chan_group_lock_list, cgl, entry) {
1723+
if (!strcmp(group, cgl->name)) {
1724+
if (!enabled) {
1725+
AST_RWLIST_REMOVE_CURRENT(entry);
1726+
ast_free(cgl);
1727+
}
1728+
break;
1729+
}
1730+
}
1731+
AST_RWLIST_TRAVERSE_SAFE_END;
1732+
1733+
if (!enabled) {
1734+
if (AST_LIST_EMPTY(&chan_group_lock_list)) {
1735+
callid_filtering = 0;
1736+
}
1737+
AST_RWLIST_UNLOCK(&chan_group_lock_list);
1738+
return 0;
1739+
}
1740+
1741+
if (!cgl) {
1742+
cgl = ast_calloc(1, sizeof(*cgl) + strlen(group) + 1);
1743+
if (!cgl) {
1744+
AST_RWLIST_UNLOCK(&chan_group_lock_list);
1745+
return -1;
1746+
}
1747+
strcpy(cgl->name, group); /* Safe */
1748+
AST_RWLIST_INSERT_HEAD(&chan_group_lock_list, cgl, entry);
1749+
} /* else, already existed, and was already enabled, no change */
1750+
callid_filtering = 1;
1751+
AST_RWLIST_UNLOCK(&chan_group_lock_list);
1752+
return 0;
1753+
}
1754+
1755+
static int callid_logging_enabled(void)
1756+
{
1757+
struct chan_group_lock *cgl;
1758+
const char *callidgroup;
1759+
1760+
if (!callid_filtering) {
1761+
return 1; /* Everything enabled by default, if no filtering */
1762+
}
1763+
1764+
callidgroup = get_callid_group();
1765+
if (!callidgroup) {
1766+
return 0; /* Filtering, but no call group, not enabled */
1767+
}
1768+
1769+
AST_RWLIST_RDLOCK(&chan_group_lock_list);
1770+
AST_RWLIST_TRAVERSE(&chan_group_lock_list, cgl, entry) {
1771+
if (!strcmp(callidgroup, cgl->name)) {
1772+
break;
1773+
}
1774+
}
1775+
AST_RWLIST_UNLOCK(&chan_group_lock_list);
1776+
return cgl ? 1 : 0; /* If found, enabled, otherwise not */
1777+
}
1778+
1779+
/*** DOCUMENTATION
1780+
<function name="LOG_GROUP" language="en_US">
1781+
<synopsis>
1782+
Set the channel group name for log filtering on this channel
1783+
</synopsis>
1784+
<syntax>
1785+
<parameter name="group" required="false">
1786+
<para>Channel log group name. Leave empty to remove any existing group membership.</para>
1787+
<para>You can use any arbitrary alphanumeric name that can then be used by the
1788+
"logger filter changroup" CLI command to filter dialplan output by group name.</para>
1789+
</parameter>
1790+
</syntax>
1791+
<description>
1792+
<para>Assign a channel to a group for log filtering.</para>
1793+
<para>Because this application can result in dialplan execution logs
1794+
being suppressed (or unsuppressed) from the CLI if filtering is active,
1795+
it is recommended to call this as soon as possible when dialplan execution begins.</para>
1796+
<para>Calling this multiple times will replace any previous group assignment.</para>
1797+
<example title="Associate channel with group test">
1798+
exten => s,1,Set(LOG_GROUP()=test)
1799+
same => n,NoOp() ; if a logging call ID group filter name is enabled but test is not included, you will not see this
1800+
</example>
1801+
<example title="Associate channel with group important">
1802+
exten => s,1,Set(LOG_GROUP()=important)
1803+
same => n,Set(foo=bar) ; do some important things to show on the CLI (assuming it is filtered with important enabled)
1804+
same => n,Set(LOG_GROUP()=) ; remove from group important to stop showing execution on the CLI
1805+
same => n,Wait(5) ; do some unimportant stuff
1806+
</example>
1807+
</description>
1808+
<see-also>
1809+
<ref type="application">Log</ref>
1810+
</see-also>
1811+
</function>
1812+
***/
1813+
1814+
static int log_group_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
1815+
{
1816+
int res = callid_set_chanloggroup(value);
1817+
if (res) {
1818+
ast_log(LOG_ERROR, "Failed to set channel log group for %s\n", ast_channel_name(chan));
1819+
return -1;
1820+
}
1821+
return 0;
1822+
}
1823+
1824+
static struct ast_custom_function log_group_function = {
1825+
.name = "LOG_GROUP",
1826+
.write = log_group_write,
1827+
};
1828+
1829+
static char *handle_logger_chanloggroup_filter(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1830+
{
1831+
int enabled;
1832+
1833+
switch (cmd) {
1834+
case CLI_INIT:
1835+
e->command = "logger filter changroup";
1836+
e->usage =
1837+
"Usage: logger filter changroup <group> {on|off}\n"
1838+
" Add or remove channel groups from log filtering.\n"
1839+
" If filtering is active, only channels assigned\n"
1840+
" to a group that has been enabled using this command\n"
1841+
" will have execution shown in the CLI.\n";
1842+
return NULL;
1843+
case CLI_GENERATE:
1844+
return NULL;
1845+
}
1846+
1847+
if (a->argc < 5) {
1848+
return CLI_SHOWUSAGE;
1849+
}
1850+
1851+
enabled = ast_true(a->argv[4]) ? 1 : 0;
1852+
if (callid_group_set_filter(a->argv[3], enabled)) {
1853+
ast_cli(a->fd, "Failed to set channel group filter for group %s\n", a->argv[3]);
1854+
return CLI_FAILURE;
1855+
}
1856+
1857+
ast_cli(a->fd, "Logging of channel group '%s' is now %s\n", a->argv[3], enabled ? "enabled" : "disabled");
1858+
return CLI_SUCCESS;
1859+
}
1860+
1861+
static char *handle_logger_filter_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1862+
{
1863+
int i = 0;
1864+
struct chan_group_lock *cgl;
1865+
1866+
switch (cmd) {
1867+
case CLI_INIT:
1868+
e->command = "logger filter show";
1869+
e->usage =
1870+
"Usage: logger filter show\n"
1871+
" Show current logger filtering settings.\n";
1872+
return NULL;
1873+
case CLI_GENERATE:
1874+
return NULL;
1875+
}
1876+
1877+
AST_RWLIST_RDLOCK(&chan_group_lock_list);
1878+
AST_RWLIST_TRAVERSE(&chan_group_lock_list, cgl, entry) {
1879+
ast_cli(a->fd, "%3d %-32s\n", ++i, cgl->name);
1880+
}
1881+
AST_RWLIST_UNLOCK(&chan_group_lock_list);
1882+
1883+
if (i) {
1884+
ast_cli(a->fd, "%d channel group%s currently enabled\n", i, ESS(i));
1885+
} else {
1886+
ast_cli(a->fd, "No filtering currently active\n");
1887+
}
1888+
return CLI_SUCCESS;
1889+
}
1890+
1891+
static char *handle_logger_filter_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1892+
{
1893+
int removed;
1894+
1895+
switch (cmd) {
1896+
case CLI_INIT:
1897+
e->command = "logger filter reset";
1898+
e->usage =
1899+
"Usage: logger filter reset\n"
1900+
" Reset the logger filter.\n"
1901+
" This removes any channel groups from filtering\n"
1902+
" (all channel execution will be shown)\n";
1903+
return NULL;
1904+
case CLI_GENERATE:
1905+
return NULL;
1906+
}
1907+
1908+
removed = callid_group_remove_filters();
1909+
1910+
ast_cli(a->fd, "Log filtering has been reset (%d filter%s removed)\n", removed, ESS(removed));
1911+
return CLI_SUCCESS;
1912+
}
1913+
16481914
static struct ast_cli_entry cli_logger[] = {
16491915
AST_CLI_DEFINE(handle_logger_show_channels, "List configured log channels"),
16501916
AST_CLI_DEFINE(handle_logger_show_levels, "List configured log levels"),
@@ -1653,6 +1919,9 @@ static struct ast_cli_entry cli_logger[] = {
16531919
AST_CLI_DEFINE(handle_logger_set_level, "Enables/Disables a specific logging level for this console"),
16541920
AST_CLI_DEFINE(handle_logger_add_channel, "Adds a new logging channel"),
16551921
AST_CLI_DEFINE(handle_logger_remove_channel, "Removes a logging channel"),
1922+
AST_CLI_DEFINE(handle_logger_chanloggroup_filter, "Filter PBX logs by channel log group"),
1923+
AST_CLI_DEFINE(handle_logger_filter_show, "Show current PBX channel filtering"),
1924+
AST_CLI_DEFINE(handle_logger_filter_reset, "Reset PBX channel filtering"),
16561925
};
16571926

16581927
static void _handle_SIGXFSZ(int sig)
@@ -1709,7 +1978,7 @@ static void logger_print_normal(struct logmsg *logmsg)
17091978
}
17101979
break;
17111980
case LOGTYPE_CONSOLE:
1712-
if (!chan->formatter.format_log(chan, logmsg, buf, sizeof(buf))) {
1981+
if (!logmsg->hidecli && !chan->formatter.format_log(chan, logmsg, buf, sizeof(buf))) {
17131982
ast_console_puts_mutable_full(buf, logmsg->level, logmsg->sublevel);
17141983
}
17151984
break;
@@ -1977,6 +2246,7 @@ int init_logger(void)
19772246

19782247
/* register the logger cli commands */
19792248
ast_cli_register_multiple(cli_logger, ARRAY_LEN(cli_logger));
2249+
ast_custom_function_register(&log_group_function);
19802250

19812251
ast_mkdir(ast_config_AST_LOG_DIR, 0777);
19822252

@@ -2001,6 +2271,7 @@ void close_logger(void)
20012271

20022272
ast_logger_category_unload();
20032273

2274+
ast_custom_function_unregister(&log_group_function);
20042275
ast_cli_unregister_multiple(cli_logger, ARRAY_LEN(cli_logger));
20052276

20062277
logger_initialized = 0;
@@ -2030,6 +2301,8 @@ void close_logger(void)
20302301
ast_free(f);
20312302
}
20322303

2304+
callid_group_remove_filters();
2305+
20332306
closelog(); /* syslog */
20342307

20352308
AST_RWLIST_UNLOCK(&logchannels);
@@ -2140,12 +2413,26 @@ static void __attribute__((format(printf, 7, 0))) ast_log_full(int level, int su
21402413
const char *file, int line, const char *function, ast_callid callid,
21412414
const char *fmt, va_list ap)
21422415
{
2416+
int hidecli = 0;
21432417
struct logmsg *logmsg = NULL;
21442418

21452419
if (level == __LOG_VERBOSE && ast_opt_remote && ast_opt_exec) {
21462420
return;
21472421
}
21482422

2423+
if (callid_filtering && !callid_logging_enabled()) {
2424+
switch (level) {
2425+
case __LOG_VERBOSE:
2426+
case __LOG_DEBUG:
2427+
case __LOG_TRACE:
2428+
case __LOG_DTMF:
2429+
hidecli = 1; /* Hide the message from the CLI, but still log to any log files */
2430+
default: /* Always show NOTICE, WARNING, ERROR, etc. */
2431+
break;
2432+
}
2433+
return;
2434+
}
2435+
21492436
AST_LIST_LOCK(&logmsgs);
21502437
if (logger_queue_size >= logger_queue_limit && !close_logger_thread) {
21512438
logger_messages_discarded++;
@@ -2166,6 +2453,8 @@ static void __attribute__((format(printf, 7, 0))) ast_log_full(int level, int su
21662453
return;
21672454
}
21682455

2456+
logmsg->hidecli = hidecli;
2457+
21692458
/* If the logger thread is active, append it to the tail end of the list - otherwise skip that step */
21702459
if (logthread != AST_PTHREADT_NULL) {
21712460
AST_LIST_LOCK(&logmsgs);

0 commit comments

Comments
 (0)