void zlog_log()

in src/logging/zlog/src/zlog.c [236:395]


void zlog_log(enum ZLOG_SEVERITY msg_level, const char* func, unsigned int line, const char* fmt, ...)
{
    const bool console_log_needed =
        (log_setting.console_logging_mode != ZLOG_CLM_DISABLED) && (msg_level >= log_setting.console_level);
    const bool file_log_needed = zlog_is_file_log_open() && (msg_level >= log_setting.file_level);

    if (!console_log_needed && !file_log_needed)
    {
        // If we're not logging to console or file, there's nothing to do.
        return;
    }

    char prelude_buffer[PRELUDE_BUFFER_SIZE];
    prelude_buffer[0] = '\0';

    struct timespec curtime;
    ADUCPAL_clock_gettime(CLOCK_REALTIME, &curtime);

    const time_t seconds = curtime.tv_sec;

    struct tm gmtval;
    struct tm* tmval = ADUCPAL_gmtime_r(&seconds, &gmtval);

    if (tmval != NULL)
    {
        // % 100 below to ensure the values fit in 2-digits template.
        int ret = snprintf(
            prelude_buffer,
            PRELUDE_BUFFER_SIZE,
            PRELUDE_FORMAT,
            tmval->tm_year + 1900,
            tmval->tm_mon + 1,
            tmval->tm_mday % 100,
            tmval->tm_hour % 100,
            tmval->tm_min % 100,
            tmval->tm_sec % 100,
            (int)(curtime.tv_nsec / 100000),
            ADUCPAL_getpid(),
            (pid_t)ADUCPAL_syscall(SYS_gettid) /* cannot call gettid() directly */
        );

        if (ret < 0)
        {
            return;
        }
    }

    char va_buffer[LOG_CONTENT_BUFFER_SIZE];
    va_list va;
    va_start(va, fmt);
    int full_log_len_ret = vsnprintf(va_buffer, sizeof(va_buffer) / sizeof(va_buffer[0]), fmt, va);

    //
    // If value is velow zero we've encountered some fatal error in vsnprintf
    //
    if (full_log_len_ret < 0)
    {
        return;
    }

    size_t full_log_len = (size_t)full_log_len_ret;

    // A return value of size or more means that the output was truncated.
    bool log_truncated = full_log_len >= (sizeof(va_buffer) / sizeof(va_buffer[0]));
    va_end(va);

    if (console_log_needed)
    {
        // Output to console

        const char* color_prefix;
        const char* color_suffix;

        if (log_setting.console_logging_mode != ZLOG_CLM_ENABLED_TTYCOLOR)
        {
            color_prefix = "";
            color_suffix = "";
        }
        else
        {
            // Use Bold Red for error, Bold Yellow for warn.
            color_prefix = (msg_level == ZLOG_ERROR) ? "\033[1;31m" : (msg_level == ZLOG_WARN) ? "\033[1;33m" : "";
            color_suffix = "\033[m";
        }

        FILE* output = msg_level == ZLOG_ERROR ? stderr : stdout;

        if (log_truncated)
        {
            // va_buffer contains truncated log. Let's use vfprintf to directly
            // print log to console instead.
            fprintf(output, "%s %s[%c]%s ", prelude_buffer, color_prefix, level_names[msg_level], color_suffix);
            va_start(va, fmt);
            (void)vfprintf(output, fmt, va);
            va_end(va);
            fprintf(output, " [%s:%u]\n", func, line);
            fflush(output);
        }
        else
        {
            fprintf(
                msg_level == ZLOG_ERROR ? stderr : stdout,
                "%s %s[%c]%s %s [%s:%u]\n",
                prelude_buffer,
                color_prefix,
                level_names[msg_level],
                color_suffix,
                va_buffer,
                func,
                line);
        }
    }

    if (file_log_needed)
    {
        if ((full_log_len + RESERVED_INFO_SIZE) < ZLOG_BUFFER_LINE_MAXCHARS)
        {
            // The log can fit in one line. Just add to zlog buffer.
            char* buffer = zlog_lock_and_get_buffer();

            (void)snprintf(
                buffer,
                ZLOG_BUFFER_LINE_MAXCHARS,
                LOG_FORMAT,
                prelude_buffer,
                level_names[msg_level],
                va_buffer,
                func,
                line);

            zlog_finish_buffer_and_unlock();
        }
        else
        {
            // The log is too long, let's flush the buffer then write to log file directly.
            _zlog_buffer_lock();
            _zlog_flush_buffer();

            _zlog_roll_over_if_file_size_too_large(
                full_log_len + sizeof(MULTILINE_BEGIN_FORMAT) + sizeof(MULTILINE_END_FORMAT)
                + (PRELUDE_BUFFER_SIZE + MAX_FUNCTION_NAME) * 2);
            fprintf(zlog_fout, MULTILINE_BEGIN_FORMAT, prelude_buffer, level_names[msg_level], func, line);

            va_start(va, fmt);
            (void)vfprintf(zlog_fout, fmt, va);
            va_end(va);

            fprintf(zlog_fout, MULTILINE_END_FORMAT, prelude_buffer, level_names[msg_level], func, line);
            fflush(zlog_fout);

            _zlog_buffer_unlock();
        }
    }

    if (msg_level == ZLOG_ERROR || (seconds - zlog_last_flushed) >= ZLOG_FLUSH_INTERVAL_SEC)
    {
        zlog_flush_buffer();
        zlog_last_flushed = seconds;
    }
}