1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <android/log.h>
#include <aws/common/clock.h>
#include <aws/common/logging.h>
#include <aws/common/string.h>
#include <inttypes.h>
#include <stdarg.h>
#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;
}
|