Add alternative logging backends

This commit allows for btop to log to either syslog(3) or systemd's
journal
This commit is contained in:
nobounce 2023-09-18 20:19:39 +02:00 committed by Steffen Winter
parent e04cbcde8a
commit a9061dfad2
No known key found for this signature in database
GPG key ID: D4053C3600EF3B1F
6 changed files with 147 additions and 21 deletions

View file

@ -36,6 +36,8 @@ include(CMakeDependentOption)
option(BTOP_STATIC "Link btop statically" OFF)
option(BTOP_LTO "Enable LTO" ON)
option(BTOP_USE_MOLD "Use mold to link btop" OFF)
option(BTOP_SYSLOG "Log with syslog instead of log file" OFF)
option(BTOP_JOURNALD "Directly log to journald instead of log file" OFF)
option(BTOP_PEDANTIC "Enable a bunch of additional warnings" OFF)
option(BTOP_WERROR "Compile with warnings as errors" OFF)
option(BTOP_GPU "Enable GPU support" ON)
@ -60,6 +62,7 @@ add_executable(btop
src/btop_config.cpp
src/btop_draw.cpp
src/btop_input.cpp
src/btop_log.cpp
src/btop_menu.cpp
src/btop_shared.cpp
src/btop_theme.cpp
@ -181,6 +184,22 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(btop PRIVATE Threads::Threads)
# Logging
if(BTOP_JOURNALD)
find_package(Systemd REQUIRED)
target_compile_definitions(btop PRIVATE HAVE_JOURNALD)
target_link_libraries(btop PRIVATE Systemd::Journald)
message(STATUS "Using journald as logging backend")
elseif(BTOP_SYSLOG)
check_include_file_cxx("syslog.h" HAVE_SYSLOG)
if(HAVE_SYSLOG)
target_compile_definitions(btop PRIVATE HAVE_SYSLOG)
message(STATUS "Using syslog as logging backend")
endif()
else()
message(STATUS "Using plain log file")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_link_libraries(btop PRIVATE $<LINK_LIBRARY:FRAMEWORK,CoreFoundation)
target_link_libraries(btop PRIVATE $<LINK_LIBRARY:FRAMEWORK,IOKit)

View file

@ -167,6 +167,13 @@ else
LTO := $(THREADS)
endif
#? Enable journald direct logging
ifeq ($(ENABLE_JOURNALD),true)
override ADDFLAGS += $(shell echo "int main() {}" | $(CXX) -include "systemd/sd-journal.h" -lsystemd -o /dev/null -x c++ - >/dev/null 2>&1 && echo "-DHAVE_JOURNALD -lsystemd")
else ifeq ($(ENABLE_SYSLOG),true)
override ADDFLAGS += $(strip $(shell echo "int main() {}" | $(CXX) -include "syslog.h" -o /dev/null -x c++ - >/dev/null 2>&1 && echo "-DHAVE_SYSLOG"))
endif
#? The Directories, Source, Includes, Objects and Binary
SRCDIR := src
INCDIRS := include $(wildcard lib/**/include)

View file

@ -493,6 +493,8 @@ Also needs a UTF8 locale and a font that covers:
| `-DBTOP_STATIC=<ON\|OFF>` | Enables static linking (OFF by default) |
| `-DBTOP_LTO=<ON\|OFF>` | Enables link time optimization (ON by default) |
| `-DBTOP_USE_MOLD=<ON\|OFF>` | Use mold to link btop (OFF by default) |
| `-DBTOP_SYSLOG=<ON\|OFF>` | Log to syslog instead of a log file (OFF by default) |
| `-DBTOP_JOURNALD=<ON\|OFF>` | Log to journald instead of a log file (OFF by default) |
| `-DBTOP_PEDANTIC=<ON\|OFF>` | Compile with additional warnings (OFF by default) |
| `-DBTOP_WERROR=<ON\|OFF>` | Compile with warnings as errors (OFF by default) |
| `-DBTOP_GPU=<ON\|OFF>` | Enable GPU support (ON by default) |
@ -764,6 +766,7 @@ Also needs a UTF8 locale and a font that covers:
| `-DBTOP_STATIC=<ON\|OFF>` | Enables static linking (OFF by default) |
| `-DBTOP_LTO=<ON\|OFF>` | Enables link time optimization (ON by default) |
| `-DBTOP_USE_MOLD=<ON\|OFF>` | Use mold to link btop (OFF by default) |
| `-DBTOP_SYSLOG=<ON\|OFF>` | Log to syslog instead of a log file (OFF by default) |
| `-DBTOP_PEDANTIC=<ON\|OFF>` | Compile with additional warnings (OFF by default) |
| `-DBTOP_WERROR=<ON\|OFF>` | Compile with warnings as errors (OFF by default) |
| `-DCMAKE_INSTALL_PREFIX=<path>` | The installation prefix ('/usr/local' by default) |

View file

@ -0,0 +1,26 @@
# SPDX-License-Identifier: Apache-2.0
#
# Find systemd
#
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
find_path(Systemd_INCLUDE_DIR NAMES systemd/sd-journal.h)
find_library(Systemd_LIBRARY NAMES systemd)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Systemd REQUIRED_VARS Systemd_LIBRARY Systemd_INCLUDE_DIR)
if(Systemd_FOUND AND NOT TARGET Systemd::Systemd)
add_library(Systemd::Systemd UNKNOWN IMPORTED)
set_target_properties(Systemd::Systemd PROPERTIES
IMPORTED_LOCATION "${Systemd_LIBRARY}"
)
endif()
if(Systemd_FOUND AND NOT TARGET Systemd::Journald)
add_library(Systemd::Journald ALIAS Systemd::Systemd)
endif()
mark_as_advanced(Systemd_INCLUDE_DIR Systemd_LIBRARY)
endif()

