diff --git a/src/core/log.c b/src/core/log.c new file mode 100644 index 00000000000..e00b223179f --- /dev/null +++ b/src/core/log.c @@ -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 +#include +#include +#include +#ifdef ATOMIC_OPERATIONS_SUPPORTED +#include +#endif /* ATOMIC_OPERATIONS_SUPPORTED */ +#include + +#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; +} diff --git a/src/core/log_default.c b/src/core/log_default.c new file mode 100644 index 00000000000..c8066cddaae --- /dev/null +++ b/src/core/log_default.c @@ -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 +#include +#include + +#include +#include +#include +#include +#include + +#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. + */ +} diff --git a/src/core/log_default.h b/src/core/log_default.h new file mode 100644 index 00000000000..613e0e594c9 --- /dev/null +++ b/src/core/log_default.h @@ -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 */ diff --git a/src/core/log_internal.h b/src/core/log_internal.h new file mode 100644 index 00000000000..a502d0d8134 --- /dev/null +++ b/src/core/log_internal.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright 2020-2024, Intel Corporation */ + +/* + * log_internal.h -- internal logging interfaces + */ + +#ifndef CORE_LOG_INTERNAL_H +#define CORE_LOG_INTERNAL_H + +#include +#include +#include +#include +#ifdef ATOMIC_OPERATIONS_SUPPORTED +#include +#endif /* ATOMIC_OPERATIONS_SUPPORTED */ + +enum core_log_level { + /* all messages will be suppressed */ + CORE_LOG_DISABLED = -1, + /* an error that causes the library to stop working immediately */ + CORE_LOG_LEVEL_FATAL, + /* an error that causes the library to stop working properly */ + CORE_LOG_LEVEL_ERROR, + /* an errors that could be handled in the upper level */ + CORE_LOG_LEVEL_WARNING, + /* non-massive info mainly related to public API function completions */ + CORE_LOG_LEVEL_NOTICE, + /* massive info e.g. every write operation indication */ + CORE_LOG_LEVEL_INFO, + /* debug info e.g. write operation dump */ + CORE_LOG_LEVEL_DEBUG, +}; + +enum core_log_threshold { + /* + * the main threshold level - the logging messages above this level + * won't trigger the logging functions + */ + CORE_LOG_THRESHOLD, + /* + * the auxiliary threshold level - may or may not be used by the logging + * function + */ + CORE_LOG_THRESHOLD_AUX, + CORE_LOG_THRESHOLD_MAX +}; + +int core_log_set_threshold(enum core_log_threshold threshold, + enum core_log_level level); + +int core_log_get_threshold(enum core_log_threshold threshold, + enum core_log_level *level); + +/* + * the type used for defining logging functions + */ +typedef void core_log_function( + /* the context provided when setting the log function */ + void *context, + /* the log level of the message */ + enum core_log_level level, + /* name of the source file where the message coming from */ + const char *file_name, + /* the source file line where the message coming from */ + const int line_no, + /* the function name where the message coming from */ + const char *function_name, + /* printf(3)-like format string of the message */ + const char *message_format, + /* additional arguments of the message format string */ + ...); + +#define CORE_LOG_USE_DEFAULT_FUNCTION (NULL) + +int core_log_set_function(core_log_function *log_function, void *context); + +/* pointer to the logging function */ +extern +#ifdef ATOMIC_OPERATIONS_SUPPORTED +_Atomic +#endif /* ATOMIC_OPERATIONS_SUPPORTED */ +uintptr_t Core_log_function; + +/* the logging function's context */ +extern +#ifdef ATOMIC_OPERATIONS_SUPPORTED +_Atomic +#endif /* ATOMIC_OPERATIONS_SUPPORTED */ +void *Core_log_function_context; + +/* threshold levels */ +extern +#ifdef ATOMIC_OPERATIONS_SUPPORTED +_Atomic +#endif /* ATOMIC_OPERATIONS_SUPPORTED */ +enum core_log_level Core_log_threshold[CORE_LOG_THRESHOLD_MAX]; + +void core_log_init(void); + +void core_log_fini(void); + +#define CORE_LOG(level, format, ...) \ + do { \ + if (level <= Core_log_threshold[RPMA_LOG_THRESHOLD] && \ + 0 != Core_log_function) { \ + ((log_function *)Core_log_function)( \ + Core_log_function_context, level, __FILE__, \ + __LINE__, __func__, format, ##__VA_ARGS__); \ + } \ + } while (0) + +#define CORE_LOG_LEVEL_ALWAYS (CORE_LOG_DISABLED - 1) + +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, ...); + +/* + * Set of macros that should be used as the primary API for logging. + * Direct call to log shall be used only in exceptional, corner cases. + */ +#define CORE_LOG_DEBUG(format, ...) \ + CORE_LOG(CORE_LOG_LEVEL_DEBUG, format "\n", ##__VA_ARGS__) + +#define CORE_LOG_INFO(format, ...) \ + CORE_LOG(CORE_LOG_LEVEL_INFO, format "\n", ##__VA_ARGS__) + +#define CORE_LOG_NOTICE(format, ...) \ + CORE_LOG(CORE_LOG_LEVEL_NOTICE, format "\n", ##__VA_ARGS__) + +#define CORE_LOG_WARNING(format, ...) \ + CORE_LOG(CORE_LOG_LEVEL_WARNING, format "\n", ##__VA_ARGS__) + +#define CORE_LOG_ERROR(format, ...) \ + CORE_LOG(CORE_LOG_LEVEL_ERROR, format "\n", ##__VA_ARGS__) + +#define CORE_LOG_FATAL(format, ...) \ + CORE_LOG(CORE_LOG_LEVEL_FATAL, format "\n", ##__VA_ARGS__) + +#define CORE_LOG_ALWAYS(format, ...) \ + CORE_LOG(CORE_LOG_LEVEL_ALWAYS, format "\n", ##__VA_ARGS__) + +/* + * 'f' stands here for 'function' or 'format' where the latter may accept + * additional arguments. + */ +#define LOG_ERROR_WITH_ERRNO(e, f, ...) \ + LOG_ERROR(f " failed: %s", ##__VA_ARGS__, strerror(abs(e))); + +static inline int +core_log_error_translate(int ret) +{ + if (ret != 0) { + errno = ret; + return 1; + } + + return 0; +} + +#endif /* CORE_LOG_INTERNAL_H */ diff --git a/src/core/pmemcore.h b/src/core/pmemcore.h index 8291ad1a35e..4eb7f7dffe0 100644 --- a/src/core/pmemcore.h +++ b/src/core/pmemcore.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: BSD-3-Clause */ -/* Copyright 2020, Intel Corporation */ +/* Copyright 2020-2024, Intel Corporation */ /* * pmemcore.h -- definitions for "core" module @@ -10,6 +10,7 @@ #include "util.h" #include "out.h" +#include "log_internal.h" #ifdef __cplusplus extern "C" { @@ -24,6 +25,7 @@ core_init(const char *log_prefix, const char *log_level_var, int minor_version) { util_init(); + core_log_init(); out_init(log_prefix, log_level_var, log_file_var, major_version, minor_version); } @@ -35,6 +37,7 @@ static inline void core_fini(void) { out_fini(); + core_log_fini(); } #ifdef __cplusplus diff --git a/src/core/pmemcore.inc b/src/core/pmemcore.inc index d701ab0cb97..e07f5a8f7e1 100644 --- a/src/core/pmemcore.inc +++ b/src/core/pmemcore.inc @@ -40,4 +40,7 @@ SOURCE +=\ $(CORE)/ravl.c\ $(CORE)/ravl_interval.c\ $(CORE)/util.c\ - $(CORE)/util_posix.c + $(CORE)/util_posix.c \ + $(CORE)/log.c \ + $(CORE)/log_default.c + diff --git a/src/include/libpmemobj/log.h b/src/include/libpmemobj/log.h new file mode 100644 index 00000000000..4a0be3966ce --- /dev/null +++ b/src/include/libpmemobj/log.h @@ -0,0 +1,236 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright 2024, Intel Corporation */ + +/* + * libpmemobj/log.h -- the controls for the libpmemobj's logging output + */ + +#ifndef LIBPMEMOBJ_LOG_H +#define LIBPMEMOBJ_LOG_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Available log levels. Log levels (except for PMEMOBJ_LOG_DISABLED) are used + * in the logging API calls to indicate logging message severity. Log levels are + * also used to define thresholds for the logging. + */ +enum pmemobj_log_level { + /* all messages will be suppressed */ + PMEMOBJ_LOG_DISABLED = -1, + /* an error that causes the library to stop working immediately */ + PMEMOBJ_LOG_LEVEL_FATAL, + /* an error that causes the library to stop working properly */ + PMEMOBJ_LOG_LEVEL_ERROR, + /* an errors that could be handled in the upper level */ + PMEMOBJ_LOG_LEVEL_WARNING, + /* non-massive info mainly related to public API function completions */ + PMEMOBJ_LOG_LEVEL_NOTICE, + /* massive info e.g. every write operation indication */ + PMEMOBJ_LOG_LEVEL_INFO, + /* debug info e.g. write operation dump */ + PMEMOBJ_LOG_LEVEL_DEBUG, +}; + +enum pmemobj_log_threshold { + /* + * the main threshold level - the logging messages above this level + * won't trigger the logging functions + */ + PMEMOBJ_LOG_THRESHOLD, + /* + * the auxiliary threshold level - may or may not be used by the logging + * function + */ + PMEMOBJ_LOG_THRESHOLD_AUX, + PMEMOBJ_LOG_THRESHOLD_MAX +}; + +/* + * pmemobj_log_set_threshold - set the logging threshold level + * + * SYNOPSIS + * + * int pmemobj_log_set_threshold(enum pmemobj_log_threshold threshold, + * enum pmemobj_log_level level); + * + * enum log_level { + * PMEMOBJ_LOG_DISABLED, + * PMEMOBJ_LOG_LEVEL_FATAL, + * PMEMOBJ_LOG_LEVEL_ERROR, + * PMEMOBJ_LOG_LEVEL_WARNING, + * PMEMOBJ_LOG_LEVEL_NOTICE, + * PMEMOBJ_LOG_LEVEL_INFO, + * PMEMOBJ_LOG_LEVEL_DEBUG, + * }; + * + * enum log_threshold { + * PMEMOBJ_LOG_THRESHOLD, + * PMEMOBJ_LOG_THRESHOLD_AUX, + * PMEMOBJ_LOG_THRESHOLD_MAX + * }; + * + * DESCRIPTION + * pmemobj_log_set_threshold() sets the logging threshold level. + * + * Available thresholds are: + * - PMEMOBJ_LOG_THRESHOLD - the main threshold used to filter out undesired + * logging messages. Messages on a higher level than the primary threshold + * level are ignored. PMEMOBJ_LOG_DISABLED shall be used to suppress logging. + * The default value is PMEMOBJ_LOG_WARNING. + * - PMEMOBJ_LOG_THRESHOLD_AUX - the auxiliary threshold intended for use inside + * the logging function (please see log_get_threshold(3)). The logging + * function may or may not take this threshold into consideration. The default + * value is PMEMOBJ_LOG_DISABLED. + * + * Available threshold levels are defined by enum log_level: + * - PMEMOBJ_LOG_DISABLED - all messages will be suppressed + * - PMEMOBJ_LOG_LEVEL_FATAL - an error that causes the library to stop working + * immediately + * - PMEMOBJ_LOG_LEVEL_ERROR - an error that causes the library to stop working + * properly + * - PMEMOBJ_LOG_LEVEL_WARNING - an error that could be handled in the upper + * level + * - PMEMOBJ_LOG_LEVEL_NOTICE - non-massive info mainly related to public API + * function completions + * - PMEMOBJ_LOG_LEVEL_INFO - massive info e.g. every write operation indication + * - PMEMOBJ_LOG_LEVEL_DEBUG - debug info e.g. write operation dump + * + * THE DEFAULT LOGGING FUNCTION + * The default logging function writes messages to syslog(3) and to stderr(3), + * where syslog(3) is the primary destination (PMEMOBJ_LOG_THRESHOLD applies) + * whereas stderr(3) is the secondary destination (PMEMOBJ_LOG_THRESHOLD_AUX + * applies). + * + * RETURN VALUE + * pmemobj_log_set_threshold() function returns 0 on success or returns + * a non-zero value and sets errno on failure. + * + * ERRORS + * pmemobj_log_set_threshold() can set the following errno values on fail: + * - EINVAL - threshold is not PMEMOBJ_LOG_THRESHOLD nor + * PMEMOBJ_LOG_THRESHOLD_AUX + * - EINVAL - level is not a value defined by enum log_level type + * - EAGAIN - a temporary error occurred, the retry may fix the problem + * + * SEE ALSO + * pmemobj_log_get_threshold(3), pmemobj_log_set_function(3). + */ +int pmemobj_log_set_threshold(enum pmemobj_log_threshold threshold, + enum pmemobj_log_level level); + +/* + * pmemobj_log_get_threshold - get the logging threshold level + * + * SYNOPSIS + * + * int pmemobj_log_get_threshold(enum pmemobj_log_threshold threshold, + * enum pmemobj_log_level *level); + * + * DESCRIPTION + * pmemobj_log_get_threshold() gets the current level of the threshold. + * See pmemobj_log_set_threshold(3) for available thresholds and levels. + * + * RETURN VALUE + * pmemobj_log_get_threshold() function returns 0 on success or returns + * a non-zero value and sets errno on failure. + * + * ERRORS + * pmemobj_log_get_threshold() can fail with the following errors: + * - EINVAL - threshold is not CORE_LOG_THRESHOLD nor CORE_LOG_THRESHOLD_AUX + * - EINVAL - *level is NULL + * + * SEE ALSO + * pmemobj_log_set_function(3), pmemobj_log_set_threshold(3). + */ +int pmemobj_log_get_threshold(enum pmemobj_log_threshold threshold, + enum pmemobj_log_level *level); + +/* + * the type used for defining logging functions + */ +typedef void pmemobj_log_function( + /* the context provided when setting the log function */ + void *context, + /* the log level of the message */ + enum pmemobj_log_level level, + /* name of the source file where the message coming from */ + const char *file_name, + /* the source file line where the message coming from */ + const int line_no, + /* the function name where the message coming from */ + const char *function_name, + /* printf(3)-like format string of the message */ + const char *message_format, + /* additional arguments of the message format string */ + ...); + +#define PMEMOBJ_LOG_USE_DEFAULT_FUNCTION (NULL) + +/* + * pmemobj_log_set_function - set the logging function + * + * SYNOPSIS + * + * typedef void pmemobj_log_function( + * void *context, + * enum pmemobj_log_level level, + * const char *file_name, + * const int line_no, + * const char *function_name, + * const char *message_format, + * ...); + * + * int pmemobj_log_set_function(pmemobj_log_function *log_function, + * void *context); + * + * DESCRIPTION + * pmemobj_log_set_function() allows choosing the function which will get all + * the generated logging messages. The log_function can be either + * PMEMOBJ_LOG_USE_DEFAULT_FUNCTION which will use the default logging function + * (built into the library) or a pointer to a user-defined function. + * The context allows to pass an additional value which will be passed along + * with all the logging messages to the logging function. When the provided + * log_function is PMEMOBJ_LOG_USE_DEFAULT_FUNCTION the provided context is + * ignored. + * + * Parameters of a user-defined log function are as follow: + * - level - the log level of the message + * - file_name - name of the source file where the message coming from. It could + * be set to NULL and + * in such case neither line_no nor function_name are provided. + * - line_no - the source file line where the message coming from + * - function_name - the function name where the message coming from + * - message_format - printf(3)-like format string of the message + * - "..." - additional arguments of the message format string + * + * THE DEFAULT LOGGING FUNCTION + * The initial value of the logging function is CORE_LOG_USE_DEFAULT_FUNCTION. + * This function writes messages to syslog(3) (the primary destination) and to + * stderr(3) (the secondary destination). + * + * RETURN VALUE + * pmemobj_log_set_function() function returns 0 on success or returns + * a non-zero value and sets errno on failure. + * + * ERRORS + * - EAGAIN - a temporary error occurred, the retry may fix the problem + * + * NOTE + * The logging messages on the levels above the CORE_LOG_THRESHOLD level won't + * trigger the logging function. + * + * The user defined function must be thread-safe. + * + * SEE ALSO + * pmemobj_log_get_threshold(3), pmemobj_log_set_threshold(3). + */ +int pmemobj_log_set_function(pmemobj_log_function *log_function, void *context); + +#ifdef __cplusplus +} +#endif + +#endif /* libpmemobj/log.h */ diff --git a/src/libpmem/Makefile b/src/libpmem/Makefile index 54eda860469..a3f8a70e064 100644 --- a/src/libpmem/Makefile +++ b/src/libpmem/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: BSD-3-Clause -# Copyright 2014-2023, Intel Corporation +# Copyright 2014-2024, Intel Corporation # # src/libpmem/Makefile -- Makefile for libpmem @@ -18,6 +18,8 @@ SOURCE =\ $(CORE)/out.c\ $(CORE)/util.c\ $(CORE)/util_posix.c\ + $(CORE)/log.c\ + $(CORE)/log_default.c\ $(COMMON)/file.c\ $(COMMON)/file_posix.c\ $(COMMON)/mmap.c\ diff --git a/src/libpmemobj/Makefile b/src/libpmemobj/Makefile index af013dd3cc8..44ad0cc4933 100644 --- a/src/libpmemobj/Makefile +++ b/src/libpmemobj/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: BSD-3-Clause -# Copyright 2014-2023, Intel Corporation +# Copyright 2014-2024, Intel Corporation # # src/libpmemobj/Makefile -- Makefile for libpmemobj @@ -26,6 +26,7 @@ SOURCE +=\ memblock.c\ memops.c\ obj.c\ + obj_log.c\ palloc.c\ pmalloc.c\ recycler.c\ diff --git a/src/libpmemobj/libpmemobj.link.in b/src/libpmemobj/libpmemobj.link.in index 5b730caa6b0..0bb0aea2e02 100644 --- a/src/libpmemobj/libpmemobj.link.in +++ b/src/libpmemobj/libpmemobj.link.in @@ -56,6 +56,9 @@ LIBPMEMOBJ_1.0 { pmemobj_list_insert_new; pmemobj_list_remove; pmemobj_list_move; + pmemobj_log_get_threshold; + pmemobj_log_set_function; + pmemobj_log_set_threshold; pmemobj_tx_begin; pmemobj_tx_stage; pmemobj_tx_abort; diff --git a/src/libpmemobj/obj_log.c b/src/libpmemobj/obj_log.c new file mode 100644 index 00000000000..3984e9e5d5e --- /dev/null +++ b/src/libpmemobj/obj_log.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright 2024, Intel Corporation */ + +/* + * obj_log.c -- the public interface to control the logging output + */ +#include + +#include "log_internal.h" +#include "util.h" + +/* + * pmemobj_log_set_threshold -- set the log level threshold + */ +int +pmemobj_log_set_threshold(enum pmemobj_log_threshold threshold, + enum pmemobj_log_level level) +{ + int ret = core_log_set_threshold((enum core_log_threshold)threshold, + (enum core_log_level)level); + return core_log_error_translate(ret); +} + +/* + * pmemobj_log_get_threshold -- get the log level threshold + */ +int +pmemobj_log_get_threshold(enum pmemobj_log_threshold threshold, + enum pmemobj_log_level *level) +{ + int ret = core_log_get_threshold((enum core_log_threshold)threshold, + (enum core_log_level *)level); + return core_log_error_translate(ret); +} + +/* + * pmemobj_log_set_function -- set the log function pointer either to + * a user-provided function pointer or to the default logging function. + */ +int +pmemobj_log_set_function(pmemobj_log_function *log_function, void *context) +{ + if (log_function == PMEMOBJ_LOG_USE_DEFAULT_FUNCTION) + context = NULL; + + int ret = core_log_set_function((core_log_function *)log_function, + context); + return core_log_error_translate(ret); +} diff --git a/src/test/Makefile.inc b/src/test/Makefile.inc index 2d893b96618..bf066cf7b71 100644 --- a/src/test/Makefile.inc +++ b/src/test/Makefile.inc @@ -1,5 +1,5 @@ # SPDX-License-Identifier: BSD-3-Clause -# Copyright 2014-2023, Intel Corporation +# Copyright 2014-2024, Intel Corporation # # @@ -388,6 +388,8 @@ ifeq ($(LIBPMEMCORE), internal-nondebug) OBJS +=\ $(TOP)/src/nondebug/core/alloc.o\ $(TOP)/src/nondebug/core/fs_posix.o\ + $(TOP)/src/nondebug/core/log.o\ + $(TOP)/src/nondebug/core/log_default.o\ $(TOP)/src/nondebug/core/os_posix.o\ $(TOP)/src/nondebug/core/os_thread_posix.o\ $(TOP)/src/nondebug/core/out.o\ @@ -403,6 +405,8 @@ ifeq ($(LIBPMEMCORE), internal-debug) OBJS +=\ $(TOP)/src/debug/core/alloc.o\ $(TOP)/src/debug/core/fs_posix.o\ + $(TOP)/src/debug/core/log.o\ + $(TOP)/src/debug/core/log_default.o\ $(TOP)/src/debug/core/os_posix.o\ $(TOP)/src/debug/core/os_thread_posix.o\ $(TOP)/src/debug/core/out.o\ diff --git a/src/test/scope/out5.log.match b/src/test/scope/out5.log.match index 3ad10d14459..d88824a39f1 100644 --- a/src/test/scope/out5.log.match +++ b/src/test/scope/out5.log.match @@ -31,6 +31,9 @@ pmemobj_list_insert$(nW) pmemobj_list_insert_new$(nW) pmemobj_list_move$(nW) pmemobj_list_remove$(nW) +pmemobj_log_get_threshold$(nW) +pmemobj_log_set_function$(nW) +pmemobj_log_set_threshold$(nW) pmemobj_memcpy$(nW) pmemobj_memcpy_persist$(nW) pmemobj_memmove$(nW)