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 | */ |
39 | enum 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 | */ |
64 | typedef 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 | |
70 | struct 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 | |
79 | struct aws_log_subject_info_list { |
80 | struct aws_log_subject_info *subject_list; |
81 | size_t count; |
82 | }; |
83 | |
84 | enum 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 | |
92 | struct aws_logger; |
93 | struct aws_log_formatter; |
94 | struct aws_log_channel; |
95 | struct 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 | */ |
104 | struct 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 | |
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 | 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 | */ |
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 | * 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 | */ |
237 | AWS_COMMON_API |
238 | int 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 | */ |
243 | AWS_COMMON_API |
244 | const char *aws_log_subject_name(aws_log_subject_t subject); |
245 | |
246 | /** |
247 | * Connects log subject strings with log subject integer values |
248 | */ |
249 | AWS_COMMON_API |
250 | void 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 | */ |
255 | AWS_COMMON_API |
256 | void 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 | */ |
262 | AWS_COMMON_API |
263 | int 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 | */ |
272 | AWS_COMMON_API |
273 | int 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 | */ |
284 | AWS_COMMON_API |
285 | extern 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 | */ |
291 | AWS_COMMON_API |
292 | int aws_logger_init_noalloc( |
293 | struct aws_logger *logger, |
294 | struct aws_allocator *allocator, |
295 | struct aws_logger_standard_options *options); |
296 | |
297 | AWS_EXTERN_C_END |
298 | |
299 | #endif /* AWS_COMMON_LOGGING_H */ |
300 | |