@ -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 @@ static char *levels[NUMLOGLEVELS] = {
" 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 ,
@ -696,6 +709,26 @@ void ast_init_logger_for_socket_console(void)
ast_config_destroy ( cfg ) ;
}
/*!
* \ 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 @@ static int init_logger_chain(const char *altconf)
}
}
/* 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 @@ static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struc
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 char *handle_logger_remove_channel(struct ast_cli_entry *e, int cmd, stru
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 @@ static void update_logchannels(void)
{
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 @@ void __ast_trace(const char *file, int line, const char *func, enum ast_trace_in
}
# 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 @@ int ast_logger_register_level(const char *name)
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 @@ int ast_logger_register_level(const char *name)
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 @@ int ast_logger_register_level(const char *name)
return available ;
}
void ast_logger_un register_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 ) ) {
level = x ;
break ;
}
}
if ( strcasecmp ( levels [ x ] , name ) ) {
continue ;
return level ;
}
found = 1 ;
break ;
int ast_logger_get_dynamic_level ( const char * name )
{
int level = - 1 ;
AST_RWLIST_RDLOCK ( & logchannels ) ;
level = logger_get_dynamic_level ( name ) ;
AST_RWLIST_UNLOCK ( & logchannels ) ;
return level ;
}
if ( found ) {
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 ;
AST_RWLIST_UNLOCK ( & logchannels ) ;
return x ;
}
ast_debug ( 1 , " Unregistered dynamic logger level '%s' with index %u. \n " , name , 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 ) ;
if ( x ) {
ast_debug ( 1 , " Unregistered dynamic logger level '%s' with index %u. \n " , name , x ) ;
}
}