Skip to content

Commit

Permalink
common: introduce a new logging mechanism
Browse files Browse the repository at this point in the history
Derived from the one developed for pmem/rpma.

Signed-off-by: Jan Michalski <[email protected]>
  • Loading branch information
janekmi committed Jan 17, 2024
1 parent 96a8fad commit 97dee5f
Show file tree
Hide file tree
Showing 13 changed files with 849 additions and 5 deletions.
176 changes: 176 additions & 0 deletions src/core/log.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// SPDX-License-Identifier: BSD-3-Clause
/* Copyright 2020-2024, Intel Corporation */

/*
* log.c -- support for logging output to either syslog or stderr or
* via user defined function
*/

#include <stdarg.h>
#include <syslog.h>
#include <time.h>
#include <errno.h>
#ifdef ATOMIC_OPERATIONS_SUPPORTED
#include <stdatomic.h>
#endif /* ATOMIC_OPERATIONS_SUPPORTED */
#include <string.h>

#include "log_internal.h"
#include "log_default.h"

/*
* Default levels of the logging thresholds
*/
#ifdef DEBUG
#define CORE_LOG_THRESHOLD_DEFAULT CORE_LOG_LEVEL_DEBUG
#define CORE_LOG_THRESHOLD_AUX_DEFAULT CORE_LOG_LEVEL_WARNING
#else
#define CORE_LOG_THRESHOLD_DEFAULT CORE_LOG_LEVEL_WARNING
#define CORE_LOG_THRESHOLD_AUX_DEFAULT CORE_LOG_DISABLED
#endif

/*
* Core_log_function -- pointer to the logging function saved as uintptr_t to
* make it _Atomic, because function pointers cannot be _Atomic. By default it
* is core_log_default_function(), but could be a user-defined logging function
* provided via core_log_set_function().
*/
#ifdef ATOMIC_OPERATIONS_SUPPORTED
_Atomic
#endif /* ATOMIC_OPERATIONS_SUPPORTED */
uintptr_t Core_log_function;

/* the logging function's context */
#ifdef ATOMIC_OPERATIONS_SUPPORTED
_Atomic
#endif /* ATOMIC_OPERATIONS_SUPPORTED */
void *Core_log_function_context;

/* threshold levels */
#ifdef ATOMIC_OPERATIONS_SUPPORTED
_Atomic
#endif /* ATOMIC_OPERATIONS_SUPPORTED */
enum core_log_level Core_log_threshold[] = {
CORE_LOG_THRESHOLD_DEFAULT,
CORE_LOG_THRESHOLD_AUX_DEFAULT
};

/*
* core_log_init -- initialize and set the default logging function
*/
void
core_log_init()
{
/* enable the default logging function */
core_log_default_init();
while (EAGAIN ==
core_log_set_function(CORE_LOG_USE_DEFAULT_FUNCTION, NULL))
;
}

/*
* core_log_fini -- disable logging and cleanup the default logging function
*/
void
core_log_fini()
{
/*
* NULL-ed function pointer turns off the logging. No matter if
* the previous value was the default logging function or a user
* logging function.
*/
Core_log_function = 0;
Core_log_function_context = NULL;

/* cleanup the default logging function */
core_log_default_fini();
}

/*
* core_log_set_function -- set the log function pointer either to
* a user-provided function pointer or to the default logging function.
*/
int
core_log_set_function(core_log_function *log_function, void *context)
{

if (log_function == CORE_LOG_USE_DEFAULT_FUNCTION)
log_function = core_log_default_function;

#ifdef ATOMIC_OPERATIONS_SUPPORTED
atomic_store_explicit(&Core_log_function, (uintptr_t)log_function,
__ATOMIC_SEQ_CST);
atomic_store_explicit(&Core_log_function_context, context,
__ATOMIC_SEQ_CST);
return 0;
#else
uintptr_t core_log_function_old = Core_log_function;
if (__sync_bool_compare_and_swap(&Core_log_function,
core_log_function_old, (uintptr_t)log_function))
return 0;
else
return EAGAIN;

void *context_old = Core_log_function_context;
if (__sync_bool_compare_and_swap(&Core_log_function_context,
context_old, context))
return 0;
else
return EAGAIN;
#endif /* ATOMIC_OPERATIONS_SUPPORTED */
}

