1#ifndef AWS_COMMON_LOGGING_H
2#define AWS_COMMON_LOGGING_H
3
4/*
5 * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License").
8 * You may not use this file except in compliance with the License.
9 * A copy of the License is located at
10 *
11 * http://aws.amazon.com/apache2.0
12 *
13 * or in the "license" file accompanying this file. This file is distributed
14 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
15 * express or implied. See the License for the specific language governing
16 * permissions and limitations under the License.
17 */
18
19#include <aws/common/common.h>
20
21#define AWS_LOG_LEVEL_NONE 0
22#define AWS_LOG_LEVEL_FATAL 1
23#define AWS_LOG_LEVEL_ERROR 2
24#define AWS_LOG_LEVEL_WARN 3
25#define AWS_LOG_LEVEL_INFO 4
26#define AWS_LOG_LEVEL_DEBUG 5
27#define AWS_LOG_LEVEL_TRACE 6
28
29/**
30 * Controls what log calls pass through the logger and what log calls get filtered out.
31 * If a log level has a value of X, then all log calls using a level <= X will appear, while
32 * those using a value > X will not occur.
33 *
34 * You can filter both dynamically (by setting the log level on the logger object) or statically
35 * (by defining AWS_STATIC_LOG_LEVEL to be an appropriate integer module-wide). Statically filtered
36 * log calls will be completely compiled out but require a rebuild if you want to get more detail
37 * about what's happening.
38 */
39enum aws_log_level {
40 AWS_LL_NONE = AWS_LOG_LEVEL_NONE,
41 AWS_LL_FATAL = AWS_LOG_LEVEL_FATAL,
42 AWS_LL_ERROR = AWS_LOG_LEVEL_ERROR,
43 AWS_LL_WARN = AWS_LOG_LEVEL_WARN,
44 AWS_LL_INFO = AWS_LOG_LEVEL_INFO,
45 AWS_LL_DEBUG = AWS_LOG_LEVEL_DEBUG,
46 AWS_LL_TRACE = AWS_LOG_LEVEL_TRACE,
47
48 AWS_LL_COUNT
49};
50
51/**
52 * Log subject is a way of designating the topic of logging.
53 *
54 * The general idea is to support a finer-grained approach to log level control. The primary use case
55 * is for situations that require more detailed logging within a specific domain, where enabling that detail
56 * globally leads to an untenable flood of information.
57 *
58 * For example, enable TRACE logging for tls-related log statements (handshake binary payloads), but
59 * only WARN logging everywhere else (because http payloads would blow up the log files).
60 *
61 * Log subject is an enum similar to aws error: each library has its own value-space and someone is
62 * responsible for registering the value <-> string connections.
63 */
64typedef uint32_t aws_log_subject_t;
65
66#define AWS_LOG_SUBJECT_BIT_SPACE 10
67#define AWS_LOG_SUBJECT_SPACE_SIZE (1 << AWS_LOG_SUBJECT_BIT_SPACE)
68#define AWS_LOG_SUBJECT_SPACE_MASK (AWS_LOG_SUBJECT_SPACE_SIZE - 1)
69
70struct aws_log_subject_info {
71 aws_log_subject_t subject_id;
72 const char *subject_name;
73 const char *subject_description;
74};
75
76#define DEFINE_LOG_SUBJECT_INFO(id, name, desc) \
77 { .subject_id = (id), .subject_name = (name), .subject_description = (desc) }
78
79struct aws_log_subject_info_list {
80 struct aws_log_subject_info *subject_list;
81 size_t count;
82};
83
84enum aws_common_log_subject {
85 AWS_LS_COMMON_GENERAL = 0,
86 AWS_LS_COMMON_TASK_SCHEDULER,
87 AWS_LS_COMMON_MEMTRACE,
88
89 AWS_LS_COMMON_LAST = (AWS_LS_COMMON_GENERAL + AWS_LOG_SUBJECT_SPACE_SIZE - 1)
90};
91
92struct aws_logger;
93struct aws_log_formatter;
94struct aws_log_channel;
95struct aws_log_writer;
96
97/**
98 * We separate the log level function from the log call itself so that we can do the filter check in the macros (see
99 * below)
100 *
101 * By doing so, we make it so that the variadic format arguments are not even evaluated if the filter check does not
102 * succeed.
103 */
104struct aws_logger_vtable {
105 int (*const log)(
106 struct aws_logger *logger,
107 enum aws_log_level log_level,
108 aws_log_subject_t subject,
109 const char *format,
110 ...)
111#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
112 __attribute__((format(printf, 4, 5)))
113#endif /* non-ms compilers: TODO - find out what versions format support was added in */
114 ;
115 enum aws_log_level (*const get_log_level)(struct aws_logger *logger, aws_log_subject_t subject);
116 void (*const clean_up)(struct aws_logger *logger);
117};
118
119struct 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 */
192struct 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 enum aws_log_level 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 */
205struct aws_logger_standard_options {
206 enum aws_log_level level;
207 const char *filename;
208 FILE *file;
209};
210
211AWS_EXTERN_C_BEGIN
212
213/**
214 * Sets the aws logger used globally across the process. Not thread-safe. Must only be called once.
215 */
216AWS_COMMON_API
217void aws_logger_set(struct aws_logger *logger);
218
219/**
220 * Gets the aws logger used globally across the process.
221 */
222AWS_COMMON_API
223struct 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 */
228AWS_COMMON_API
229void aws_logger_clean_up(struct aws_logger *logger);
230
231/**
232 * Converts a log level to a c-string constant. Intended primarily to support building log lines that
233 * include the level in them, i.e.
234 *
235 * [ERROR] 10:34:54.642 01-31-19 - Json parse error....
236 */
237AWS_COMMON_API
238int aws_log_level_to_string(enum aws_log_level log_level, const char **level_string);
239
240/**
241 * Get subject name from log subject.
242 */
243AWS_COMMON_API
244const char *aws_log_subject_name(aws_log_subject_t subject);
245
246/**
247 * Connects log subject strings with log subject integer values
248 */
249AWS_COMMON_API
250void aws_register_log_subject_info_list(struct aws_log_subject_info_list *log_subject_list);
251
252/**
253 * Disconnects log subject strings with log subject integer values
254 */
255AWS_COMMON_API
256void aws_unregister_log_subject_info_list(struct aws_log_subject_info_list *log_subject_list);
257
258/*
259 * Initializes a pipeline logger that is built from the default formatter, a background thread-based channel, and
260 * a file writer. The default logger in almost all circumstances.
261 */
262AWS_COMMON_API
263int aws_logger_init_standard(
264 struct aws_logger *logger,
265 struct aws_allocator *allocator,
266 struct aws_logger_standard_options *options);
267
268/*
269 * Initializes a pipeline logger from components that have already been initialized. This is not an ownership transfer.
270 * After the pipeline logger is cleaned up, the components will have to manually be cleaned up by the user.
271 */
272AWS_COMMON_API
273int aws_logger_init_from_external(
274 struct aws_logger *logger,
275 struct aws_allocator *allocator,
276 struct aws_log_formatter *formatter,
277 struct aws_log_channel *channel,
278 struct aws_log_writer *writer,
279 enum aws_log_level level);
280
281/*
282 * Pipeline logger vtable for custom configurations
283 */
284AWS_COMMON_API
285extern struct aws_logger_vtable g_pipeline_logger_owned_vtable;
286
287/*
288 * Initializes a logger that does not perform any allocation during logging. Log lines larger than the internal
289 * constant are truncated. Formatting matches the standard logger. Used for memory tracing logging.
290 */
291AWS_COMMON_API
292int aws_logger_init_noalloc(
293 struct aws_logger *logger,
294 struct aws_allocator *allocator,
295 struct aws_logger_standard_options *options);
296
297AWS_EXTERN_C_END
298
299#endif /* AWS_COMMON_LOGGING_H */
300