-
Notifications
You must be signed in to change notification settings - Fork 158
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move base log interface up into common (#416)
- Loading branch information
1 parent
f674342
commit ba1a782
Showing
15 changed files
with
953 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
#ifndef AWS_COMMON_LOGGING_H | ||
#define AWS_COMMON_LOGGING_H | ||
|
||
/* | ||
* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* A copy of the License is located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed | ||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
#include <aws/common/common.h> | ||
|
||
#define AWS_LOG_LEVEL_NONE 0 | ||
#define AWS_LOG_LEVEL_FATAL 1 | ||
#define AWS_LOG_LEVEL_ERROR 2 | ||
#define AWS_LOG_LEVEL_WARN 3 | ||
#define AWS_LOG_LEVEL_INFO 4 | ||
#define AWS_LOG_LEVEL_DEBUG 5 | ||
#define AWS_LOG_LEVEL_TRACE 6 | ||
|
||
/** | ||
* Controls what log calls pass through the logger and what log calls get filtered out. | ||
* If a log level has a value of X, then all log calls using a level <= X will appear, while | ||
* those using a value > X will not occur. | ||
* | ||
* You can filter both dynamically (by setting the log level on the logger object) or statically | ||
* (by defining AWS_STATIC_LOG_LEVEL to be an appropriate integer module-wide). Statically filtered | ||
* log calls will be completely compiled out but require a rebuild if you want to get more detail | ||
* about what's happening. | ||
*/ | ||
enum aws_log_level { | ||
AWS_LL_NONE = AWS_LOG_LEVEL_NONE, | ||
AWS_LL_FATAL = AWS_LOG_LEVEL_FATAL, | ||
AWS_LL_ERROR = AWS_LOG_LEVEL_ERROR, | ||
AWS_LL_WARN = AWS_LOG_LEVEL_WARN, | ||
AWS_LL_INFO = AWS_LOG_LEVEL_INFO, | ||
AWS_LL_DEBUG = AWS_LOG_LEVEL_DEBUG, | ||
AWS_LL_TRACE = AWS_LOG_LEVEL_TRACE, | ||
|
||
AWS_LL_COUNT | ||
}; | ||
|
||
/** | ||
* Log subject is a way of designating the topic of logging. | ||
* | ||
* The general idea is to support a finer-grained approach to log level control. The primary use case | ||
* is for situations that require more detailed logging within a specific domain, where enabling that detail | ||
* globally leads to an untenable flood of information. | ||
* | ||
* For example, enable TRACE logging for tls-related log statements (handshake binary payloads), but | ||
* only WARN logging everywhere else (because http payloads would blow up the log files). | ||
* | ||
* Log subject is an enum similar to aws error: each library has its own value-space and someone is | ||
* responsible for registering the value <-> string connections. | ||
*/ | ||
typedef uint32_t aws_log_subject_t; | ||
|
||
#define AWS_LOG_SUBJECT_BIT_SPACE 10 | ||
#define AWS_LOG_SUBJECT_SPACE_SIZE (1 << AWS_LOG_SUBJECT_BIT_SPACE) | ||
#define AWS_LOG_SUBJECT_SPACE_MASK (AWS_LOG_SUBJECT_SPACE_SIZE - 1) | ||
|
||
struct aws_log_subject_info { | ||
aws_log_subject_t subject_id; | ||
const char *subject_name; | ||
const char *subject_description; | ||
}; | ||
|
||
#define DEFINE_LOG_SUBJECT_INFO(id, name, desc) \ | ||
{ .subject_id = (id), .subject_name = (name), .subject_description = (desc) } | ||
|
||
struct aws_log_subject_info_list { | ||
struct aws_log_subject_info *subject_list; | ||
size_t count; | ||
}; | ||
|
||
enum aws_common_log_subject { | ||
AWS_LS_COMMON_GENERAL = 0, | ||
AWS_LS_COMMON_TASK_SCHEDULER, | ||
|
||
AWS_LS_COMMON_LAST = (AWS_LS_COMMON_GENERAL + AWS_LOG_SUBJECT_SPACE_SIZE - 1) | ||
}; | ||
|
||
struct aws_logger; | ||
|
||
/** | ||
* We separate the log level function from the log call itself so that we can do the filter check in the macros (see | ||
* below) | ||
* | ||
* By doing so, we make it so that the variadic format arguments are not even evaluated if the filter check does not | ||
* succeed. | ||
*/ | ||
struct aws_logger_vtable { | ||
int (*const log)( | ||
struct aws_logger *logger, | ||
enum aws_log_level log_level, | ||
aws_log_subject_t subject, | ||
const char *format, | ||
...) | ||
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) | ||
__attribute__((format(printf, 4, 5))) | ||
#endif /* non-ms compilers: TODO - find out what versions format support was added in */ | ||
; | ||
enum aws_log_level (*const get_log_level)(struct aws_logger *logger, aws_log_subject_t subject); | ||
void (*const clean_up)(struct aws_logger *logger); | ||
}; | ||
|
||
struct aws_logger { | ||
struct aws_logger_vtable *vtable; | ||
struct aws_allocator *allocator; | ||
void *p_impl; | ||
}; | ||
|
||
/** | ||
* The base formatted logging macro that all other formatted logging macros resolve to. | ||
* Checks for a logger and filters based on log level. | ||
* | ||
*/ | ||
#define AWS_LOGF(log_level, subject, ...) \ | ||
{ \ | ||
AWS_ASSERT(log_level > 0); \ | ||
struct aws_logger *logger = aws_logger_get(); \ | ||
if (logger != NULL && logger->vtable->get_log_level(logger, (subject)) >= (log_level)) { \ | ||
logger->vtable->log(logger, log_level, subject, __VA_ARGS__); \ | ||
} \ | ||
} | ||
|
||
/** | ||
* LOGF_<level> variants for each level. These are what should be used directly to do all logging. | ||
* | ||
* i.e. | ||
* | ||
* LOGF_FATAL("Device \"%s\" not found", device->name); | ||
* | ||
* | ||
* Later we will likely expose Subject-aware variants | ||
*/ | ||
#if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_FATAL) | ||
# define AWS_LOGF_FATAL(subject, ...) AWS_LOGF(AWS_LL_FATAL, subject, __VA_ARGS__) | ||
#else | ||
# define AWS_LOGF_FATAL(subject, ...) | ||
#endif | ||
|
||
#if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_ERROR) | ||
# define AWS_LOGF_ERROR(subject, ...) AWS_LOGF(AWS_LL_ERROR, subject, __VA_ARGS__) | ||
#else | ||
# define AWS_LOGF_ERROR(subject, ...) | ||
#endif | ||
|
||
#if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_WARN) | ||
# define AWS_LOGF_WARN(subject, ...) AWS_LOGF(AWS_LL_WARN, subject, __VA_ARGS__) | ||
#else | ||
# define AWS_LOGF_WARN(subject, ...) | ||
#endif | ||
|
||
#if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_INFO) | ||
# define AWS_LOGF_INFO(subject, ...) AWS_LOGF(AWS_LL_INFO, subject, __VA_ARGS__) | ||
#else | ||
# define AWS_LOGF_INFO(subject, ...) | ||
#endif | ||
|
||
#if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_DEBUG) | ||
# define AWS_LOGF_DEBUG(subject, ...) AWS_LOGF(AWS_LL_DEBUG, subject, __VA_ARGS__) | ||
#else | ||
# define AWS_LOGF_DEBUG(subject, ...) | ||
#endif | ||
|
||
#if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_TRACE) | ||
# define AWS_LOGF_TRACE(subject, ...) AWS_LOGF(AWS_LL_TRACE, subject, __VA_ARGS__) | ||
#else | ||
# define AWS_LOGF_TRACE(subject, ...) | ||
#endif | ||
|
||
AWS_EXTERN_C_BEGIN | ||
|
||
/** | ||
* Sets the aws logger used globally across the process. Not thread-safe. Must only be called once. | ||
*/ | ||
AWS_COMMON_API | ||
void aws_logger_set(struct aws_logger *logger); | ||
|
||
/** | ||
* Gets the aws logger used globally across the process. | ||
*/ | ||
AWS_COMMON_API | ||
struct aws_logger *aws_logger_get(void); | ||
|
||
/** | ||
* Cleans up all resources used by the logger; simply invokes the clean_up v-function | ||
*/ | ||
AWS_COMMON_API | ||
void aws_logger_clean_up(struct aws_logger *logger); | ||
|
||
/** | ||
* Converts a log level to a c-string constant. Intended primarily to support building log lines that | ||
* include the level in them, i.e. | ||
* | ||
* [ERROR] 10:34:54.642 01-31-19 - Json parse error.... | ||
*/ | ||
AWS_COMMON_API | ||
int aws_log_level_to_string(enum aws_log_level log_level, const char **level_string); | ||
|
||
/** | ||
* Get subject name from log subject. | ||
*/ | ||
AWS_COMMON_API | ||
const char *aws_log_subject_name(aws_log_subject_t subject); | ||
|
||
/** | ||
* Connects log subject strings with log subject integer values | ||
*/ | ||
AWS_COMMON_API | ||
void aws_register_log_subject_info_list(struct aws_log_subject_info_list *log_subject_list); | ||
|
||
/** | ||
* Load aws-c-commons's log subject strings. | ||
*/ | ||
AWS_COMMON_API | ||
void aws_common_load_log_subject_strings(void); | ||
|
||
AWS_EXTERN_C_END | ||
|
||
#endif /* AWS_COMMON_LOGGING_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/* | ||
* Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* A copy of the License is located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed | ||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
#include <aws/common/logging.h> | ||
|
||
static enum aws_log_level s_null_logger_get_log_level(struct aws_logger *logger, aws_log_subject_t subject) { | ||
(void)logger; | ||
(void)subject; | ||
|
||
return AWS_LL_NONE; | ||
} | ||
|
||
static int s_null_logger_log( | ||
struct aws_logger *logger, | ||
enum aws_log_level log_level, | ||
aws_log_subject_t subject, | ||
const char *format, | ||
...) { | ||
|
||
(void)logger; | ||
(void)log_level; | ||
(void)subject; | ||
(void)format; | ||
|
||
return AWS_OP_SUCCESS; | ||
} | ||
|
||
static void s_null_logger_clean_up(struct aws_logger *logger) { | ||
(void)logger; | ||
} | ||
|
||
static struct aws_logger_vtable s_null_vtable = { | ||
.get_log_level = s_null_logger_get_log_level, | ||
.log = s_null_logger_log, | ||
.clean_up = s_null_logger_clean_up, | ||
}; | ||
|
||
static struct aws_logger s_null_logger = {.vtable = &s_null_vtable, .allocator = NULL, .p_impl = NULL}; | ||
|
||
static struct aws_logger *s_root_logger_ptr = &s_null_logger; | ||
|
||
void aws_logger_set(struct aws_logger *logger) { | ||
if (logger != NULL) { | ||
s_root_logger_ptr = logger; | ||
} else { | ||
s_root_logger_ptr = &s_null_logger; | ||
} | ||
} | ||
|
||
struct aws_logger *aws_logger_get(void) { | ||
return s_root_logger_ptr; | ||
} | ||
|
||
void aws_logger_clean_up(struct aws_logger *logger) { | ||
AWS_ASSERT(logger->vtable->clean_up != NULL); | ||
|
||
logger->vtable->clean_up(logger); | ||
} | ||
|
||
static const char *s_log_level_strings[AWS_LL_COUNT] = {"NONE ", "FATAL", "ERROR", "WARN ", "INFO ", "DEBUG", "TRACE"}; | ||
|
||
int aws_log_level_to_string(enum aws_log_level log_level, const char **level_string) { | ||
if (log_level >= AWS_LL_COUNT) { | ||
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); | ||
} | ||
|
||
if (level_string != NULL) { | ||
*level_string = s_log_level_strings[log_level]; | ||
} | ||
|
||
return AWS_OP_SUCCESS; | ||
} | ||
|
||
#ifndef AWS_MAX_LOG_SUBJECT_SLOTS | ||
# define AWS_MAX_LOG_SUBJECT_SLOTS 16u | ||
#endif | ||
|
||
static const uint32_t S_MAX_LOG_SUBJECT = AWS_LOG_SUBJECT_SPACE_SIZE * AWS_MAX_LOG_SUBJECT_SLOTS - 1; | ||
|
||
static const struct aws_log_subject_info_list *volatile s_log_subject_slots[AWS_MAX_LOG_SUBJECT_SLOTS] = {0}; | ||
|
||
static const struct aws_log_subject_info *s_get_log_subject_info_by_id(aws_log_subject_t subject) { | ||
if (subject > S_MAX_LOG_SUBJECT) { | ||
return NULL; | ||
} | ||
|
||
uint32_t slot_index = subject >> AWS_LOG_SUBJECT_BIT_SPACE; | ||
uint32_t subject_index = subject & AWS_LOG_SUBJECT_SPACE_MASK; | ||
|
||
const struct aws_log_subject_info_list *subject_slot = s_log_subject_slots[slot_index]; | ||
|
||
if (!subject_slot || subject_index >= subject_slot->count) { | ||
return NULL; | ||
} | ||
|
||
return &subject_slot->subject_list[subject_index]; | ||
} | ||
|
||
const char *aws_log_subject_name(aws_log_subject_t subject) { | ||
const struct aws_log_subject_info *subject_info = s_get_log_subject_info_by_id(subject); | ||
|
||
if (subject_info != NULL) { | ||
return subject_info->subject_name; | ||
} | ||
|
||
return "Unknown"; | ||
} | ||
|
||
void aws_register_log_subject_info_list(struct aws_log_subject_info_list *log_subject_list) { | ||
(void)log_subject_list; | ||
|
||
/* | ||
* We're not so worried about these asserts being removed in an NDEBUG build | ||
* - we'll either segfault immediately (for the first two) or for the count | ||
* assert, the registration will be ineffective. | ||
*/ | ||
AWS_ASSERT(log_subject_list); | ||
AWS_ASSERT(log_subject_list->subject_list); | ||
AWS_ASSERT(log_subject_list->count); | ||
|
||
uint32_t min_range = log_subject_list->subject_list[0].subject_id; | ||
|
||
uint32_t slot_index = min_range >> AWS_LOG_SUBJECT_BIT_SPACE; | ||
|
||
AWS_ASSERT(slot_index < AWS_MAX_LOG_SUBJECT_SLOTS); | ||
|
||
if (slot_index >= AWS_MAX_LOG_SUBJECT_SLOTS) { | ||
/* This is an NDEBUG build apparently. Kill the process rather than | ||
* corrupting heap. */ | ||
fprintf(stderr, "Bad log subject slot index 0x%016x\n", slot_index); | ||
abort(); | ||
} | ||
|
||
s_log_subject_slots[slot_index] = log_subject_list; | ||
} | ||
|
||
static struct aws_log_subject_info s_common_log_subject_infos[] = { | ||
DEFINE_LOG_SUBJECT_INFO( | ||
AWS_LS_COMMON_GENERAL, | ||
"aws-c-common", | ||
"Subject for aws-c-common logging that doesn't belong to any particular category"), | ||
DEFINE_LOG_SUBJECT_INFO( | ||
AWS_LS_COMMON_TASK_SCHEDULER, | ||
"task-scheduler", | ||
"Subject for task scheduler or task specific logging."), | ||
}; | ||
|
||
static struct aws_log_subject_info_list s_common_log_subject_list = { | ||
.subject_list = s_common_log_subject_infos, | ||
.count = AWS_ARRAY_SIZE(s_common_log_subject_infos), | ||
}; | ||
|
||
void aws_common_load_log_subject_strings(void) { | ||
aws_register_log_subject_info_list(&s_common_log_subject_list); | ||
} |
Oops, something went wrong.