/*
* core_log_set_threshold -- set the log level threshold
*/
int
core_log_set_threshold(enum core_log_threshold threshold,
enum core_log_level level)
{
if (threshold != CORE_LOG_THRESHOLD &&
threshold != CORE_LOG_THRESHOLD_AUX)
return EINVAL;

if (level < CORE_LOG_DISABLED || level > CORE_LOG_LEVEL_DEBUG)
return EINVAL;

#ifdef ATOMIC_OPERATIONS_SUPPORTED
atomic_store_explicit(&Log_threshold[threshold], level,
__ATOMIC_SEQ_CST);
return 0;
#else
enum core_log_level level_old;
while (EAGAIN == core_log_get_threshold(threshold, &level_old))
;

if (__sync_bool_compare_and_swap(&Core_log_threshold[threshold],
level_old, level))
return 0;
else
return EAGAIN;
#endif /* ATOMIC_OPERATIONS_SUPPORTED */
}

/*
* core_log_get_threshold -- get the log level threshold
*/
int
core_log_get_threshold(enum core_log_threshold threshold,
enum core_log_level *level)
{
if (threshold != CORE_LOG_THRESHOLD &&
threshold != CORE_LOG_THRESHOLD_AUX)
return EINVAL;

if (level == NULL)
return EINVAL;

#ifdef ATOMIC_OPERATIONS_SUPPORTED
*level = atomic_load_explicit(&Log_threshold[threshold],
__ATOMIC_SEQ_CST);
#else
*level = Core_log_threshold[threshold];
#endif /* ATOMIC_OPERATIONS_SUPPORTED */

return 0;
}
182 changes: 182 additions & 0 deletions src/core/log_default.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// SPDX-License-Identifier: BSD-3-Clause
/* Copyright 2020-2024, Intel Corporation */

/*
* log_default.c -- the default logging function with support for logging either
* to syslog or to stderr
*/

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>

#include <stdarg.h>
#include <syslog.h>
#include <time.h>
#include <string.h>
#include <stdio.h>

#include "log_internal.h"
#include "log_default.h"
#include "os.h"
#include "util.h"

static const char log_level_names[6][9] = {
[CORE_LOG_LEVEL_FATAL] = "*FATAL* ",
[CORE_LOG_LEVEL_ERROR] = "*ERROR* ",
[CORE_LOG_LEVEL_WARNING] = "*WARN* ",
[CORE_LOG_LEVEL_NOTICE] = "*NOTE* ",
[CORE_LOG_LEVEL_INFO] = "*INFO* ",
[CORE_LOG_LEVEL_DEBUG] = "*DEBUG* ",
};

static const int log_level_syslog_severity[] = {
[CORE_LOG_LEVEL_FATAL] = LOG_CRIT,
[CORE_LOG_LEVEL_ERROR] = LOG_ERR,
[CORE_LOG_LEVEL_WARNING] = LOG_WARNING,
[CORE_LOG_LEVEL_NOTICE] = LOG_NOTICE,
[CORE_LOG_LEVEL_INFO] = LOG_INFO,
[CORE_LOG_LEVEL_DEBUG] = LOG_DEBUG,
};

/*
* get_timestamp_prefix -- provide actual time in a readable string
*
* NOTE
* This function is static now, so we know all possible calls of snprintf()
* and we conclude it can not fail.
*
* ASSUMPTIONS:
* - buf != NULL && buf_size >= 16
*/
static void
get_timestamp_prefix(char *buf, size_t buf_size)
{
struct tm info;
char date[24];
struct timespec ts;
long usec;

const char error_message[] = "[time error] ";

if (os_clock_gettime(CLOCK_REALTIME, &ts))
goto err_message;

if (NULL == localtime_r(&ts.tv_sec, &info))
goto err_message;

usec = ts.tv_nsec / 1000;
if (!strftime(date, sizeof(date), "%b %d %H:%M:%S", &info))
goto err_message;

/* it cannot fail - please see the note above */
(void) snprintf(buf, buf_size, "%s.%06ld ", date, usec);
if (strnlen(buf, buf_size) == buf_size)
goto err_message;

return;

err_message:
memcpy(buf, error_message, sizeof(error_message));
}

