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