Line data Source code
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 0 : #define AWS_LOG_SUBJECT_STRIDE_BITS 10
60 0 : #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 :
85 : AWS_LS_COMMON_LAST = AWS_LOG_SUBJECT_END_RANGE(AWS_C_COMMON_PACKAGE_ID)
86 : };
87 :
88 : struct aws_logger;
89 : struct aws_log_formatter;
90 : struct aws_log_channel;
91 : struct aws_log_writer;
92 :
93 : /**
94 : * We separate the log level function from the log call itself so that we can do the filter check in the macros (see
95 : * below)
96 : *
97 : * By doing so, we make it so that the variadic format arguments are not even evaluated if the filter check does not
98 : * succeed.
99 : */
100 : struct aws_logger_vtable {
101 : int (*const log)(
102 : struct aws_logger *logger,
103 : enum aws_log_level log_level,
104 : aws_log_subject_t subject,
105 : const char *format,
106 : ...)
107 : #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
108 : __attribute__((format(printf, 4, 5)))
109 : #endif /* non-ms compilers: TODO - find out what versions format support was added in */
110 : ;
111 : enum aws_log_level (*const get_log_level)(struct aws_logger *logger, aws_log_subject_t subject);
112 : void (*const clean_up)(struct aws_logger *logger);
113 : int (*set_log_level)(struct aws_logger *logger, enum aws_log_level);
114 : };
115 :
116 : struct aws_logger {
117 : struct aws_logger_vtable *vtable;
118 : struct aws_allocator *allocator;
119 : void *p_impl;
120 : };
121 :
122 : /**
123 : * The base formatted logging macro that all other formatted logging macros resolve to.
124 : * Checks for a logger and filters based on log level.
125 : *
126 : */
127 : #define AWS_LOGF(log_level, subject, ...) \
128 0 : { \
129 0 : AWS_ASSERT(log_level > 0); \
130 0 : struct aws_logger *logger = aws_logger_get(); \
131 0 : if (logger != NULL && logger->vtable->get_log_level(logger, (subject)) >= (log_level)) { \
132 0 : logger->vtable->log(logger, log_level, subject, __VA_ARGS__); \
133 0 : } \
134 0 : }
135 :
136 : /**
137 : * LOGF_<level> variants for each level. These are what should be used directly to do all logging.
138 : *
139 : * i.e.
140 : *
141 : * LOGF_FATAL("Device \"%s\" not found", device->name);
142 : *
143 : *
144 : * Later we will likely expose Subject-aware variants
145 : */
146 : #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_FATAL)
147 : # define AWS_LOGF_FATAL(subject, ...) AWS_LOGF(AWS_LL_FATAL, subject, __VA_ARGS__)
148 : #else
149 : # define AWS_LOGF_FATAL(subject, ...)
150 : #endif
151 :
152 : #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_ERROR)
153 0 : # define AWS_LOGF_ERROR(subject, ...) AWS_LOGF(AWS_LL_ERROR, subject, __VA_ARGS__)
154 : #else
155 : # define AWS_LOGF_ERROR(subject, ...)
156 : #endif
157 :
158 : #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_WARN)
159 0 : # define AWS_LOGF_WARN(subject, ...) AWS_LOGF(AWS_LL_WARN, subject, __VA_ARGS__)
160 : #else
161 : # define AWS_LOGF_WARN(subject, ...)
162 : #endif
163 :
164 : #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_INFO)
165 0 : # define AWS_LOGF_INFO(subject, ...) AWS_LOGF(AWS_LL_INFO, subject, __VA_ARGS__)
166 : #else
167 : # define AWS_LOGF_INFO(subject, ...)
168 : #endif
169 :
170 : #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_DEBUG)
171 : # define AWS_LOGF_DEBUG(subject, ...) AWS_LOGF(AWS_LL_DEBUG, subject, __VA_ARGS__)
172 : #else
173 : # define AWS_LOGF_DEBUG(subject, ...)
174 : #endif
175 :
176 : #if !defined(AWS_STATIC_LOG_LEVEL) || (AWS_STATIC_LOG_LEVEL >= AWS_LOG_LEVEL_TRACE)
177 0 : # define AWS_LOGF_TRACE(subject, ...) AWS_LOGF(AWS_LL_TRACE, subject, __VA_ARGS__)
178 : #else
179 : # define AWS_LOGF_TRACE(subject, ...)
180 : #endif
181 :
182 : /*
183 : * Standard logger implementation composing three sub-components:
184 : *
185 : * The formatter takes var args input from the user and produces a formatted log line
186 : * The writer takes a formatted log line and outputs it somewhere
187 : * The channel is the transport between the two
188 : */
189 : struct aws_logger_pipeline {
190 : struct aws_log_formatter *formatter;
191 : struct aws_log_channel *channel;
192 : struct aws_log_writer *writer;
193 : struct aws_allocator *allocator;
194 : struct aws_atomic_var level;
195 : };
196 :
197 : /**
198 : * Options for aws_logger_init_standard().
199 : * Set `filename` to open a file for logging and close it when the logger cleans up.
200 : * Set `file` to use a file that is already open, such as `stderr` or `stdout`.
201 : */
202 : struct aws_logger_standard_options {
203 : enum aws_log_level level;
204 : const char *filename;
205 : FILE *file;
206 : };
207 :
208 : AWS_EXTERN_C_BEGIN
209 :
210 : /**
211 : * Sets the aws logger used globally across the process. Not thread-safe. Must only be called once.
212 : */
213 : AWS_COMMON_API
214 : void aws_logger_set(struct aws_logger *logger);
215 :
216 : /**
217 : * Gets the aws logger used globally across the process.
218 : */
219 : AWS_COMMON_API
220 : struct aws_logger *aws_logger_get(void);
221 :
222 : /**
223 : * Cleans up all resources used by the logger; simply invokes the clean_up v-function
224 : */
225 : AWS_COMMON_API
226 : void aws_logger_clean_up(struct aws_logger *logger);
227 :
228 : /**
229 : * Sets the current logging level for the logger. Loggers are not require to support this.
230 : * @param logger logger to set the log level for
231 : * @param level new log level for the logger
232 : * @return AWS_OP_SUCCESS if the level was successfully set, AWS_OP_ERR otherwise
233 : */
234 : AWS_COMMON_API
235 : int aws_logger_set_log_level(struct aws_logger *logger, enum aws_log_level level);
236 :
237 : /**
238 : * Converts a log level to a c-string constant. Intended primarily to support building log lines that
239 : * include the level in them, i.e.
240 : *
241 : * [ERROR] 10:34:54.642 01-31-19 - Json parse error....
242 : */
243 : AWS_COMMON_API
244 : int aws_log_level_to_string(enum aws_log_level log_level, const char **level_string);
245 :
246 : /**
247 : * Converts a c-string constant to a log level value. Uses case-insensitive comparison
248 : * and simply iterates all possibilities until a match or nothing remains. If no match
249 : * is found, AWS_OP_ERR is returned.
250 : */
251 : AWS_COMMON_API
252 : int aws_string_to_log_level(const char *level_string, enum aws_log_level *log_level);
253 :
254 : /**
255 : * Converts an aws_thread_id_t to a c-string. For portability, aws_thread_id_t
256 : * must not be printed directly. Intended primarily to support building log
257 : * lines that include the thread id in them. The parameter `buffer` must
258 : * point-to a char buffer of length `bufsz == AWS_THREAD_ID_T_REPR_BUFSZ`. The
259 : * thread id representation is returned in `buffer`.
260 : */
261 : AWS_COMMON_API
262 : int aws_thread_id_t_to_string(aws_thread_id_t thread_id, char *buffer, size_t bufsz);
263 :
264 : /**
265 : * Get subject name from log subject.
266 : */
267 : AWS_COMMON_API
268 : const char *aws_log_subject_name(aws_log_subject_t subject);
269 :
270 : /**
271 : * Connects log subject strings with log subject integer values
272 : */
273 : AWS_COMMON_API
274 : void aws_register_log_subject_info_list(struct aws_log_subject_info_list *log_subject_list);
275 :
276 : /**
277 : * Disconnects log subject strings with log subject integer values
278 : */
279 : AWS_COMMON_API
280 : void aws_unregister_log_subject_info_list(struct aws_log_subject_info_list *log_subject_list);
281 :
282 : /*
283 : * Initializes a pipeline logger that is built from the default formatter, a background thread-based channel, and
284 : * a file writer. The default logger in almost all circumstances.
285 : */
286 : AWS_COMMON_API
287 : int aws_logger_init_standard(
288 : struct aws_logger *logger,
289 : struct aws_allocator *allocator,
290 : struct aws_logger_standard_options *options);
291 :
292 : /*
293 : * Initializes a pipeline logger from components that have already been initialized. This is not an ownership transfer.
294 : * After the pipeline logger is cleaned up, the components will have to manually be cleaned up by the user.
295 : */
296 : AWS_COMMON_API
297 : int aws_logger_init_from_external(
298 : struct aws_logger *logger,
299 : struct aws_allocator *allocator,
300 : struct aws_log_formatter *formatter,
301 : struct aws_log_channel *channel,
302 : struct aws_log_writer *writer,
303 : enum aws_log_level level);
304 :
305 : /*
306 : * Pipeline logger vtable for custom configurations
307 : */
308 : AWS_COMMON_API
309 : extern struct aws_logger_vtable g_pipeline_logger_owned_vtable;
310 :
311 : /*
312 : * Initializes a logger that does not perform any allocation during logging. Log lines larger than the internal
313 : * constant are truncated. Formatting matches the standard logger. Used for memory tracing logging.
314 : */
315 : AWS_COMMON_API
316 : int aws_logger_init_noalloc(
317 : struct aws_logger *logger,
318 : struct aws_allocator *allocator,
319 : struct aws_logger_standard_options *options);
320 :
321 : AWS_EXTERN_C_END
322 :
323 : #endif /* AWS_COMMON_LOGGING_H */
|