/*
* core_log_default_function -- default logging function used to log a message
* to syslog and/or stderr
*
* The message is started with prefix composed from file, line, func parameters
* followed by string pointed by format. If format includes format specifiers
* (subsequences beginning with %), the additional arguments following format
* are formatted and inserted in the message.
*
* ASSUMPTIONS:
* - level >= CORE_LOG_LEVEL_FATAL && level <= CORE_LOG_LEVEL_DEBUG
* - level <= Core_log_threshold[LOG_THRESHOLD]
* - file == NULL || (file != NULL && function != NULL)
*/
void
core_log_default_function(void *context, enum core_log_level level,
const char *file_name, const int line_no, const char *function_name,
const char *message_format, ...)
{
SUPPRESS_UNUSED(context);

char file_info_buffer[256] = "";
const char *file_info = file_info_buffer;
char message[1024] = "";
const char file_info_error[] = "[file info error]: ";

if (CORE_LOG_DISABLED == level)
return;

va_list arg;
va_start(arg, message_format);
if (vsnprintf(message, sizeof(message), message_format, arg) < 0) {
va_end(arg);
return;
}
va_end(arg);

if (file_name) {
/* extract base_file_name */
const char *base_file_name = strrchr(file_name, '/');
if (!base_file_name)
base_file_name = file_name;
else
/* skip '/' */
base_file_name++;

if (snprintf(file_info_buffer, sizeof(file_info_buffer),
"%s: %3d: %s: ", base_file_name, line_no,
function_name) < 0) {
file_info = file_info_error;
}
}

if (level <= Core_log_threshold[CORE_LOG_THRESHOLD_AUX] ||
level == CORE_LOG_LEVEL_ALWAYS) {
char times_tamp[45] = "";
get_timestamp_prefix(times_tamp, sizeof(times_tamp));
(void) fprintf(stderr, "%s[%ld] %s%s%s", times_tamp,
syscall(SYS_gettid),
log_level_names[(level == CORE_LOG_LEVEL_ALWAYS) ?
CORE_LOG_LEVEL_DEBUG : level],
file_info, message);
}

/* do not log to syslog in case of CORE_LOG_LEVEL_ALWAYS */
if (CORE_LOG_LEVEL_ALWAYS == level)
return;

/* assumed: level <= Core_log_threshold[CORE_LOG_THRESHOLD] */
syslog(log_level_syslog_severity[level], "%s%s%s",
log_level_names[level], file_info, message);
}

/*
* core_log_default_init -- explain why not calling openlog(3)
*/
void
core_log_default_init()
{
/*
* Despite the default logging function prints to the syslog it is
* undesirable to call openlog(3) here since other software components
* might already configured the syslog. It is also unnecessary since
* the first syslog(3) call will call it.
*/
}

/*
* core_log_default_fini -- explain why not calling closelog(3)
*/
void
core_log_default_fini(void)
{
/*
* Since the PMDK libraries might not be the only software components
* making use of the syslog it is undesirable to call closelog(3)
* explicitly. Note its use is optional.
*/
}
19 changes: 19 additions & 0 deletions src/core/log_default.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright 2020-2024, Intel Corporation */

/*
* log_default.h -- the default logging function definitions
*/

#ifndef CORE_LOG_DEFAULT_H
#define CORE_LOG_DEFAULT_H

void core_log_default_function(void *context, enum core_log_level level,
const char *file_name, const int line_no, const char *function_name,
const char *message_format, ...);

void core_log_default_init(void);

void core_log_default_fini(void);

#endif /* CORE_LOG_DEFAULT_H */
Loading

0 comments on commit 97dee5f

Please sign in to comment.