Line data Source code
1 : /**
2 : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 : * SPDX-License-Identifier: Apache-2.0.
4 : */
5 :
6 : #include <aws/common/logging.h>
7 :
8 : #include <aws/common/log_channel.h>
9 : #include <aws/common/log_formatter.h>
10 : #include <aws/common/log_writer.h>
11 : #include <aws/common/mutex.h>
12 : #include <aws/common/string.h>
13 :
14 : #include <errno.h>
15 : #include <stdarg.h>
16 :
17 : #if _MSC_VER
18 : # pragma warning(disable : 4204) /* non-constant aggregate initializer */
19 : # pragma warning(disable : 4996) /* Disable warnings about fopen() being insecure */
20 : #endif
21 :
22 : /*
23 : * Null logger implementation
24 : */
25 0 : static enum aws_log_level s_null_logger_get_log_level(struct aws_logger *logger, aws_log_subject_t subject) {
26 0 : (void)logger;
27 0 : (void)subject;
28 0 :
29 0 : return AWS_LL_NONE;
30 0 : }
31 :
32 : static int s_null_logger_log(
33 : struct aws_logger *logger,
34 : enum aws_log_level log_level,
35 : aws_log_subject_t subject,
36 : const char *format,
37 0 : ...) {
38 0 :
39 0 : (void)logger;
40 0 : (void)log_level;
41 0 : (void)subject;
42 0 : (void)format;
43 0 :
44 0 : return AWS_OP_SUCCESS;
45 0 : }
46 :
47 0 : static void s_null_logger_clean_up(struct aws_logger *logger) {
48 0 : (void)logger;
49 0 : }
50 :
51 : static struct aws_logger_vtable s_null_vtable = {
52 : .get_log_level = s_null_logger_get_log_level,
53 : .log = s_null_logger_log,
54 : .clean_up = s_null_logger_clean_up,
55 : };
56 :
57 : static struct aws_logger s_null_logger = {
58 : .vtable = &s_null_vtable,
59 : .allocator = NULL,
60 : .p_impl = NULL,
61 : };
62 :
63 : /*
64 : * Pipeline logger implementation
65 : */
66 0 : static void s_aws_logger_pipeline_owned_clean_up(struct aws_logger *logger) {
67 0 : struct aws_logger_pipeline *impl = logger->p_impl;
68 0 :
69 0 : AWS_ASSERT(impl->channel->vtable->clean_up != NULL);
70 0 : (impl->channel->vtable->clean_up)(impl->channel);
71 0 :
72 0 : AWS_ASSERT(impl->formatter->vtable->clean_up != NULL);
73 0 : (impl->formatter->vtable->clean_up)(impl->formatter);
74 0 :
75 0 : AWS_ASSERT(impl->writer->vtable->clean_up != NULL);
76 0 : (impl->writer->vtable->clean_up)(impl->writer);
77 0 :
78 0 : aws_mem_release(impl->allocator, impl->channel);
79 0 : aws_mem_release(impl->allocator, impl->formatter);
80 0 : aws_mem_release(impl->allocator, impl->writer);
81 0 :
82 0 : aws_mem_release(impl->allocator, impl);
83 0 : }
84 :
85 : /*
86 : * Pipeline logger implementation
87 : */
88 : static int s_aws_logger_pipeline_log(
89 : struct aws_logger *logger,
90 : enum aws_log_level log_level,
91 : aws_log_subject_t subject,
92 : const char *format,
93 0 : ...) {
94 0 : va_list format_args;
95 0 : va_start(format_args, format);
96 0 :
97 0 : struct aws_logger_pipeline *impl = logger->p_impl;
98 0 : struct aws_string *output = NULL;
99 0 :
100 0 : AWS_ASSERT(impl->formatter->vtable->format != NULL);
101 0 : int result = (impl->formatter->vtable->format)(impl->formatter, &output, log_level, subject, format, format_args);
102 0 :
103 0 : va_end(format_args);
104 0 :
105 0 : if (result != AWS_OP_SUCCESS || output == NULL) {
106 0 : return AWS_OP_ERR;
107 0 : }
108 0 :
109 0 : AWS_ASSERT(impl->channel->vtable->send != NULL);
110 0 : if ((impl->channel->vtable->send)(impl->channel, output)) {
111 0 : /*
112 0 : * failure to send implies failure to transfer ownership
113 0 : */
114 0 : aws_string_destroy(output);
115 0 : return AWS_OP_ERR;
116 0 : }
117 0 :
118 0 : return AWS_OP_SUCCESS;
119 0 : }
120 :
121 0 : static enum aws_log_level s_aws_logger_pipeline_get_log_level(struct aws_logger *logger, aws_log_subject_t subject) {
122 0 : (void)subject;
123 0 :
124 0 : struct aws_logger_pipeline *impl = logger->p_impl;
125 0 :
126 0 : return (enum aws_log_level)aws_atomic_load_int(&impl->level);
127 0 : }
128 :
129 0 : static int s_aws_logger_pipeline_set_log_level(struct aws_logger *logger, enum aws_log_level level) {
130 0 : struct aws_logger_pipeline *impl = logger->p_impl;
131 0 :
132 0 : aws_atomic_store_int(&impl->level, (size_t)level);
133 0 :
134 0 : return AWS_OP_SUCCESS;
135 0 : }
136 :
137 : struct aws_logger_vtable g_pipeline_logger_owned_vtable = {
138 : .get_log_level = s_aws_logger_pipeline_get_log_level,
139 : .log = s_aws_logger_pipeline_log,
140 : .clean_up = s_aws_logger_pipeline_owned_clean_up,
141 : .set_log_level = s_aws_logger_pipeline_set_log_level,
142 : };
143 :
144 : int aws_logger_init_standard(
145 : struct aws_logger *logger,
146 : struct aws_allocator *allocator,
147 0 : struct aws_logger_standard_options *options) {
148 0 :
149 : #ifdef ANDROID
150 : (void)options;
151 : extern int aws_logger_init_logcat(
152 : struct aws_logger *, struct aws_allocator *, struct aws_logger_standard_options *);
153 : return aws_logger_init_logcat(logger, allocator, options);
154 : #endif
155 :
156 0 : struct aws_logger_pipeline *impl = aws_mem_calloc(allocator, 1, sizeof(struct aws_logger_pipeline));
157 0 : if (impl == NULL) {
158 0 : return AWS_OP_ERR;
159 0 : }
160 0 :
161 0 : struct aws_log_writer *writer = aws_mem_acquire(allocator, sizeof(struct aws_log_writer));
162 0 : if (writer == NULL) {
163 0 : goto on_allocate_writer_failure;
164 0 : }
165 0 :
166 0 : struct aws_log_writer_file_options file_writer_options = {
167 0 : .filename = options->filename,
168 0 : .file = options->file,
169 0 : };
170 0 :
171 0 : if (aws_log_writer_init_file(writer, allocator, &file_writer_options)) {
172 0 : goto on_init_writer_failure;
173 0 : }
174 0 :
175 0 : struct aws_log_formatter *formatter = aws_mem_acquire(allocator, sizeof(struct aws_log_formatter));
176 0 : if (formatter == NULL) {
177 0 : goto on_allocate_formatter_failure;
178 0 : }
179 0 :
180 0 : struct aws_log_formatter_standard_options formatter_options = {.date_format = AWS_DATE_FORMAT_ISO_8601};
181 0 :
182 0 : if (aws_log_formatter_init_default(formatter, allocator, &formatter_options)) {
183 0 : goto on_init_formatter_failure;
184 0 : }
185 0 :
186 0 : struct aws_log_channel *channel = aws_mem_acquire(allocator, sizeof(struct aws_log_channel));
187 0 : if (channel == NULL) {
188 0 : goto on_allocate_channel_failure;
189 0 : }
190 0 :
191 0 : if (aws_log_channel_init_background(channel, allocator, writer) == AWS_OP_SUCCESS) {
192 0 : impl->formatter = formatter;
193 0 : impl->channel = channel;
194 0 : impl->writer = writer;
195 0 : impl->allocator = allocator;
196 0 : aws_atomic_store_int(&impl->level, (size_t)options->level);
197 0 :
198 0 : logger->vtable = &g_pipeline_logger_owned_vtable;
199 0 : logger->allocator = allocator;
200 0 : logger->p_impl = impl;
201 0 :
202 0 : return AWS_OP_SUCCESS;
203 0 : }
204 0 :
205 0 : aws_mem_release(allocator, channel);
206 0 :
207 0 : on_allocate_channel_failure:
208 0 : aws_log_formatter_clean_up(formatter);
209 0 :
210 0 : on_init_formatter_failure:
211 0 : aws_mem_release(allocator, formatter);
212 0 :
213 0 : on_allocate_formatter_failure:
214 0 : aws_log_writer_clean_up(writer);
215 0 :
216 0 : on_init_writer_failure:
217 0 : aws_mem_release(allocator, writer);
218 0 :
219 0 : on_allocate_writer_failure:
220 0 : aws_mem_release(allocator, impl);
221 0 :
222 0 : return AWS_OP_ERR;
223 0 : }
224 :
225 : /*
226 : * Pipeline logger implementation where all the components are externally owned. No clean up
227 : * is done on the components. Useful for tests where components are on the stack and often mocked.
228 : */
229 0 : static void s_aws_pipeline_logger_unowned_clean_up(struct aws_logger *logger) {
230 0 : struct aws_logger_pipeline *impl = (struct aws_logger_pipeline *)logger->p_impl;
231 0 :
232 0 : aws_mem_release(impl->allocator, impl);
233 0 : }
234 :
235 : static struct aws_logger_vtable s_pipeline_logger_unowned_vtable = {
236 : .get_log_level = s_aws_logger_pipeline_get_log_level,
237 : .log = s_aws_logger_pipeline_log,
238 : .clean_up = s_aws_pipeline_logger_unowned_clean_up,
239 : .set_log_level = s_aws_logger_pipeline_set_log_level,
240 : };
241 :
242 : int aws_logger_init_from_external(
243 : struct aws_logger *logger,
244 : struct aws_allocator *allocator,
245 : struct aws_log_formatter *formatter,
246 : struct aws_log_channel *channel,
247 : struct aws_log_writer *writer,
248 0 : enum aws_log_level level) {
249 0 :
250 0 : struct aws_logger_pipeline *impl = aws_mem_acquire(allocator, sizeof(struct aws_logger_pipeline));
251 0 :
252 0 : if (impl == NULL) {
253 0 : return AWS_OP_ERR;
254 0 : }
255 0 :
256 0 : impl->formatter = formatter;
257 0 : impl->channel = channel;
258 0 : impl->writer = writer;
259 0 : impl->allocator = allocator;
260 0 : aws_atomic_store_int(&impl->level, (size_t)level);
261 0 :
262 0 : logger->vtable = &s_pipeline_logger_unowned_vtable;
263 0 : logger->allocator = allocator;
264 0 : logger->p_impl = impl;
265 0 :
266 0 : return AWS_OP_SUCCESS;
267 0 : }
268 :
269 : /*
270 : * Global API
271 : */
272 : static struct aws_logger *s_root_logger_ptr = &s_null_logger;
273 :
274 0 : void aws_logger_set(struct aws_logger *logger) {
275 0 : if (logger != NULL) {
276 0 : s_root_logger_ptr = logger;
277 0 : } else {
278 0 : s_root_logger_ptr = &s_null_logger;
279 0 : }
280 0 : }
281 :
282 0 : struct aws_logger *aws_logger_get(void) {
283 0 : return s_root_logger_ptr;
284 0 : }
285 :
286 0 : void aws_logger_clean_up(struct aws_logger *logger) {
287 0 : AWS_ASSERT(logger->vtable->clean_up != NULL);
288 0 :
289 0 : logger->vtable->clean_up(logger);
290 0 : }
291 :
292 : static const char *s_log_level_strings[AWS_LL_COUNT] = {"NONE", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"};
293 :
294 0 : int aws_log_level_to_string(enum aws_log_level log_level, const char **level_string) {
295 0 : AWS_ERROR_PRECONDITION(log_level < AWS_LL_COUNT);
296 0 :
297 0 : if (level_string != NULL) {
298 0 : *level_string = s_log_level_strings[log_level];
299 0 : }
300 0 :
301 0 : return AWS_OP_SUCCESS;
302 0 : }
303 :
304 0 : int aws_string_to_log_level(const char *level_string, enum aws_log_level *log_level) {
305 0 : if (level_string != NULL && log_level != NULL) {
306 0 : size_t level_length = strlen(level_string);
307 0 : for (int i = 0; i < AWS_LL_COUNT; ++i) {
308 0 : if (aws_array_eq_c_str_ignore_case(level_string, level_length, s_log_level_strings[i])) {
309 0 : *log_level = i;
310 0 : return AWS_OP_SUCCESS;
311 0 : }
312 0 : }
313 0 : }
314 0 :
315 0 : aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
316 0 : return AWS_OP_ERR;
317 0 : }
318 :
319 0 : int aws_thread_id_t_to_string(aws_thread_id_t thread_id, char *buffer, size_t bufsz) {
320 0 : AWS_ERROR_PRECONDITION(AWS_THREAD_ID_T_REPR_BUFSZ == bufsz);
321 0 : AWS_ERROR_PRECONDITION(buffer && AWS_MEM_IS_WRITABLE(buffer, bufsz));
322 0 : size_t current_index = 0;
323 0 : unsigned char *bytes = (unsigned char *)&thread_id;
324 0 : for (size_t i = sizeof(aws_thread_id_t); i != 0; --i) {
325 0 : unsigned char c = bytes[i - 1];
326 0 : int written = snprintf(buffer + current_index, bufsz - current_index, "%02x", c);
327 0 : if (written < 0) {
328 0 : return AWS_OP_ERR;
329 0 : }
330 0 : current_index += written;
331 0 : if (bufsz <= current_index) {
332 0 : return AWS_OP_ERR;
333 0 : }
334 0 : }
335 0 : return AWS_OP_SUCCESS;
336 0 : }
337 :
338 0 : #define AWS_LOG_SUBJECT_SPACE_MASK (AWS_LOG_SUBJECT_STRIDE - 1)
339 :
340 : static const uint32_t S_MAX_LOG_SUBJECT = AWS_LOG_SUBJECT_STRIDE * AWS_PACKAGE_SLOTS - 1;
341 :
342 : static const struct aws_log_subject_info_list *volatile s_log_subject_slots[AWS_PACKAGE_SLOTS] = {0};
343 :
344 0 : static const struct aws_log_subject_info *s_get_log_subject_info_by_id(aws_log_subject_t subject) {
345 0 : if (subject > S_MAX_LOG_SUBJECT) {
346 0 : return NULL;
347 0 : }
348 0 :
349 0 : uint32_t slot_index = subject >> AWS_LOG_SUBJECT_STRIDE_BITS;
350 0 : uint32_t subject_index = subject & AWS_LOG_SUBJECT_SPACE_MASK;
351 0 :
352 0 : const struct aws_log_subject_info_list *subject_slot = s_log_subject_slots[slot_index];
353 0 :
354 0 : if (!subject_slot || subject_index >= subject_slot->count) {
355 0 : return NULL;
356 0 : }
357 0 :
358 0 : return &subject_slot->subject_list[subject_index];
359 0 : }
360 :
361 0 : const char *aws_log_subject_name(aws_log_subject_t subject) {
362 0 : const struct aws_log_subject_info *subject_info = s_get_log_subject_info_by_id(subject);
363 0 :
364 0 : if (subject_info != NULL) {
365 0 : return subject_info->subject_name;
366 0 : }
367 0 :
368 0 : return "Unknown";
369 0 : }
370 :
371 0 : void aws_register_log_subject_info_list(struct aws_log_subject_info_list *log_subject_list) {
372 0 : /*
373 0 : * We're not so worried about these asserts being removed in an NDEBUG build
374 0 : * - we'll either segfault immediately (for the first two) or for the count
375 0 : * assert, the registration will be ineffective.
376 0 : */
377 0 : AWS_FATAL_ASSERT(log_subject_list);
378 0 : AWS_FATAL_ASSERT(log_subject_list->subject_list);
379 0 : AWS_FATAL_ASSERT(log_subject_list->count);
380 0 :
381 0 : const uint32_t min_range = log_subject_list->subject_list[0].subject_id;
382 0 : const uint32_t slot_index = min_range >> AWS_LOG_SUBJECT_STRIDE_BITS;
383 0 :
384 0 : if (slot_index >= AWS_PACKAGE_SLOTS) {
385 0 : /* This is an NDEBUG build apparently. Kill the process rather than
386 0 : * corrupting heap. */
387 0 : fprintf(stderr, "Bad log subject slot index 0x%016x\n", slot_index);
388 0 : abort();
389 0 : }
390 0 :
391 0 : s_log_subject_slots[slot_index] = log_subject_list;
392 0 : }
393 :
394 0 : void aws_unregister_log_subject_info_list(struct aws_log_subject_info_list *log_subject_list) {
395 0 : /*
396 0 : * We're not so worried about these asserts being removed in an NDEBUG build
397 0 : * - we'll either segfault immediately (for the first two) or for the count
398 0 : * assert, the registration will be ineffective.
399 0 : */
400 0 : AWS_FATAL_ASSERT(log_subject_list);
401 0 : AWS_FATAL_ASSERT(log_subject_list->subject_list);
402 0 : AWS_FATAL_ASSERT(log_subject_list->count);
403 0 :
404 0 : const uint32_t min_range = log_subject_list->subject_list[0].subject_id;
405 0 : const uint32_t slot_index = min_range >> AWS_LOG_SUBJECT_STRIDE_BITS;
406 0 :
407 0 : if (slot_index >= AWS_PACKAGE_SLOTS) {
408 0 : /* This is an NDEBUG build apparently. Kill the process rather than
409 0 : * corrupting heap. */
410 0 : fprintf(stderr, "Bad log subject slot index 0x%016x\n", slot_index);
411 0 : AWS_FATAL_ASSERT(false);
412 0 : }
413 0 :
414 0 : s_log_subject_slots[slot_index] = NULL;
415 0 : }
416 :
417 : /*
418 : * no alloc implementation
419 : */
420 : struct aws_logger_noalloc {
421 : struct aws_atomic_var level;
422 : FILE *file;
423 : bool should_close;
424 : struct aws_mutex lock;
425 : };
426 :
427 0 : static enum aws_log_level s_noalloc_stderr_logger_get_log_level(struct aws_logger *logger, aws_log_subject_t subject) {
428 0 : (void)subject;
429 0 :
430 0 : struct aws_logger_noalloc *impl = logger->p_impl;
431 0 : return (enum aws_log_level)aws_atomic_load_int(&impl->level);
432 0 : }
433 :
434 0 : #define MAXIMUM_NO_ALLOC_LOG_LINE_SIZE 8192
435 :
436 : static int s_noalloc_stderr_logger_log(
437 : struct aws_logger *logger,
438 : enum aws_log_level log_level,
439 : aws_log_subject_t subject,
440 : const char *format,
441 0 : ...) {
442 0 :
443 0 : char format_buffer[MAXIMUM_NO_ALLOC_LOG_LINE_SIZE];
444 0 :
445 0 : va_list format_args;
446 0 : va_start(format_args, format);
447 0 :
448 : #if _MSC_VER
449 : # pragma warning(push)
450 : # pragma warning(disable : 4221) /* allow struct member to reference format_buffer */
451 : #endif
452 :
453 0 : struct aws_logging_standard_formatting_data format_data = {
454 0 : .log_line_buffer = format_buffer,
455 0 : .total_length = MAXIMUM_NO_ALLOC_LOG_LINE_SIZE,
456 0 : .level = log_level,
457 0 : .subject_name = aws_log_subject_name(subject),
458 0 : .format = format,
459 0 : .date_format = AWS_DATE_FORMAT_ISO_8601,
460 0 : .allocator = logger->allocator,
461 0 : .amount_written = 0,
462 0 : };
463 0 :
464 : #if _MSC_VER
465 : # pragma warning(pop) /* disallow struct member to reference local value */
466 : #endif
467 :
468 0 : int result = aws_format_standard_log_line(&format_data, format_args);
469 0 :
470 0 : va_end(format_args);
471 0 :
472 0 : if (result == AWS_OP_ERR) {
473 0 : return AWS_OP_ERR;
474 0 : }
475 0 :
476 0 : struct aws_logger_noalloc *impl = logger->p_impl;
477 0 :
478 0 : aws_mutex_lock(&impl->lock);
479 0 :
480 0 : if (fwrite(format_buffer, 1, format_data.amount_written, impl->file) < format_data.amount_written) {
481 0 : return aws_translate_and_raise_io_error(errno);
482 0 : }
483 0 :
484 0 : aws_mutex_unlock(&impl->lock);
485 0 :
486 0 : return AWS_OP_SUCCESS;
487 0 : }
488 :
489 0 : static void s_noalloc_stderr_logger_clean_up(struct aws_logger *logger) {
490 0 : if (logger == NULL) {
491 0 : return;
492 0 : }
493 0 :
494 0 : struct aws_logger_noalloc *impl = logger->p_impl;
495 0 : if (impl->should_close) {
496 0 : fclose(impl->file);
497 0 : }
498 0 :
499 0 : aws_mutex_clean_up(&impl->lock);
500 0 :
501 0 : aws_mem_release(logger->allocator, impl);
502 0 : AWS_ZERO_STRUCT(*logger);
503 0 : }
504 :
505 0 : int s_no_alloc_stderr_logger_set_log_level(struct aws_logger *logger, enum aws_log_level level) {
506 0 : struct aws_logger_noalloc *impl = logger->p_impl;
507 0 :
508 0 : aws_atomic_store_int(&impl->level, (size_t)level);
509 0 :
510 0 : return AWS_OP_SUCCESS;
511 0 : }
512 :
513 : static struct aws_logger_vtable s_noalloc_stderr_vtable = {
514 : .get_log_level = s_noalloc_stderr_logger_get_log_level,
515 : .log = s_noalloc_stderr_logger_log,
516 : .clean_up = s_noalloc_stderr_logger_clean_up,
517 : .set_log_level = s_no_alloc_stderr_logger_set_log_level,
518 : };
519 :
520 : int aws_logger_init_noalloc(
521 : struct aws_logger *logger,
522 : struct aws_allocator *allocator,
523 0 : struct aws_logger_standard_options *options) {
524 0 :
525 0 : struct aws_logger_noalloc *impl = aws_mem_calloc(allocator, 1, sizeof(struct aws_logger_noalloc));
526 0 :
527 0 : if (impl == NULL) {
528 0 : return AWS_OP_ERR;
529 0 : }
530 0 :
531 0 : aws_atomic_store_int(&impl->level, (size_t)options->level);
532 0 :
533 0 : if (options->file != NULL) {
534 0 : impl->file = options->file;
535 0 : impl->should_close = false;
536 0 : } else { /* _MSC_VER */
537 0 : impl->file = fopen(options->filename, "w");
538 0 : impl->should_close = true;
539 0 : }
540 0 :
541 0 : aws_mutex_init(&impl->lock);
542 0 :
543 0 : logger->vtable = &s_noalloc_stderr_vtable;
544 0 : logger->allocator = allocator;
545 0 : logger->p_impl = impl;
546 0 :
547 0 : return AWS_OP_SUCCESS;
548 0 : }
549 :
550 0 : int aws_logger_set_log_level(struct aws_logger *logger, enum aws_log_level level) {
551 0 : if (logger == NULL || logger->vtable == NULL) {
552 0 : return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
553 0 : }
554 0 :
555 0 : if (logger->vtable->set_log_level == NULL) {
556 0 : return aws_raise_error(AWS_ERROR_UNIMPLEMENTED);
557 0 : }
558 0 :
559 0 : return logger->vtable->set_log_level(logger, level);
560 0 : }
|