/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include #include #include #include #define LOGCAT_MAX_BUFFER_SIZE (4 * 1024) struct logcat_format_data { char *buffer; size_t bytes_written; size_t total_length; const char *format; }; 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; } /* Override this for Android, as time and log level are taken care of by logcat */ static int s_logcat_format(struct logcat_format_data *formatting_data, va_list args) { size_t current_index = 0; 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; if (current_index < fake_total_length) { /* * Add thread id and user content separator (" - ") */ aws_thread_id_t current_thread_id = aws_thread_current_thread_id(); char thread_id[AWS_THREAD_ID_T_REPR_BUFSZ]; if (aws_thread_id_t_to_string(current_thread_id, thread_id, AWS_THREAD_ID_T_REPR_BUFSZ)) { return AWS_OP_ERR; } int thread_id_written = snprintf(formatting_data->buffer + current_index, fake_total_length - current_index, "[%s] ", thread_id); 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) { uint64_t now = 0; aws_high_res_clock_get_ticks(&now); int current_time_written = snprintf( formatting_data->buffer + current_index, fake_total_length - current_index, "(HRC:%" PRIu64 ") ", now); if (current_time_written < 0) { return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } current_index = s_advance_and_clamp_index(current_index, current_time_written, fake_total_length); } if (current_index < fake_total_length) { int separator_written = snprintf(formatting_data->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 */ int written_count = vsnprintf( formatting_data->buffer + current_index, fake_total_length - current_index, formatting_data->format, args); 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->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->bytes_written = current_index + newline_written_count; return AWS_OP_SUCCESS; } static struct aws_logger_logcat { enum aws_log_level level; } s_logcat_impl; static int s_logcat_log( struct aws_logger *logger, enum aws_log_level log_level, aws_log_subject_t subject, const char *format, ...) { (void)logger; va_list format_args; va_start(format_args, format); char buffer[LOGCAT_MAX_BUFFER_SIZE]; struct logcat_format_data fmt = { .buffer = buffer, .total_length = AWS_ARRAY_SIZE(buffer), .format = format, }; int result = s_logcat_format(&fmt, format_args); va_end(format_args); if (result != AWS_OP_SUCCESS) { return AWS_OP_ERR; } /* ANDROID_LOG_VERBOSE = 2, ANDROID_LOG_FATAL = 7 */ const int prio = 0x8 - log_level; __android_log_write(prio, aws_log_subject_name(subject), buffer); return AWS_OP_SUCCESS; } static enum aws_log_level s_logcat_get_log_level(struct aws_logger *logger, aws_log_subject_t subject) { (void)subject; struct aws_logger_logcat *impl = logger->p_impl; return impl->level; } static void s_logcat_clean_up(struct aws_logger *logger) { logger->p_impl = NULL; } static struct aws_logger_vtable s_logcat_vtable = { .log = s_logcat_log, .get_log_level = s_logcat_get_log_level, .clean_up = s_logcat_clean_up, }; int aws_logger_init_logcat( struct aws_logger *logger, struct aws_allocator *allocator, struct aws_logger_standard_options *options) { logger->allocator = allocator; logger->vtable = &s_logcat_vtable; logger->p_impl = &s_logcat_impl; s_logcat_impl.level = options->level; return AWS_OP_SUCCESS; }