1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
|
#ifndef AWS_COMMON_LOGGING_H
#define AWS_COMMON_LOGGING_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/atomics.h>
#include <aws/common/common.h>
#include <aws/common/thread.h>
AWS_PUSH_SANE_WARNING_LEVEL
#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;
/* Each library gets space for 2^^10 log subject entries */
enum {
AWS_LOG_SUBJECT_STRIDE_BITS = 10,
};
#define AWS_LOG_SUBJECT_STRIDE (1U << AWS_LOG_SUBJECT_STRIDE_BITS)
#define AWS_LOG_SUBJECT_BEGIN_RANGE(x) ((x)*AWS_LOG_SUBJECT_STRIDE)
#define AWS_LOG_SUBJECT_END_RANGE(x) (((x) + 1) * AWS_LOG_SUBJECT_STRIDE - 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 = AWS_LOG_SUBJECT_BEGIN_RANGE(AWS_C_COMMON_PACKAGE_ID),
AWS_LS_COMMON_TASK_SCHEDULER,
AWS_LS_COMMON_THREAD,
AWS_LS_COMMON_MEMTRACE,
AWS_LS_COMMON_XML_PARSER,
AWS_LS_COMMON_IO,
AWS_LS_COMMON_BUS,
AWS_LS_COMMON_TEST,
AWS_LS_COMMON_JSON_PARSER,
AWS_LS_COMMON_LAST = AWS_LOG_SUBJECT_END_RANGE(AWS_C_COMMON_PACKAGE_ID)
};
struct aws_logger;
struct aws_log_formatter;
struct aws_log_channel;
struct aws_log_writer;
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4623) /* default constructor was implicitly defined as deleted */
# pragma warning(disable : 4626) /* assignment operator was implicitly defined as deleted */
# pragma warning(disable : 5027) /* move assignment operator was implicitly defined as deleted */
#endif
/**
* 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);
int (*set_log_level)(struct aws_logger *logger, enum aws_log_level);
};
#ifdef _MSC_VER
# pragma warning(pop)
#endif
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, ...) \
do { \
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__); \
} \
} while (0)
/**
* Unconditional logging macro that takes a logger and does not do a level check or a null check. Intended for
* situations when you need to log many things and do a single manual level check before beginning.
*/
#define AWS_LOGUF(logger, log_level, subject, ...) \
{ 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
/*
* Standard logger implementation composing three sub-components:
*
* The formatter takes var args input from the user and produces a formatted log line
* The writer takes a formatted log line and outputs it somewhere
* The channel is the transport between the two
*/
struct aws_logger_pipeline {
struct aws_log_formatter *formatter;
struct aws_log_channel *channel;
struct aws_log_writer *writer;
struct aws_allocator *allocator;
struct aws_atomic_var level;
};
/**
* Options for aws_logger_init_standard().
* Set `filename` to open a file for logging and close it when the logger cleans up.
* Set `file` to use a file that is already open, such as `stderr` or `stdout`.
*/
struct aws_logger_standard_options {
enum aws_log_level level;
const char *filename;
FILE *file;
};
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);
/**
* Gets the aws logger used globally across the process if the logging level is at least the inputted level.
*
* @param subject log subject to perform the level check versus, not currently used
* @param level logging level to check against in order to return the logger
* @return the current logger if the current logging level is at or more detailed then the supplied logging level
*/
AWS_COMMON_API
struct aws_logger *aws_logger_get_conditional(aws_log_subject_t subject, enum aws_log_level level);
/**
* 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);
/**
* Sets the current logging level for the logger. Loggers are not require to support this.
* @param logger logger to set the log level for
* @param level new log level for the logger
* @return AWS_OP_SUCCESS if the level was successfully set, AWS_OP_ERR otherwise
*/
AWS_COMMON_API
int aws_logger_set_log_level(struct aws_logger *logger, enum aws_log_level level);
/**
* 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);
/**
* Converts a c-string constant to a log level value. Uses case-insensitive comparison
* and simply iterates all possibilities until a match or nothing remains. If no match
* is found, AWS_OP_ERR is returned.
*/
AWS_COMMON_API
int aws_string_to_log_level(const char *level_string, enum aws_log_level *log_level);
/**
* Converts an aws_thread_id_t to a c-string. For portability, aws_thread_id_t
* must not be printed directly. Intended primarily to support building log
* lines that include the thread id in them. The parameter `buffer` must
* point-to a char buffer of length `bufsz == AWS_THREAD_ID_T_REPR_BUFSZ`. The
* thread id representation is returned in `buffer`.
*/
AWS_COMMON_API
int aws_thread_id_t_to_string(aws_thread_id_t thread_id, char *buffer, size_t bufsz);
/**
* 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);
/**
* Disconnects log subject strings with log subject integer values
*/
AWS_COMMON_API
void aws_unregister_log_subject_info_list(struct aws_log_subject_info_list *log_subject_list);
/*
* Initializes a pipeline logger that is built from the default formatter, a background thread-based channel, and
* a file writer. The default logger in almost all circumstances.
*/
AWS_COMMON_API
int aws_logger_init_standard(
struct aws_logger *logger,
struct aws_allocator *allocator,
struct aws_logger_standard_options *options);
/*
* Initializes a pipeline logger from components that have already been initialized. This is not an ownership transfer.
* After the pipeline logger is cleaned up, the components will have to manually be cleaned up by the user.
*/
AWS_COMMON_API
int aws_logger_init_from_external(
struct aws_logger *logger,
struct aws_allocator *allocator,
struct aws_log_formatter *formatter,
struct aws_log_channel *channel,
struct aws_log_writer *writer,
enum aws_log_level level);
/*
* Pipeline logger vtable for custom configurations
*/
AWS_COMMON_API
extern struct aws_logger_vtable g_pipeline_logger_owned_vtable;
/*
* Initializes a logger that does not perform any allocation during logging. Log lines larger than the internal
* constant are truncated. Formatting matches the standard logger. Used for memory tracing logging.
* If no file or filename is set in the aws_logger_standard_options, then it will use stderr.
*/
AWS_COMMON_API
int aws_logger_init_noalloc(
struct aws_logger *logger,
struct aws_allocator *allocator,
struct aws_logger_standard_options *options);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_COMMON_LOGGING_H */
|