blender/intern/clog/CLG_log.h
Nick Alberelli f7e7a5d09e Logging: Add argument to list all available categories
New `--log-list-categories` command line argument to list all available
logging categories. This improves documentation of logging, allowing
users to understand what logging categories are available for use. It is
also useful for developers to understand what logging categories already
exist before making a change.

Implemented using static initializers to register all logrefs on startup.

Ref  #141754

Pull Request: https://projects.blender.org/blender/blender/pulls/146456
2025-09-26 17:33:26 +02:00

206 lines
7.1 KiB
C

/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup intern_clog
*
* C Logging Library (clog)
* ========================
*
* Usage
* -----
*
* - `CLG_LOGREF_DECLARE_GLOBAL` macro to declare #CLG_LogRef pointers.
* - `CLOG_` prefixed macros for logging.
*
* Identifiers
* -----------
*
* #CLG_LogRef holds an identifier which defines the category of the logger.
*
* You can define and use identifiers as needed, logging will lazily initialize them.
*
* By convention lower case dot separated identifiers are used, eg:
* `module.sub_module`, this allows filtering by `module.*`,
* see #CLG_type_filter_include, #CLG_type_filter_exclude
*
* There is currently no functionality to remove a category once it's created.
*/
#pragma once
#ifdef __GNUC__
# define _CLOG_ATTR_NONNULL(args...) __attribute__((nonnull(args)))
#else
# define _CLOG_ATTR_NONNULL(...)
#endif
#ifdef __GNUC__
# define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param) \
__attribute__((format(printf, format_param, dots_param)))
#else
# define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param)
#endif
#define STRINGIFY_ARG(x) "" #x
#define STRINGIFY_APPEND(a, b) "" a #b
#define STRINGIFY(x) STRINGIFY_APPEND("", x)
struct CLogContext;
enum CLG_Level {
/* Similar to assert. This logs the message, then a stack trace and abort. */
CLG_LEVEL_FATAL = 0,
/* An error we can recover from, should not happen. */
CLG_LEVEL_ERROR = 1,
/* General warnings (which aren't necessary to show to users). */
CLG_LEVEL_WARN = 2,
/* Information about devices, files, configuration, user operations. */
CLG_LEVEL_INFO = 3,
/* Debugging information for developers. */
CLG_LEVEL_DEBUG = 4,
/* Very verbose code execution tracing. */
CLG_LEVEL_TRACE = 5,
};
#define CLG_LEVEL_LEN (CLG_LEVEL_TRACE + 1)
/* Each logger ID has one of these. */
struct CLG_LogType {
struct CLG_LogType *next;
char identifier[64];
/** FILE output. */
struct CLogContext *ctx;
/** Control behavior. */
CLG_Level level;
};
struct CLG_LogRef {
CLG_LogRef(const char *identifier);
const char *identifier;
CLG_LogType *type;
struct CLG_LogRef *next;
};
void CLG_log_str(const CLG_LogType *lg,
enum CLG_Level level,
const char *file_line,
const char *fn,
const char *message) _CLOG_ATTR_NONNULL(1, 3, 4, 5);
void CLG_logf(const CLG_LogType *lg,
enum CLG_Level level,
const char *file_line,
const char *fn,
const char *format,
...) _CLOG_ATTR_NONNULL(1, 3, 4, 5) _CLOG_ATTR_PRINTF_FORMAT(5, 6);
void CLG_log_raw(const CLG_LogType *lg, const char *message);
/* Main initializer and destructor (per session, not logger). */
void CLG_init();
void CLG_exit();
void CLG_output_set(void *file_handle);
void CLG_output_use_source_set(int value);
void CLG_output_use_basename_set(int value);
void CLG_output_use_timestamp_set(int value);
void CLG_output_use_memory_set(int value);
void CLG_error_fn_set(void (*error_fn)(void *file_handle));
void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle));
void CLG_backtrace_fn_set(void (*fatal_fn)(void *file_handle));
void CLG_type_filter_include(const char *type_match, int type_match_len);
void CLG_type_filter_exclude(const char *type_match, int type_match_len);
void CLG_level_set(CLG_Level level);
void CLG_logref_init(CLG_LogRef *clg_ref);
void CLG_logref_register(CLG_LogRef *clg_ref);
void CLG_logref_list_all(void (*callback)(const char *identifier, void *user_data),
void *user_data);
int CLG_color_support_get(CLG_LogRef *clg_ref);
/* When true, quiet any NOCHECK logs that would otherwise be printed regardless of log filters
* and levels. This is used so command line tools can control output without unnecessary noise.
*
* Note this does not silence log filters and levels that have been explicitly enabled. */
void CLG_quiet_set(bool quiet);
bool CLG_quiet_get();
inline CLG_LogRef::CLG_LogRef(const char *identifier)
: identifier(identifier), type(nullptr), next(nullptr)
{
CLG_logref_register(this);
}
/** Declare outside function, declare as extern in header. */
#define CLG_LOGREF_DECLARE_GLOBAL(var, id) \
static CLG_LogRef _static_##var = {id}; \
CLG_LogRef *var = &_static_##var
/** Initialize struct once. */
#define CLOG_ENSURE(clg_ref) \
((clg_ref)->type ? (clg_ref)->type : (CLG_logref_init(clg_ref), (clg_ref)->type))
#define CLOG_CHECK(clg_ref, verbose_level, ...) \
((void)CLOG_ENSURE(clg_ref), ((clg_ref)->type->level >= verbose_level))
#define CLOG_AT_LEVEL(clg_ref, verbose_level, ...) \
{ \
const CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
if (_lg_ty->level >= verbose_level) { \
CLG_logf(_lg_ty, verbose_level, __FILE__ ":" STRINGIFY(__LINE__), __func__, __VA_ARGS__); \
} \
} \
((void)0)
#define CLOG_AT_LEVEL_NOCHECK(clg_ref, verbose_level, ...) \
{ \
const CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
if (!CLG_quiet_get() || _lg_ty->level >= verbose_level) { \
CLG_logf(_lg_ty, verbose_level, __FILE__ ":" STRINGIFY(__LINE__), __func__, __VA_ARGS__); \
} \
} \
((void)0)
#define CLOG_STR_AT_LEVEL(clg_ref, verbose_level, str) \
{ \
const CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
if (_lg_ty->level >= verbose_level) { \
CLG_log_str(_lg_ty, verbose_level, __FILE__ ":" STRINGIFY(__LINE__), __func__, str); \
} \
} \
((void)0)
#define CLOG_STR_AT_LEVEL_NOCHECK(clg_ref, verbose_level, str) \
{ \
const CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
if (!CLG_quiet_get() || _lg_ty->level >= verbose_level) { \
CLG_log_str(_lg_ty, verbose_level, __FILE__ ":" STRINGIFY(__LINE__), __func__, str); \
} \
} \
((void)0)
/* Log with format string. */
#define CLOG_FATAL(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_FATAL, __VA_ARGS__)
#define CLOG_ERROR(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_ERROR, __VA_ARGS__)
#define CLOG_WARN(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_WARN, __VA_ARGS__)
#define CLOG_INFO(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_INFO, __VA_ARGS__)
#define CLOG_DEBUG(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_DEBUG, __VA_ARGS__)
#define CLOG_TRACE(clg_ref, ...) CLOG_AT_LEVEL(clg_ref, CLG_LEVEL_TRACE, __VA_ARGS__)
/* Log single string. */
#define CLOG_STR_FATAL(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_FATAL, str)
#define CLOG_STR_ERROR(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_ERROR, str)
#define CLOG_STR_WARN(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_WARN, str)
#define CLOG_STR_INFO(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_INFO, str)
#define CLOG_STR_DEBUG(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_DEBUG, str)
#define CLOG_STR_TRACE(clg_ref, str) CLOG_STR_AT_LEVEL(clg_ref, CLG_LEVEL_TRACE, str)
/* Log regardless of filters and levels, for a few important messages like blend save and load.
* Only #CLG_quiet_set will silence these. */
#define CLOG_INFO_NOCHECK(clg_ref, format, ...) \
CLOG_AT_LEVEL_NOCHECK(clg_ref, CLG_LEVEL_INFO, format, __VA_ARGS__)
#define CLOG_STR_INFO_NOCHECK(clg_ref, str) CLOG_STR_AT_LEVEL_NOCHECK(clg_ref, CLG_LEVEL_INFO, str)