View file

@ -5,9 +5,16 @@
#include <filesystem>
#include <iostream>
#include <mutex>
#include <source_location>
#include <string>
#include <string_view>
#if defined(HAVE_JOURNALD)
#include <systemd/sd-journal.h>
#elif defined(HAVE_SYSLOG)
#include <syslog.h>
#endif
#include <fmt/core.h>
#include <fmt/ostream.h>
@ -18,7 +25,7 @@ namespace fs = std::filesystem;
namespace Logger {
using namespace Tools;
std::mutex log_mtx {};
std::mutex log_mtx{};
bool first = true;
const string tdf = "%Y/%m/%d (%T) | ";
@ -44,12 +51,40 @@ namespace Logger {
void set(const string& level) { loglevel = v_index(log_levels, level); }
void log_write(const size_t level, const std::string_view msg) {
if (loglevel < level or logfile.empty()) {
void log(const size_t level, const std::string_view msg, [[maybe_unused]] const std::source_location location) {
if (loglevel < level) {
return;
}
std::lock_guard lock {log_mtx};
std::lock_guard lock{log_mtx};
lose_priv neutered{};
#if defined(HAVE_JOURNALD) || defined(HAVE_SYSLOG)
int status = LOG_DEBUG;
switch (level) {
case 1: status = LOG_ERR; break;
case 2: status = LOG_WARNING; break;
case 3: status = LOG_INFO; break;
case 4: status = LOG_DEBUG; break;
}
#endif
#if defined(HAVE_JOURNALD)
using namespace std::literals;
sd_journal_print_with_location( // NOLINT(cppcoreguidelines-pro-type-vararg)
status,
("CODE_FILE="s + location.file_name()).data(),
("CODE_LINE="s + std::to_string(location.line())).data(),
location.function_name(),
"%s",
msg.data()
);
#elif defined(HAVE_SYSLOG)
syslog(status, "%s", msg.data()); // NOLINT(cppcoreguidelines-pro-type-vararg)
#else
if (logfile.empty()) {
return;
}
std::error_code ec;
try {
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
@ -80,5 +115,6 @@ namespace Logger {
logfile.clear();
throw std::runtime_error(fmt::format("Exception in Logger::log_write() : {}", e.what()));
}
#endif
}
} // namespace Logger

View file

@ -3,6 +3,7 @@
#pragma once
#include <filesystem>
#include <source_location>
#include <string_view>
#include <vector>
@ -16,30 +17,64 @@ namespace Logger {
"INFO",
"DEBUG",
};
extern std::filesystem::path logfile;
//* Set log level, valid arguments: "DISABLED", "ERROR", "WARNING", "INFO" and "DEBUG"
void set(const std::string& level);
void log_write(const size_t level, const std::string_view msg);
void log(const size_t level, const std::string_view msg, const std::source_location location);
template <typename... T>
inline void error(const fmt::format_string<T...> fmt, T&&... args) {
log_write(1, fmt::format(fmt, std::forward<T>(args)...));
}
template<typename... Args>
struct error { // NOLINT(readability-identifier-naming)
constexpr error(
const fmt::format_string<Args...> fmt, Args&&... args,
const std::source_location location = std::source_location::current()
) {
log(1, fmt::format(fmt, std::forward<Args>(args)...), location);
}
};
template <typename... T>
inline void warning(const fmt::format_string<T...> fmt, T&&... args) {
log_write(2, fmt::format(fmt, std::forward<T>(args)...));
}
template<typename... Args>
struct warning { // NOLINT(readability-identifier-naming)
constexpr warning(
const fmt::format_string<Args...> fmt, Args&&... args,
const std::source_location location = std::source_location::current()
) {
log(2, fmt::format(fmt, std::forward<Args>(args)...), location);
}
};
template <typename... T>
inline void info(const fmt::format_string<T...> fmt, T&&... args) {
log_write(3, fmt::format(fmt, std::forward<T>(args)...));
}
template<typename... Args>
struct info { // NOLINT(readability-identifier-naming)
constexpr info(
const fmt::format_string<Args...> fmt, Args&&... args,
const std::source_location location = std::source_location::current()
) {
log(3, fmt::format(fmt, std::forward<Args>(args)...), location);
}
};
template <typename... T>
inline void debug(const fmt::format_string<T...> fmt, T&&... args) {
log_write(4, fmt::format(fmt, std::forward<T>(args)...));
}
template<typename... Args>
struct debug { // NOLINT(readability-identifier-naming)
constexpr debug(
const fmt::format_string<Args...> fmt, Args&&... args,
const std::source_location location = std::source_location::current()
) {
log(4, fmt::format(fmt, std::forward<Args>(args)...), location);
}
};
// Deduction guide to allow default arguement after variadic template
template<typename... Args>
error(const std::string_view, Args&&...) -> error<Args...>;
template<typename... Args>
warning(const std::string_view, Args&&...) -> warning<Args...>;
template<typename... Args>
info(const std::string_view, Args&&...) -> info<Args...>;
template<typename... Args>
debug(const std::string_view, Args&&...) -> debug<Args...>;
} // namespace Logger