@@ -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+
16481914static 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
16581927static 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