File Explorer

/var/runtime/node_modules/@aws-sdk/node_modules/aws-crt/source

This explorer reads the filesystem of the server it runs on, so /workspace/user isn't present here. Browsing and the terminal still work against this server's own disk from /.

1 dir
34 files
logger.c13.5 KB · 365 lines
/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include "logger.h" #include <aws/common/linked_list.h>#include <aws/common/log_channel.h>#include <aws/common/log_formatter.h>#include <aws/common/log_writer.h>#include <aws/common/mutex.h>#include <aws/common/ring_buffer.h> #include <ctype.h> #define LOG_RING_BUFFER_CAPACITY (128 * 1024) #ifdef _MSC_VER#    pragma warning(disable : 4204)#endif /* _MSC_VER */ /* * One of these is allocated per napi_env/thread and stored in TLS. Worker threads will call into * their env's instance, and all other event loop threads will call into the main thread's * instance. */struct aws_napi_logger_ctx {    napi_env env;    struct aws_allocator *allocator;    /* ring buffer for copying log messages for queueing */    struct aws_ring_buffer buffer;    /* allocator that uses the ring buffer */    struct aws_allocator buffer_allocator;    /* messages to be logged, filled from any thread, drained from node thread */    struct {        struct aws_mutex mutex;        struct aws_linked_list messages;    } msg_queue;    /* log function in node */    napi_threadsafe_function log_drain;}; static AWS_THREAD_LOCAL struct aws_napi_logger_ctx *tl_logger_ctx; /* aws_log_pipeline components */static struct {    struct aws_logger logger;    struct aws_log_formatter formatter;    struct aws_log_writer writer;    struct aws_log_channel channel;    struct aws_napi_logger_ctx *default_ctx;} s_napi_logger; struct log_message {    struct aws_linked_list_node node;    struct aws_string *message;}; /* custom aws_log_writer that writes via process._rawDebug() within the node env via threadsafe function */static int s_napi_log_writer_write(struct aws_log_writer *writer, const struct aws_string *output) {    (void)writer;    struct aws_napi_logger_ctx *ctx = tl_logger_ctx ? tl_logger_ctx : s_napi_logger.default_ctx;    /* this can only happen if someone tries to log after the main thread has cleaned up */    AWS_FATAL_ASSERT(ctx && "No TLS log context, and no default fallback");     /* node will append a newline, so strip the ones from the logger */    size_t newlines = 0;    while (isspace((const char)(aws_string_bytes(output)[output->len - newlines - 1])) && newlines < output->len) {        ++newlines;    }     /*     * If log_drain is null, it's been released and we can't use it anymore, but we don't want     * to lose logs at shutdown. Node should not close and re-open stderr at this point, so we'll     * just write to it immediately. These messages will escape any application log overrides.     */    if (!ctx->log_drain) {#ifdef AWS_NAPI_LOG_AFTER_SHUTDOWN        fprintf(stderr, "%*s", (int)(output->len - newlines), aws_string_c_str(output));#endif        return AWS_OP_SUCCESS;    }     /*     * Pin the log drain function until we try to call it. If napi_closing is returned, the function     * has been released, which means we are shutting down, so we just bail     */    AWS_NAPI_CALL(env, napi_acquire_threadsafe_function(ctx->log_drain), {        return (status == napi_closing) ? AWS_OP_SUCCESS : AWS_OP_ERR;    });     /* must allocate in the order things will be freed because we use a ring buffer */    struct aws_string *message =        aws_string_new_from_array(&ctx->buffer_allocator, aws_string_bytes(output), output->len - newlines);    struct log_message *msg = aws_mem_calloc(&ctx->buffer_allocator, 1, sizeof(struct log_message));    msg->message = message;     /* queue up the message to be logged next time the function runs */    aws_mutex_lock(&ctx->msg_queue.mutex);    aws_linked_list_push_back(&ctx->msg_queue.messages, &msg->node);    aws_mutex_unlock(&ctx->msg_queue.mutex);     /* queue the call */    AWS_NAPI_ENSURE(ctx->env, napi_call_threadsafe_function(ctx->log_drain, NULL, napi_tsfn_nonblocking));    return AWS_OP_SUCCESS;} static void s_napi_log_writer_clean_up(struct aws_log_writer *writer) {    (void)writer;} static struct aws_log_writer_vtable s_napi_log_writer_vtable = {    .write = s_napi_log_writer_write,    .clean_up = s_napi_log_writer_clean_up,}; void aws_napi_logger_set_level(enum aws_log_level level) {    AWS_FATAL_ASSERT(s_napi_logger.logger.p_impl);    aws_atomic_store_int(&((struct aws_logger_pipeline *)s_napi_logger.logger.p_impl)->level, level);} /* Allocator used to allocate buffered log messages from a ring buffer */static void *s_ring_buffer_mem_acquire(struct aws_allocator *allocator, size_t size) {    struct aws_ring_buffer *buffer = allocator->impl;    struct aws_byte_buf buf;    AWS_ZERO_STRUCT(buf);    /* if the ring buffer is full, fall back to the normal allocator */    if (aws_ring_buffer_acquire(buffer, size + sizeof(size_t), &buf)) {        AWS_FATAL_ASSERT(AWS_OP_SUCCESS == aws_byte_buf_init(&buf, buffer->allocator, size + sizeof(size_t)));    }    *((size_t *)buf.buffer) = buf.capacity;    return buf.buffer + sizeof(size_t);} static void s_ring_buffer_mem_release(struct aws_allocator *allocator, void *ptr) {    void *addr = ((uint8_t *)ptr - sizeof(size_t));    size_t size = *((size_t *)addr);    struct aws_byte_buf buf = {        .allocator = allocator,        .buffer = addr,        .capacity = size,        .len = 0,    };    struct aws_ring_buffer *buffer = allocator->impl;     /* see if the memory comes from the ring buffer */    if (aws_ring_buffer_buf_belongs_to_pool(buffer, &buf)) {        aws_ring_buffer_release(buffer, &buf);    } else {        /* release to the fallback allocator */        aws_mem_release(buffer->allocator, addr);    }} static void *s_ring_buffer_mem_calloc(struct aws_allocator *allocator, size_t num, size_t size) {    void *mem = s_ring_buffer_mem_acquire(allocator, num * size);    memset(mem, 0, size);    return mem;} /* called from every thread as its node environment shuts down */void s_threadsafe_log_finalize(napi_env env, void *finalize_data, void *finalize_hint) {    (void)env;    (void)finalize_hint;     struct aws_napi_logger_ctx *ctx = finalize_data;     /* empty message queue, do not need to delete messages as they all come from the ring buffer */    struct aws_linked_list msgs;    aws_linked_list_init(&msgs);    aws_mutex_lock(&ctx->msg_queue.mutex);    aws_linked_list_swap_contents(&ctx->msg_queue.messages, &msgs);    aws_mutex_unlock(&ctx->msg_queue.mutex);     /* Drop the ref to the function. All attempts to acquire will return napi_closing after this */    AWS_NAPI_ENSURE(env, napi_release_threadsafe_function(ctx->log_drain, napi_tsfn_abort));    ctx->log_drain = NULL;     /* The rest is cleaned up by the env context clean up via aws_napi_logger_destroy() */} /* batch drain the entire queue, subsequent queued calls will do no work */static void s_threadsafe_log_call(napi_env env, napi_value node_log_fn, void *context, void *user_data) {    (void)user_data;    struct aws_napi_logger_ctx *ctx = context;     /* transfer the messages under lock */    struct aws_linked_list msgs;    aws_linked_list_init(&msgs);    aws_mutex_lock(&ctx->msg_queue.mutex);    aws_linked_list_swap_contents(&ctx->msg_queue.messages, &msgs);    aws_mutex_unlock(&ctx->msg_queue.mutex);     /*     * If env is null, that means that the function is simply requesting that any resources be     * freed for shutdown     */    if (!env) {        goto done;    }     /* nothing to do, maybe next time... */    if (aws_linked_list_empty(&msgs)) {        goto done;    }     /*     * Look up `process` to use as this for the _rawDebug call, if these fail it's because the function     * call was queued during shutdown, so we will just skip out of here.     * Avoid printing scary looking error messages (ex: avoid use of AWS_NAPI_CALL macro).     */    napi_value node_global = NULL;    if (napi_ok != napi_get_global(env, &node_global)) {        goto done;    }     napi_value node_process = NULL;    if (napi_ok != napi_get_named_property(env, node_global, "process", &node_process)) {        goto done;    }     for (struct aws_linked_list_node *list_node = aws_linked_list_begin(&msgs); list_node != aws_linked_list_end(&msgs);         list_node = aws_linked_list_next(list_node)) {         struct log_message *msg = AWS_CONTAINER_OF(list_node, struct log_message, node);         napi_value node_message = NULL;        if (napi_ok != napi_create_string_utf8(env, aws_string_c_str(msg->message), msg->message->len, &node_message)) {            goto done;        }         if (napi_ok != napi_call_function(env, node_process, node_log_fn, 1, &node_message, NULL)) {            goto done;        }    }     /* clean up memory and un-pin the log drain function */done:    while (!aws_linked_list_empty(&msgs)) {        struct aws_linked_list_node *list_node = aws_linked_list_pop_front(&msgs);        struct log_message *msg = AWS_CONTAINER_OF(list_node, struct log_message, node);        aws_string_destroy(msg->message);        aws_mem_release(&ctx->buffer_allocator, msg);    }     if (env) {        AWS_NAPI_ENSURE(env, napi_release_threadsafe_function(ctx->log_drain, napi_tsfn_release));    }} void s_threadsafe_log_create(struct aws_napi_logger_ctx *ctx, napi_env env) {    /* look up process._rawDebug */    napi_value node_global = NULL;    AWS_NAPI_ENSURE(env, napi_get_global(env, &node_global));    napi_value node_process = NULL;    AWS_NAPI_ENSURE(env, napi_get_named_property(env, node_global, "process", &node_process));    napi_value node_rawdebug = NULL;    AWS_NAPI_ENSURE(env, napi_get_named_property(env, node_process, "_rawDebug", &node_rawdebug));     napi_value resource_name = NULL;    AWS_NAPI_ENSURE(env, napi_create_string_utf8(env, "aws_logger", 10, &resource_name));     AWS_NAPI_ENSURE(        env,        napi_create_threadsafe_function(            env,            node_rawdebug,            NULL,            resource_name,            0,            1,            ctx,            s_threadsafe_log_finalize,            ctx,            s_threadsafe_log_call,            &ctx->log_drain));     /* convert the threadsafe function to a weak ref (don't keep node open if it's still out there) */    AWS_NAPI_ENSURE(env, napi_unref_threadsafe_function(env, ctx->log_drain));} struct aws_napi_logger_ctx *aws_napi_logger_new(struct aws_allocator *allocator, napi_env env) {    /* The main thread can be re-initialized multiple times, so just return the one we already have */    if (tl_logger_ctx) {        return tl_logger_ctx;    }     struct aws_napi_logger_ctx *ctx = aws_mem_calloc(allocator, 1, sizeof(struct aws_napi_logger_ctx));    AWS_FATAL_ASSERT(ctx && "Failed to allocate new logging context");    ctx->env = env;    ctx->allocator = allocator;    aws_mutex_init(&ctx->msg_queue.mutex);    aws_linked_list_init(&ctx->msg_queue.messages);     /* store this thread's context */    AWS_FATAL_ASSERT(tl_logger_ctx == NULL && "Cannot initialize multiple logging contexts in a single thread");    tl_logger_ctx = ctx;     /* stand up the ring buffer allocator for copying/queueing log messages */    AWS_FATAL_ASSERT(AWS_OP_SUCCESS == aws_ring_buffer_init(&ctx->buffer, ctx->allocator, LOG_RING_BUFFER_CAPACITY));    ctx->buffer_allocator.mem_acquire = s_ring_buffer_mem_acquire;    ctx->buffer_allocator.mem_release = s_ring_buffer_mem_release;    ctx->buffer_allocator.mem_calloc = s_ring_buffer_mem_calloc;    ctx->buffer_allocator.impl = &ctx->buffer;     /* create the log drain */    s_threadsafe_log_create(ctx, ctx->env);     /* The first context created will always be the main thread, so make it the default */    if (s_napi_logger.default_ctx == NULL) {        s_napi_logger.default_ctx = ctx;        /* there's only one logger, so if the aws logger isn't ours, set it */        struct aws_logger *logger = aws_napi_logger_get();        if (logger != aws_logger_get()) {            aws_logger_set(logger);        }    }     return ctx;} void aws_napi_logger_destroy(struct aws_napi_logger_ctx *ctx) {    AWS_ASSERT(tl_logger_ctx == ctx);    tl_logger_ctx = NULL;    if (s_napi_logger.default_ctx == ctx) {        aws_logger_set(NULL);        s_napi_logger.default_ctx = NULL;    }     aws_mutex_clean_up(&ctx->msg_queue.mutex);    /* don't need to worry about cleaning up the message queue, all of the memory comes from the ring buffer */    aws_ring_buffer_clean_up(&ctx->buffer);    aws_mem_release(ctx->allocator, ctx);} struct aws_logger *aws_napi_logger_get(void) {    if (s_napi_logger.logger.allocator) {        return &s_napi_logger.logger;    }     struct aws_allocator *allocator = aws_napi_get_allocator();     s_napi_logger.writer.allocator = allocator;    s_napi_logger.writer.vtable = &s_napi_log_writer_vtable;    s_napi_logger.writer.impl = NULL;     struct aws_log_formatter_standard_options formatter_options = {.date_format = AWS_DATE_FORMAT_ISO_8601};    int op_status = aws_log_formatter_init_default(&s_napi_logger.formatter, allocator, &formatter_options);    AWS_FATAL_ASSERT(op_status == AWS_OP_SUCCESS && "Failed to initialize formatter");     op_status = aws_log_channel_init_foreground(&s_napi_logger.channel, allocator, &s_napi_logger.writer);    AWS_FATAL_ASSERT(op_status == AWS_OP_SUCCESS && "Failed to initialize log channel");     op_status = aws_logger_init_from_external(        &s_napi_logger.logger,        aws_napi_get_allocator(),        &s_napi_logger.formatter,        &s_napi_logger.channel,        &s_napi_logger.writer,        AWS_LL_NONE);    AWS_FATAL_ASSERT(op_status == AWS_OP_SUCCESS && "Failed to initialize logger");    return &s_napi_logger.logger;}