LCOV - code coverage report
Current view: top level - aws-c-common/source - log_formatter.c (source / functions) Hit Total Coverage
Test: all_fuzz.info Lines: 0 224 0.0 %
Date: 2021-04-23 16:28:21 Functions: 0 6 0.0 %

          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/log_formatter.h>
       7             : 
       8             : #include <aws/common/date_time.h>
       9             : #include <aws/common/string.h>
      10             : #include <aws/common/thread.h>
      11             : 
      12             : #include <inttypes.h>
      13             : #include <stdarg.h>
      14             : 
      15             : /*
      16             :  * Default formatter implementation
      17             :  */
      18             : 
      19             : #if _MSC_VER
      20             : #    pragma warning(disable : 4204) /* non-constant aggregate initializer */
      21             : #endif
      22             : 
      23             : /* (max) strlen of "[<LogLevel>]" */
      24           0 : #define LOG_LEVEL_PREFIX_PADDING 7
      25             : 
      26             : /* (max) strlen of "[<ThreadId>]" */
      27           0 : #define THREAD_ID_PREFIX_PADDING 22
      28             : 
      29             : /* strlen of (user-content separator) " - " + "\n" + spaces between prefix fields + brackets around timestamp + 1 +
      30             :    subject_name padding */
      31           0 : #define MISC_PADDING 15
      32             : 
      33             : #define MAX_LOG_LINE_PREFIX_SIZE                                                                                       \
      34           0 :     (LOG_LEVEL_PREFIX_PADDING + THREAD_ID_PREFIX_PADDING + MISC_PADDING + AWS_DATE_TIME_STR_MAX_LEN)
      35             : 
      36           0 : static size_t s_advance_and_clamp_index(size_t current_index, int amount, size_t maximum) {
      37           0 :     size_t next_index = current_index + amount;
      38           0 :     if (next_index > maximum) {
      39           0 :         next_index = maximum;
      40           0 :     }
      41           0 : 
      42           0 :     return next_index;
      43           0 : }
      44             : 
      45             : /* Thread-local string representation of current thread id */
      46             : AWS_THREAD_LOCAL struct {
      47             :     bool is_valid;
      48             :     char repr[AWS_THREAD_ID_T_REPR_BUFSZ];
      49             : } tl_logging_thread_id = {.is_valid = false};
      50             : 
      51           0 : int aws_format_standard_log_line(struct aws_logging_standard_formatting_data *formatting_data, va_list args) {
      52           0 :     size_t current_index = 0;
      53           0 : 
      54           0 :     /*
      55           0 :      * Begin the log line with "[<Log Level>] ["
      56           0 :      */
      57           0 :     const char *level_string = NULL;
      58           0 :     if (aws_log_level_to_string(formatting_data->level, &level_string)) {
      59           0 :         return AWS_OP_ERR;
      60           0 :     }
      61           0 : 
      62           0 :     if (formatting_data->total_length == 0) {
      63           0 :         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
      64           0 :     }
      65           0 : 
      66           0 :     /*
      67           0 :      * Use this length for all but the last write, so we guarantee room for the newline even if we get truncated
      68           0 :      */
      69           0 :     size_t fake_total_length = formatting_data->total_length - 1;
      70           0 : 
      71           0 :     int log_level_length = snprintf(formatting_data->log_line_buffer, fake_total_length, "[%s] [", level_string);
      72           0 :     if (log_level_length < 0) {
      73           0 :         return AWS_OP_ERR;
      74           0 :     }
      75           0 : 
      76           0 :     current_index = s_advance_and_clamp_index(current_index, log_level_length, fake_total_length);
      77           0 : 
      78           0 :     if (current_index < fake_total_length) {
      79           0 :         /*
      80           0 :          * Add the timestamp.  To avoid copies and allocations, do some byte buffer tomfoolery.
      81           0 :          *
      82           0 :          * First, make a byte_buf that points to the current position in the output string
      83           0 :          */
      84           0 :         struct aws_byte_buf timestamp_buffer = {
      85           0 :             .allocator = formatting_data->allocator,
      86           0 :             .buffer = (uint8_t *)formatting_data->log_line_buffer + current_index,
      87           0 :             .capacity = fake_total_length - current_index,
      88           0 :             .len = 0,
      89           0 :         };
      90           0 : 
      91           0 :         /*
      92           0 :          * Output the current time to the byte_buf
      93           0 :          */
      94           0 :         struct aws_date_time current_time;
      95           0 :         aws_date_time_init_now(&current_time);
      96           0 : 
      97           0 :         int result = aws_date_time_to_utc_time_str(&current_time, formatting_data->date_format, &timestamp_buffer);
      98           0 :         if (result != AWS_OP_SUCCESS) {
      99           0 :             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
     100           0 :         }
     101           0 : 
     102           0 :         current_index = s_advance_and_clamp_index(current_index, (int)timestamp_buffer.len, fake_total_length);
     103           0 :     }
     104           0 : 
     105           0 :     if (current_index < fake_total_length) {
     106           0 :         /*
     107           0 :          * Add thread id and user content separator (" - ")
     108           0 :          */
     109           0 :         if (!tl_logging_thread_id.is_valid) {
     110           0 :             aws_thread_id_t current_thread_id = aws_thread_current_thread_id();
     111           0 :             if (aws_thread_id_t_to_string(current_thread_id, tl_logging_thread_id.repr, AWS_THREAD_ID_T_REPR_BUFSZ)) {
     112           0 :                 return AWS_OP_ERR;
     113           0 :             }
     114           0 :             tl_logging_thread_id.is_valid = true;
     115           0 :         }
     116           0 :         int thread_id_written = snprintf(
     117           0 :             formatting_data->log_line_buffer + current_index,
     118           0 :             fake_total_length - current_index,
     119           0 :             "] [%s] ",
     120           0 :             tl_logging_thread_id.repr);
     121           0 :         if (thread_id_written < 0) {
     122           0 :             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
     123           0 :         }
     124           0 :         current_index = s_advance_and_clamp_index(current_index, thread_id_written, fake_total_length);
     125           0 :     }
     126           0 : 
     127           0 :     if (current_index < fake_total_length) {
     128           0 :         /* output subject name */
     129           0 :         if (formatting_data->subject_name) {
     130           0 :             int subject_written = snprintf(
     131           0 :                 formatting_data->log_line_buffer + current_index,
     132           0 :                 fake_total_length - current_index,
     133           0 :                 "[%s]",
     134           0 :                 formatting_data->subject_name);
     135           0 : 
     136           0 :             if (subject_written < 0) {
     137           0 :                 return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
     138           0 :             }
     139           0 : 
     140           0 :             current_index = s_advance_and_clamp_index(current_index, subject_written, fake_total_length);
     141           0 :         }
     142           0 :     }
     143           0 : 
     144           0 :     if (current_index < fake_total_length) {
     145           0 :         int separator_written =
     146           0 :             snprintf(formatting_data->log_line_buffer + current_index, fake_total_length - current_index, " - ");
     147           0 :         current_index = s_advance_and_clamp_index(current_index, separator_written, fake_total_length);
     148           0 :     }
     149           0 : 
     150           0 :     if (current_index < fake_total_length) {
     151           0 :         /*
     152           0 :          * Now write the actual data requested by the user
     153           0 :          */
     154             : #ifdef _WIN32
     155             :         int written_count = vsnprintf_s(
     156             :             formatting_data->log_line_buffer + current_index,
     157             :             fake_total_length - current_index,
     158             :             _TRUNCATE,
     159             :             formatting_data->format,
     160             :             args);
     161             : #else
     162             :         int written_count = vsnprintf(
     163           0 :             formatting_data->log_line_buffer + current_index,
     164           0 :             fake_total_length - current_index,
     165           0 :             formatting_data->format,
     166           0 :             args);
     167           0 : #endif /* _WIN32 */
     168           0 :         if (written_count < 0) {
     169           0 :             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
     170           0 :         }
     171           0 : 
     172           0 :         current_index = s_advance_and_clamp_index(current_index, written_count, fake_total_length);
     173           0 :     }
     174           0 : 
     175           0 :     /*
     176           0 :      * End with a newline.
     177           0 :      */
     178           0 :     int newline_written_count =
     179           0 :         snprintf(formatting_data->log_line_buffer + current_index, formatting_data->total_length - current_index, "\n");
     180           0 :     if (newline_written_count < 0) {
     181           0 :         return aws_raise_error(AWS_ERROR_UNKNOWN); /* we saved space, so this would be crazy */
     182           0 :     }
     183           0 : 
     184           0 :     formatting_data->amount_written = current_index + newline_written_count;
     185           0 : 
     186           0 :     return AWS_OP_SUCCESS;
     187           0 : }
     188             : 
     189             : struct aws_default_log_formatter_impl {
     190             :     enum aws_date_format date_format;
     191             : };
     192             : 
     193             : static int s_default_aws_log_formatter_format(
     194             :     struct aws_log_formatter *formatter,
     195             :     struct aws_string **formatted_output,
     196             :     enum aws_log_level level,
     197             :     aws_log_subject_t subject,
     198             :     const char *format,
     199           0 :     va_list args) {
     200           0 : 
     201           0 :     (void)subject;
     202           0 : 
     203           0 :     struct aws_default_log_formatter_impl *impl = formatter->impl;
     204           0 : 
     205           0 :     if (formatted_output == NULL) {
     206           0 :         return AWS_OP_ERR;
     207           0 :     }
     208           0 : 
     209           0 :     /*
     210           0 :      * Calculate how much room we'll need to build the full log line.
     211           0 :      * You cannot consume a va_list twice, so we have to copy it.
     212           0 :      */
     213           0 :     va_list tmp_args;
     214           0 :     va_copy(tmp_args, args);
     215             : #ifdef _WIN32
     216             :     int required_length = _vscprintf(format, tmp_args) + 1;
     217             : #else
     218             :     int required_length = vsnprintf(NULL, 0, format, tmp_args) + 1;
     219           0 : #endif
     220           0 :     va_end(tmp_args);
     221           0 : 
     222           0 :     /*
     223           0 :      * Allocate enough room to hold the line.  Then we'll (unsafely) do formatted IO directly into the aws_string
     224           0 :      * memory.
     225           0 :      */
     226           0 :     const char *subject_name = aws_log_subject_name(subject);
     227           0 :     int subject_name_len = 0;
     228           0 : 
     229           0 :     if (subject_name) {
     230           0 :         subject_name_len = (int)strlen(subject_name);
     231           0 :     }
     232           0 : 
     233           0 :     int total_length = required_length + MAX_LOG_LINE_PREFIX_SIZE + subject_name_len;
     234           0 :     struct aws_string *raw_string = aws_mem_calloc(formatter->allocator, 1, sizeof(struct aws_string) + total_length);
     235           0 :     if (raw_string == NULL) {
     236           0 :         goto error_clean_up;
     237           0 :     }
     238           0 : 
     239           0 :     struct aws_logging_standard_formatting_data format_data = {
     240           0 :         .log_line_buffer = (char *)raw_string->bytes,
     241           0 :         .total_length = total_length,
     242           0 :         .level = level,
     243           0 :         .subject_name = subject_name,
     244           0 :         .format = format,
     245           0 :         .date_format = impl->date_format,
     246           0 :         .allocator = formatter->allocator,
     247           0 :         .amount_written = 0,
     248           0 :     };
     249           0 : 
     250           0 :     if (aws_format_standard_log_line(&format_data, args)) {
     251           0 :         goto error_clean_up;
     252           0 :     }
     253           0 : 
     254           0 :     *(struct aws_allocator **)(&raw_string->allocator) = formatter->allocator;
     255           0 :     *(size_t *)(&raw_string->len) = format_data.amount_written;
     256           0 : 
     257           0 :     *formatted_output = raw_string;
     258           0 : 
     259           0 :     return AWS_OP_SUCCESS;
     260           0 : 
     261           0 : error_clean_up:
     262           0 : 
     263           0 :     if (raw_string != NULL) {
     264           0 :         aws_mem_release(formatter->allocator, raw_string);
     265           0 :     }
     266           0 : 
     267           0 :     return AWS_OP_ERR;
     268           0 : }
     269             : 
     270           0 : static void s_default_aws_log_formatter_clean_up(struct aws_log_formatter *formatter) {
     271           0 :     aws_mem_release(formatter->allocator, formatter->impl);
     272           0 : }
     273             : 
     274             : static struct aws_log_formatter_vtable s_default_log_formatter_vtable = {
     275             :     .format = s_default_aws_log_formatter_format,
     276             :     .clean_up = s_default_aws_log_formatter_clean_up,
     277             : };
     278             : 
     279             : int aws_log_formatter_init_default(
     280             :     struct aws_log_formatter *formatter,
     281             :     struct aws_allocator *allocator,
     282           0 :     struct aws_log_formatter_standard_options *options) {
     283           0 :     struct aws_default_log_formatter_impl *impl =
     284           0 :         aws_mem_calloc(allocator, 1, sizeof(struct aws_default_log_formatter_impl));
     285           0 :     impl->date_format = options->date_format;
     286           0 : 
     287           0 :     formatter->vtable = &s_default_log_formatter_vtable;
     288           0 :     formatter->allocator = allocator;
     289           0 :     formatter->impl = impl;
     290           0 : 
     291           0 :     return AWS_OP_SUCCESS;
     292           0 : }
     293             : 
     294           0 : void aws_log_formatter_clean_up(struct aws_log_formatter *formatter) {
     295           0 :     AWS_ASSERT(formatter->vtable->clean_up);
     296           0 :     (formatter->vtable->clean_up)(formatter);
     297           0 : }

Generated by: LCOV version 1.13