Files
asterisk/main/logger_category.c
Kevin Harwell 56028426de Logging: Add debug logging categories
Added debug logging categories that allow a user to output debug
information based on a specified category. This lets the user limit,
and filter debug output to data relevant to a particular context,
or topic. For instance the following categories are now available for
debug logging purposes:

  dtls, dtls_packet, ice, rtcp, rtcp_packet, rtp, rtp_packet,
  stun, stun_packet

These debug categories can be enable/disable via an Asterisk CLI command.

While this overrides, and outputs debug data, core system debugging is
not affected by this patch. Statements still output at their appropriate
debug level. As well backwards compatibility has been maintained with
past debug groups that could be enabled using the CLI (e.g. rtpdebug,
stundebug, etc.).

ASTERISK-29054 #close

Change-Id: I6e6cb247bb1f01dbf34750b2cd98e5b5b41a1849
2020-10-02 12:58:18 -05:00

325 lines
7.7 KiB
C

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2020, Sangoma Technologies Corporation
*
* Kevin Harwell <kharwell@sangoma.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#include "asterisk.h"
#include "asterisk/cli.h"
#include "asterisk/conversions.h"
#include "asterisk/logger_category.h"
#include "asterisk/vector.h"
struct category_t {
int sublevel;
uintmax_t id;
char name[0];
};
AST_VECTOR_RW(categories_t, struct category_t *);
struct categories_level_t {
int type;
int sublevel;
uintmax_t id_pool;
uintmax_t state;
struct categories_t categories;
};
/*! \brief Retrieve the next available id.
*
* Ids must be a power of 2. This allows for fast lookup, and "or'ing" of ids
* in order to permit multiple categories in a log statement.
*/
static uintmax_t get_next_id(struct categories_level_t *level)
{
if (level->id_pool == 0) {
level->id_pool = 1;
} else if (level->id_pool >= (UINTMAX_MAX / 2)) {
/* No more ids left*/
return 0;
} else {
level->id_pool <<= 1;
}
return level->id_pool;
}
static int cmp_by_name(const struct category_t *category, const char *name)
{
return !strcmp(category->name, name);
}
static uintmax_t category_register(struct categories_level_t *level, const char *name)
{
int i;
struct category_t *category;
AST_VECTOR_RW_WRLOCK(&level->categories);
i = AST_VECTOR_GET_INDEX(&level->categories, name, cmp_by_name);
if (i >= 0) {
AST_VECTOR_RW_UNLOCK(&level->categories);
ast_log(LOG_ERROR, "Cannot register logger category '%s'. "
"Name already used for type.\n", name);
return 0;
}
category = ast_calloc(1, sizeof(*category) + strlen(name) + 1);
if (!category) {
AST_VECTOR_RW_UNLOCK(&level->categories);
return 0;
}
category->id = get_next_id(level);
category->sublevel = AST_LOG_CATEGORY_DISABLED;
strcpy(category->name, name); /* Safe */
if (AST_VECTOR_APPEND(&level->categories, category)) {
AST_VECTOR_RW_UNLOCK(&level->categories);
ast_log(LOG_ERROR, "Cannot register logger category '%s'. "
"Unable to append.\n", name);
return 0;
}
AST_VECTOR_RW_UNLOCK(&level->categories);
return category->id;
}
static int category_unregister(struct categories_level_t *level, const char *name)
{
int res;
AST_VECTOR_RW_WRLOCK(&level->categories);
res = AST_VECTOR_REMOVE_CMP_UNORDERED(&level->categories, name, cmp_by_name, ast_free);
AST_VECTOR_RW_UNLOCK(&level->categories);
return res;
}
static int casecmp_by_name(const struct category_t *category, const char *name)
{
return !strcasecmp(category->name, name);
}
static int category_set_sublevel(struct category_t *category, struct categories_level_t *level,
const char *name, int sublevel)
{
int locked = 0;
if (!category) {
struct category_t **obj;
if (!name) {
return -1;
}
locked = !AST_VECTOR_RW_WRLOCK(&level->categories);
if (!locked) {
return -1;
}
obj = AST_VECTOR_GET_CMP(&level->categories, name, casecmp_by_name);
if (!obj) {
AST_VECTOR_RW_UNLOCK(&level->categories);
return -1;
}
category = *obj;
}
category->sublevel = sublevel;
if (category->sublevel == AST_LOG_CATEGORY_DISABLED) {
level->state &= ~category->id;
} else {
level->state |= category->id;
}
if (locked) {
AST_VECTOR_RW_UNLOCK(&level->categories);
}
return 0;
}
static int category_set_sublevels(struct categories_level_t *level,
const char * const *names, size_t size, int default_sublevel)
{
int i;
if (!names || !size) {
level->state = default_sublevel;
AST_VECTOR_RW_WRLOCK(&level->categories);
AST_VECTOR_CALLBACK_VOID(&level->categories, category_set_sublevel,
level, NULL, default_sublevel);
AST_VECTOR_RW_UNLOCK(&level->categories);
return 0;
}
for (i = 0; i < size; ++i) {
const char *sublevel;
int num = default_sublevel;
sublevel = strchr(names[i], ':');
if (sublevel) {
size_t len = ++sublevel - names[i];
char name[len];
if (*sublevel && ast_str_to_int(sublevel, &num)) {
continue;
}
ast_copy_string(name, names[i], len);
category_set_sublevel(NULL, level, name, num);
} else {
category_set_sublevel(NULL, level, names[i], num);
}
}
return 0;
}
static char *category_complete(struct categories_level_t *level, const char * const *argv,
int argc, const char *word, int state)
{
int wordlen = strlen(word);
int which = 0;
int i, j;
AST_VECTOR_RW_RDLOCK(&level->categories);
if (argc == AST_VECTOR_SIZE(&level->categories)) {
AST_VECTOR_RW_UNLOCK(&level->categories);
return NULL;
}
for (i = 0; i < AST_VECTOR_SIZE(&level->categories); ++i) {
struct category_t *category = AST_VECTOR_GET(&level->categories, i);
if (!strncasecmp(word, category->name, wordlen) && (++which > state)) {
/* Check to see if one is already been included */
for (j = 0; j < argc; ++j) {
if (!strncasecmp(category->name, argv[j], strlen(category->name))) {
break;
}
}
if (j != argc) {
continue;
}
if (state != -1) {
AST_VECTOR_RW_UNLOCK(&level->categories);
return ast_strdup(category->name);
}
if (ast_cli_completion_add(ast_strdup(category->name))) {
AST_VECTOR_RW_UNLOCK(&level->categories);
return NULL;
}
}
}
AST_VECTOR_RW_UNLOCK(&level->categories);
return NULL;
}
static int category_is_allowed(int sublevel, struct categories_level_t *level, uintmax_t ids)
{
/* Check level, and potentially allow but only if there is a matching state enabled */
if (ids & level->state) {
int i;
if (sublevel == AST_LOG_CATEGORY_ENABLED || sublevel == 0) {
/* If at least one id is enabled then always allow these sublevels */
return 1;
}
AST_VECTOR_RW_RDLOCK(&level->categories);
for (i = 0; i < AST_VECTOR_SIZE(&level->categories); ++i) {
struct category_t *category = AST_VECTOR_GET(&level->categories, i);
/*
* If there is at least one matching category, and that category is enabled
* or its sub-level is at or above the given sub-level then allow.
*/
if ((category->id & ids) && category->sublevel != AST_LOG_CATEGORY_DISABLED &&
(category->sublevel == AST_LOG_CATEGORY_ENABLED || category->sublevel >= sublevel)) {
AST_VECTOR_RW_UNLOCK(&level->categories);
return 1;
}
}
AST_VECTOR_RW_UNLOCK(&level->categories);
}
return 0;
}
static struct categories_level_t debug_categories = {
.type = __LOG_DEBUG,
.sublevel = 0,
.id_pool = 0,
.state = 0,
};
uintmax_t ast_debug_category_register(const char *name)
{
return category_register(&debug_categories, name);
}
int ast_debug_category_unregister(const char *name)
{
return category_unregister(&debug_categories, name);
}
int ast_debug_category_set_sublevel(const char *name, int sublevel)
{
return category_set_sublevel(NULL, &debug_categories, name, sublevel);
}
int ast_debug_category_set_sublevels(const char * const *names,
size_t size, int default_sublevel)
{
return category_set_sublevels(&debug_categories, names, size, default_sublevel);
}
char *ast_debug_category_complete(const char * const *argv, int argc,
const char *word, int state)
{
return category_complete(&debug_categories, argv, argc, word, state);
}
int ast_debug_category_is_allowed(int sublevel, uintmax_t ids)
{
return category_is_allowed(sublevel, &debug_categories, ids);
}
int ast_logger_category_unload(void)
{
AST_VECTOR_RW_FREE(&debug_categories.categories);
return 0;
}
int ast_logger_category_load(void)
{
if (AST_VECTOR_RW_INIT(&debug_categories.categories, 10)) {
return -1;
}
return 0;
}