123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- /**
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0.
- */
- #include <aws/common/log_formatter.h>
- #include <aws/common/date_time.h>
- #include <aws/common/string.h>
- #include <aws/common/thread.h>
- #include <inttypes.h>
- #include <stdarg.h>
- /*
- * Default formatter implementation
- */
- #ifdef _MSC_VER
- # pragma warning(disable : 4204) /* non-constant aggregate initializer */
- #endif
- /* (max) strlen of "[<LogLevel>]" */
- #define LOG_LEVEL_PREFIX_PADDING 7
- /* (max) strlen of "[<ThreadId>]" */
- #define THREAD_ID_PREFIX_PADDING 22
- /* strlen of (user-content separator) " - " + "\n" + spaces between prefix fields + brackets around timestamp + 1 +
- subject_name padding */
- #define MISC_PADDING 15
- #define MAX_LOG_LINE_PREFIX_SIZE \
- (LOG_LEVEL_PREFIX_PADDING + THREAD_ID_PREFIX_PADDING + MISC_PADDING + AWS_DATE_TIME_STR_MAX_LEN)
- static size_t s_advance_and_clamp_index(size_t current_index, int amount, size_t maximum) {
- size_t next_index = current_index + amount;
- if (next_index > maximum) {
- next_index = maximum;
- }
- return next_index;
- }
- /* Thread-local string representation of current thread id */
- AWS_THREAD_LOCAL struct {
- bool is_valid;
- char repr[AWS_THREAD_ID_T_REPR_BUFSZ];
- } tl_logging_thread_id = {.is_valid = false};
- int aws_format_standard_log_line(struct aws_logging_standard_formatting_data *formatting_data, va_list args) {
- size_t current_index = 0;
- /*
- * Begin the log line with "[<Log Level>] ["
- */
- const char *level_string = NULL;
- if (aws_log_level_to_string(formatting_data->level, &level_string)) {
- return AWS_OP_ERR;
- }
- if (formatting_data->total_length == 0) {
- return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
- }
- /*
- * Use this length for all but the last write, so we guarantee room for the newline even if we get truncated
- */
- size_t fake_total_length = formatting_data->total_length - 1;
- int log_level_length = snprintf(formatting_data->log_line_buffer, fake_total_length, "[%s] [", level_string);
- if (log_level_length < 0) {
- return AWS_OP_ERR;
- }
- current_index = s_advance_and_clamp_index(current_index, log_level_length, fake_total_length);
- if (current_index < fake_total_length) {
- /*
- * Add the timestamp. To avoid copies and allocations, do some byte buffer tomfoolery.
- *
- * First, make a byte_buf that points to the current position in the output string
- */
- struct aws_byte_buf timestamp_buffer = {
- .allocator = formatting_data->allocator,
- .buffer = (uint8_t *)formatting_data->log_line_buffer + current_index,
- .capacity = fake_total_length - current_index,
- .len = 0,
- };
- /*
- * Output the current time to the byte_buf
- */
- struct aws_date_time current_time;
- aws_date_time_init_now(¤t_time);
- int result = aws_date_time_to_utc_time_str(¤t_time, formatting_data->date_format, ×tamp_buffer);
- if (result != AWS_OP_SUCCESS) {
- return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
- }
- current_index = s_advance_and_clamp_index(current_index, (int)timestamp_buffer.len, fake_total_length);
- }
- if (current_index < fake_total_length) {
- /*
- * Add thread id and user content separator (" - ")
- */
- if (!tl_logging_thread_id.is_valid) {
- aws_thread_id_t current_thread_id = aws_thread_current_thread_id();
- if (aws_thread_id_t_to_string(current_thread_id, tl_logging_thread_id.repr, AWS_THREAD_ID_T_REPR_BUFSZ)) {
- return AWS_OP_ERR;
- }
- tl_logging_thread_id.is_valid = true;
- }
- int thread_id_written = snprintf(
- formatting_data->log_line_buffer + current_index,
- fake_total_length - current_index,
- "] [%s] ",
- tl_logging_thread_id.repr);
- if (thread_id_written < 0) {
- return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
- }
- current_index = s_advance_and_clamp_index(current_index, thread_id_written, fake_total_length);
- }
- if (current_index < fake_total_length) {
- /* output subject name */
- if (formatting_data->subject_name) {
- int subject_written = snprintf(
- formatting_data->log_line_buffer + current_index,
- fake_total_length - current_index,
- "[%s]",
- formatting_data->subject_name);
- if (subject_written < 0) {
- return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
- }
- current_index = s_advance_and_clamp_index(current_index, subject_written, fake_total_length);
- }
- }
- if (current_index < fake_total_length) {
- int separator_written =
- snprintf(formatting_data->log_line_buffer + current_index, fake_total_length - current_index, " - ");
- current_index = s_advance_and_clamp_index(current_index, separator_written, fake_total_length);
- }
- if (current_index < fake_total_length) {
- /*
- * Now write the actual data requested by the user
- */
- #ifdef _WIN32
- int written_count = vsnprintf_s(
- formatting_data->log_line_buffer + current_index,
- fake_total_length - current_index,
- _TRUNCATE,
- formatting_data->format,
- args);
- #else
- int written_count = vsnprintf(
- formatting_data->log_line_buffer + current_index,
- fake_total_length - current_index,
- formatting_data->format,
- args);
- #endif /* _WIN32 */
- if (written_count < 0) {
- return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
- }
- current_index = s_advance_and_clamp_index(current_index, written_count, fake_total_length);
- }
- /*
- * End with a newline.
- */
- int newline_written_count =
- snprintf(formatting_data->log_line_buffer + current_index, formatting_data->total_length - current_index, "\n");
- if (newline_written_count < 0) {
- return aws_raise_error(AWS_ERROR_UNKNOWN); /* we saved space, so this would be crazy */
- }
- formatting_data->amount_written = current_index + newline_written_count;
- return AWS_OP_SUCCESS;
- }
- struct aws_default_log_formatter_impl {
- enum aws_date_format date_format;
- };
- static int s_default_aws_log_formatter_format(
- struct aws_log_formatter *formatter,
- struct aws_string **formatted_output,
- enum aws_log_level level,
- aws_log_subject_t subject,
- const char *format,
- va_list args) {
- (void)subject;
- struct aws_default_log_formatter_impl *impl = formatter->impl;
- if (formatted_output == NULL) {
- return AWS_OP_ERR;
- }
- /*
- * Calculate how much room we'll need to build the full log line.
- * You cannot consume a va_list twice, so we have to copy it.
- */
- va_list tmp_args;
- va_copy(tmp_args, args);
- #ifdef _WIN32
- int required_length = _vscprintf(format, tmp_args) + 1;
- #else
- int required_length = vsnprintf(NULL, 0, format, tmp_args) + 1;
- #endif
- va_end(tmp_args);
- /*
- * Allocate enough room to hold the line. Then we'll (unsafely) do formatted IO directly into the aws_string
- * memory.
- */
- const char *subject_name = aws_log_subject_name(subject);
- int subject_name_len = 0;
- if (subject_name) {
- subject_name_len = (int)strlen(subject_name);
- }
- int total_length = required_length + MAX_LOG_LINE_PREFIX_SIZE + subject_name_len;
- struct aws_string *raw_string = aws_mem_calloc(formatter->allocator, 1, sizeof(struct aws_string) + total_length);
- if (raw_string == NULL) {
- goto error_clean_up;
- }
- struct aws_logging_standard_formatting_data format_data = {
- .log_line_buffer = (char *)raw_string->bytes,
- .total_length = total_length,
- .level = level,
- .subject_name = subject_name,
- .format = format,
- .date_format = impl->date_format,
- .allocator = formatter->allocator,
- .amount_written = 0,
- };
- if (aws_format_standard_log_line(&format_data, args)) {
- goto error_clean_up;
- }
- *(struct aws_allocator **)(&raw_string->allocator) = formatter->allocator;
- *(size_t *)(&raw_string->len) = format_data.amount_written;
- *formatted_output = raw_string;
- return AWS_OP_SUCCESS;
- error_clean_up:
- if (raw_string != NULL) {
- aws_mem_release(formatter->allocator, raw_string);
- }
- return AWS_OP_ERR;
- }
- static void s_default_aws_log_formatter_clean_up(struct aws_log_formatter *formatter) {
- aws_mem_release(formatter->allocator, formatter->impl);
- }
- static struct aws_log_formatter_vtable s_default_log_formatter_vtable = {
- .format = s_default_aws_log_formatter_format,
- .clean_up = s_default_aws_log_formatter_clean_up,
- };
- int aws_log_formatter_init_default(
- struct aws_log_formatter *formatter,
- struct aws_allocator *allocator,
- struct aws_log_formatter_standard_options *options) {
- struct aws_default_log_formatter_impl *impl =
- aws_mem_calloc(allocator, 1, sizeof(struct aws_default_log_formatter_impl));
- impl->date_format = options->date_format;
- formatter->vtable = &s_default_log_formatter_vtable;
- formatter->allocator = allocator;
- formatter->impl = impl;
- return AWS_OP_SUCCESS;
- }
- void aws_log_formatter_clean_up(struct aws_log_formatter *formatter) {
- AWS_ASSERT(formatter->vtable->clean_up);
- (formatter->vtable->clean_up)(formatter);
- }
|