mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 10:47:18 +00:00 
			
		
		
		
	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
This commit is contained in:
		
				
					committed by
					
						 Kevin Harwell
						Kevin Harwell
					
				
			
			
				
	
			
			
			
						parent
						
							6698753b24
						
					
				
				
					commit
					148f8355a0
				
			
							
								
								
									
										187
									
								
								main/logger.c
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								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 @@ 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_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); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user