1 #ifndef AWS_COMMON_LOGGING_H 2 #define AWS_COMMON_LOGGING_H 3 4 /** 5 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 6 * SPDX-License-Identifier: Apache-2.0. 7 */ 8 9 #include <aws/common/atomics.h> 10 #include <aws/common/common.h> 11 #include <aws/common/thread.h> 12 13 #define AWS_LOG_LEVEL_NONE 0 14 #define AWS_LOG_LEVEL_FATAL 1 15 #define AWS_LOG_LEVEL_ERROR 2 16 #define AWS_LOG_LEVEL_WARN 3 17 #define AWS_LOG_LEVEL_INFO 4 18 #define AWS_LOG_LEVEL_DEBUG 5 19 #define AWS_LOG_LEVEL_TRACE 6 20 21 /** 22 * Controls what log calls pass through the logger and what log calls get filtered out. 23 * If a log level has a value of X, then all log calls using a level <= X will appear, while 24 * those using a value > X will not occur. 25 * 26 * You can filter both dynamically (by setting the log level on the logger object) or statically 27 * (by defining AWS_STATIC_LOG_LEVEL to be an appropriate integer module-wide). Statically filtered 28 * log calls will be completely compiled out but require a rebuild if you want to get more detail 29 * about what's happening. 30 */ 31 enum aws_log_level { 32 AWS_LL_NONE = AWS_LOG_LEVEL_NONE, 33 AWS_LL_FATAL = AWS_LOG_LEVEL_FATAL, 34 AWS_LL_ERROR = AWS_LOG_LEVEL_ERROR, 35 AWS_LL_WARN = AWS_LOG_LEVEL_WARN, 36 AWS_LL_INFO = AWS_LOG_LEVEL_INFO, 37 AWS_LL_DEBUG = AWS_LOG_LEVEL_DEBUG, 38 AWS_LL_TRACE = AWS_LOG_LEVEL_TRACE, 39 40 AWS_LL_COUNT 41 }; 42 43 /** 44 * Log subject is a way of designating the topic of logging. 45 * 46 * The general idea is to support a finer-grained approach to log level control. The primary use case 47 * is for situations that require more detailed logging within a specific domain, where enabling that detail 48 * globally leads to an untenable flood of information. 49 * 50 * For example, enable TRACE logging for tls-related log statements (handshake binary payloads), but 51 * only WARN logging everywhere else (because http payloads would blow up the log files). 52 * 53 * Log subject is an enum similar to aws error: each library has its own value-space and someone is 54 * responsible for registering the value <-> string connections. 55 */ 56 typedef uint32_t aws_log_subject_t; 57 58 /* Each library gets space for 2^^10 log subject entries */ 59 #define AWS_LOG_SUBJECT_STRIDE_BITS 10 60 #define AWS_LOG_SUBJECT_STRIDE (1U << AWS_LOG_SUBJECT_STRIDE_BITS) 61 #define AWS_LOG_SUBJECT_BEGIN_RANGE(x) ((x)*AWS_LOG_SUBJECT_STRIDE) 62 #define AWS_LOG_SUBJECT_END_RANGE(x) (((x) + 1) * AWS_LOG_SUBJECT_STRIDE - 1) 63 64 struct aws_log_subject_info { 65 aws_log_subject_t subject_id; 66 const char *subject_name; 67 const char *subject_description; 68 }; 69 70 #define DEFINE_LOG_SUBJECT_INFO(id, name, desc) \ 71 { .subject_id = (id), .subject_name = (name), .subject_description = (desc) } 72 73 struct aws_log_subject_info_list { 74 struct aws_log_subject_info *subject_list; 75 size_t count; 76 }; 77 78 enum aws_common_log_subject { 79 AWS_LS_COMMON_GENERAL = AWS_LOG_SUBJECT_BEGIN_RANGE(AWS_C_COMMON_PACKAGE_ID), 80 AWS_LS_COMMON_TASK_SCHEDULER, 81 AWS_LS_COMMON_THREAD, 82 AWS_LS_COMMON_MEMTRACE, 83 AWS_LS_COMMON_XML_PARSER, 84 AWS_LS_COMMON_IO, 85 AWS_LS_COMMON_BUS, 86 AWS_LS_COMMON_TEST, 87 88 AWS_LS_COMMON_LAST = AWS_LOG_SUBJECT_END_RANGE(AWS_C_COMMON_PACKAGE_ID) 89 }; 90 91 struct aws_logger; 92 struct aws_log_formatter; 93 struct aws_log_channel; 94 struct aws_log_writer; 95 96 /** 97 * We separate the log level function from the log call itself so that we can do the filter check in the macros (see 98 * below) 99 * 100 * By doing so, we make it so that the variadic format arguments are not even evaluated if the filter check does not 101 * succeed. 102 */ 103 struct aws_logger_vtable { 104 int (*const log)( 105 struct aws_logger *logger, 106 enum aws_log_level log_level, 107 aws_log_subject_t subject, 108 const char *format, 109 ...) 110 #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) 111 __attribute__((format(printf, 4, 5))) 112 #endif /* non-ms compilers: TODO - find out what versions format support was added in */ 113 ; 114 enum aws_log_level (*const get_log_level)(struct aws_logger *logger, aws_log_subject_t subject); 115 void (*const clean_up)(struct aws_logger *logger); 116 int (*set_log_level)(struct aws_logger *logger, enum aws_log_level); 117 }; 118 119 struct aws_logger { 120 struct aws_logger_vtable *vtable; 121 struct aws_allocator *allocator; 122 void *p_impl; 123 }; 124 125 /** 126 * The base formatted logging macro that all other formatted logging macros resolve to. 127 * Checks for a logger and filters based on log level. 128 * 129 */ 130 #define AWS_LOGF(log_level, subject, ...) \ 131 { \ 132 AWS_ASSERT(log_level > 0); \ 133 struct aws_logger *logger = aws_logger_get(); \ 134 if (logger != NULL && logger->vtable->get_log_level(logger, (subject)) >= (log_level)) { \ 135 logger->vtable->log(logger, log_level, subject, __VA_ARGS__); \ 136 } \ 137 } 138 139 /** 140 * LOGF_<level> variants for each level. These are what should be used directly to do all logging. 141 * 142 * i.e. 143 * 144 * LOGF_FATAL("Device \"%s\" not found", device->name); 145 * 146 * 147 * Later we will likely expose Subject-aware variants 148 */ 149 #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_FATAL) 150 # define AWS_LOGF_FATAL(subject, ...) AWS_LOGF(AWS_LL_FATAL, subject, __VA_ARGS__) 151 #else 152 # define AWS_LOGF_FATAL(subject, ...) 153 #endif 154 155 #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_ERROR) 156 # define AWS_LOGF_ERROR(subject, ...) AWS_LOGF(AWS_LL_ERROR, subject, __VA_ARGS__) 157 #else 158 # define AWS_LOGF_ERROR(subject, ...) 159 #endif 160 161 #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_WARN) 162 # define AWS_LOGF_WARN(subject, ...) AWS_LOGF(AWS_LL_WARN, subject, __VA_ARGS__) 163 #else 164 # define AWS_LOGF_WARN(subject, ...) 165 #endif 166 167 #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_INFO) 168 # define AWS_LOGF_INFO(subject, ...) AWS_LOGF(AWS_LL_INFO, subject, __VA_ARGS__) 169 #else 170 # define AWS_LOGF_INFO(subject, ...) 171 #endif 172 173 #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_DEBUG) 174 # define AWS_LOGF_DEBUG(subject, ...) AWS_LOGF(AWS_LL_DEBUG, subject, __VA_ARGS__) 175 #else 176 # define AWS_LOGF_DEBUG(subject, ...) 177 #endif 178 179 #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_TRACE) 180 # define AWS_LOGF_TRACE(subject, ...) AWS_LOGF(AWS_LL_TRACE, subject, __VA_ARGS__) 181 #else 182 # define AWS_LOGF_TRACE(subject, ...) 183 #endif 184 185 /* 186 * Standard logger implementation composing three sub-components: 187 * 188 * The formatter takes var args input from the user and produces a formatted log line 189 * The writer takes a formatted log line and outputs it somewhere 190 * The channel is the transport between the two 191 */ 192 struct aws_logger_pipeline { 193 struct aws_log_formatter *formatter; 194 struct aws_log_channel *channel; 195 struct aws_log_writer *writer; 196 struct aws_allocator *allocator; 197 struct aws_atomic_var level; 198 }; 199 200 /** 201 * Options for aws_logger_init_standard(). 202 * Set `filename` to open a file for logging and close it when the logger cleans up. 203 * Set `file` to use a file that is already open, such as `stderr` or `stdout`. 204 */ 205 struct aws_logger_standard_options { 206 enum aws_log_level level; 207 const char *filename; 208 FILE *file; 209 }; 210 211 AWS_EXTERN_C_BEGIN 212 213 /** 214 * Sets the aws logger used globally across the process. Not thread-safe. Must only be called once. 215 */ 216 AWS_COMMON_API 217 void aws_logger_set(struct aws_logger *logger); 218 219 /** 220 * Gets the aws logger used globally across the process. 221 */ 222 AWS_COMMON_API 223 struct aws_logger *aws_logger_get(void); 224 225 /** 226 * Cleans up all resources used by the logger; simply invokes the clean_up v-function 227 */ 228 AWS_COMMON_API 229 void aws_logger_clean_up(struct aws_logger *logger); 230 231 /** 232 * Sets the current logging level for the logger. Loggers are not require to support this. 233 * @param logger logger to set the log level for 234 * @param level new log level for the logger 235 * @return AWS_OP_SUCCESS if the level was successfully set, AWS_OP_ERR otherwise 236 */ 237 AWS_COMMON_API 238 int aws_logger_set_log_level(struct aws_logger *logger, enum aws_log_level level); 239 240 /** 241 * Converts a log level to a c-string constant. Intended primarily to support building log lines that 242 * include the level in them, i.e. 243 * 244 * [ERROR] 10:34:54.642 01-31-19 - Json parse error.... 245 */ 246 AWS_COMMON_API 247 int aws_log_level_to_string(enum aws_log_level log_level, const char **level_string); 248 249 /** 250 * Converts a c-string constant to a log level value. Uses case-insensitive comparison 251 * and simply iterates all possibilities until a match or nothing remains. If no match 252 * is found, AWS_OP_ERR is returned. 253 */ 254 AWS_COMMON_API 255 int aws_string_to_log_level(const char *level_string, enum aws_log_level *log_level); 256 257 /** 258 * Converts an aws_thread_id_t to a c-string. For portability, aws_thread_id_t 259 * must not be printed directly. Intended primarily to support building log 260 * lines that include the thread id in them. The parameter `buffer` must 261 * point-to a char buffer of length `bufsz == AWS_THREAD_ID_T_REPR_BUFSZ`. The 262 * thread id representation is returned in `buffer`. 263 */ 264 AWS_COMMON_API 265 int aws_thread_id_t_to_string(aws_thread_id_t thread_id, char *buffer, size_t bufsz); 266 267 /** 268 * Get subject name from log subject. 269 */ 270 AWS_COMMON_API 271 const char *aws_log_subject_name(aws_log_subject_t subject); 272 273 /** 274 * Connects log subject strings with log subject integer values 275 */ 276 AWS_COMMON_API 277 void aws_register_log_subject_info_list(struct aws_log_subject_info_list *log_subject_list); 278 279 /** 280 * Disconnects log subject strings with log subject integer values 281 */ 282 AWS_COMMON_API 283 void aws_unregister_log_subject_info_list(struct aws_log_subject_info_list *log_subject_list); 284 285 /* 286 * Initializes a pipeline logger that is built from the default formatter, a background thread-based channel, and 287 * a file writer. The default logger in almost all circumstances. 288 */ 289 AWS_COMMON_API 290 int aws_logger_init_standard( 291 struct aws_logger *logger, 292 struct aws_allocator *allocator, 293 struct aws_logger_standard_options *options); 294 295 /* 296 * Initializes a pipeline logger from components that have already been initialized. This is not an ownership transfer. 297 * After the pipeline logger is cleaned up, the components will have to manually be cleaned up by the user. 298 */ 299 AWS_COMMON_API 300 int aws_logger_init_from_external( 301 struct aws_logger *logger, 302 struct aws_allocator *allocator, 303 struct aws_log_formatter *formatter, 304 struct aws_log_channel *channel, 305 struct aws_log_writer *writer, 306 enum aws_log_level level); 307 308 /* 309 * Pipeline logger vtable for custom configurations 310 */ 311 AWS_COMMON_API 312 extern struct aws_logger_vtable g_pipeline_logger_owned_vtable; 313 314 /* 315 * Initializes a logger that does not perform any allocation during logging. Log lines larger than the internal 316 * constant are truncated. Formatting matches the standard logger. Used for memory tracing logging. 317 */ 318 AWS_COMMON_API 319 int aws_logger_init_noalloc( 320 struct aws_logger *logger, 321 struct aws_allocator *allocator, 322 struct aws_logger_standard_options *options); 323 324 AWS_EXTERN_C_END 325 326 #endif /* AWS_COMMON_LOGGING_H */ 327