-
Notifications
You must be signed in to change notification settings - Fork 510
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
common: introduce a new logging mechanism
Derived from the one developed for pmem/rpma. Signed-off-by: Jan Michalski <[email protected]>
- Loading branch information
Showing
13 changed files
with
849 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
*/ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 */ |
Oops, something went wrong.