diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..165b6820 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,55 @@ +# Changelog + +## v2.0.0 (Unreleased) + +### Breaking Changes + +* **C++17 minimum required** - Raised from C++11 in v1.x. +* **Header-only mode removed** - spdlog is now a compiled library only. `SPDLOG_HEADER_ONLY` and `SPDLOG_COMPILED_LIB` options are gone. All `-inl.h` files are removed. +* **CMake minimum version raised to 3.23**. +* **`filename_t` is now `std::filesystem::path`** instead of `std::string`. +* **Log level type changed** - `level::level_enum` is now `enum class level : uint8_t`. +* **Logger creation simplified** - convenience functions like `spdlog::basic_logger_mt(...)` are replaced with `spdlog::create(name, args...)`. +* **Async API changed** - `spdlog/async.h`, `spdlog::async_logger`, and `spdlog::init_thread_pool()` are removed. Use `spdlog/sinks/async_sink.h` with the new `async_sink` class instead. See README for examples. +* **Global registry removed** - `spdlog::get()`, `spdlog::register_logger()`, `spdlog::drop()`, `spdlog::drop_all()`, `spdlog::apply_all()` are all removed. +* **Default logger renamed** - `spdlog::default_logger()` is now `spdlog::global_logger()`. Similarly `set_default_logger()` is now `set_global_logger()`. +* **`std::format` support removed** - The `SPDLOG_USE_STD_FORMAT` option is gone. spdlog now uses [fmt](https://github.com/fmtlib/fmt) exclusively (bundled 12.1.0 by default, or external via `SPDLOG_FMT_EXTERNAL=ON`). +* **Bundled fmt headers moved** - `spdlog/fmt/bundled/` is gone. fmt is now fetched via CMake. Include `fmt/` headers directly. +* **Configuration module removed** - `spdlog/cfg/env.h` and `spdlog/cfg/argv.h` are gone. `spdlog::cfg::load_env_levels()` and `spdlog::cfg::load_argv_levels()` no longer exist. +* **Backtrace feature removed** - `spdlog::enable_backtrace()` and `spdlog::dump_backtrace()` are gone. +* **Wide-char support removed** - `SPDLOG_WCHAR_TO_UTF8_SUPPORT` and `SPDLOG_WCHAR_FILENAMES` options are gone. +* **`spdlog::flush_every()` removed**. +* **`SPDLOG_EOL` define removed**. +* **`tweakme.h` removed** - compile-time options are now in CMakeLists.txt. +* **Logger destructor is no longer virtual** - `~logger() = default`. +* **`log_msg.level` field renamed to `log_msg.log_level`** - affects custom sink implementations. +* **All log methods are now `noexcept`**. +* **`SPDLOG_NOEXCEPT` and `SPDLOG_CONSTEXPR` macros removed** - replaced with standard C++17 keywords. + +### New Features + +* New `async_sink` class with configurable overflow policies (`block`, `overrun_oldest`, `discard_new`). +* `async_sink::with(...)` convenience factory for creating async sinks. +* Thread start/stop callbacks in async sink configuration. +* `source_loc` now includes `short_filename` (auto-computed basename). +* Centralized error handling via `err_helper` with rate limiting. +* Improved `constexpr` and `noexcept` usage throughout. + +### Migrating from v1.x + +#### Async logging + +Before (v1.x): +```c++ +#include "spdlog/async.h" +spdlog::init_thread_pool(8192, 1); +auto logger = spdlog::basic_logger_mt("async_logger", "logs/async.txt"); +``` + +After (v2.x): +```c++ +#include "spdlog/sinks/async_sink.h" +#include "spdlog/sinks/basic_file_sink.h" +auto sink = spdlog::sinks::async_sink::with("logs/async.txt"); +auto logger = std::make_shared("async_logger", sink); +``` diff --git a/INSTALL b/INSTALL index 5a3a9e6b..fb54349f 100644 --- a/INSTALL +++ b/INSTALL @@ -1,12 +1,7 @@ -CMake: - add_executable(example example.cpp) - target_link_libraries(example spdlog::spdlog) - -Tested on: -gcc 4.8.1 and above -clang 3.5 -Visual Studio 2013 - - - - +CMake: + add_executable(example example.cpp) + target_link_libraries(example spdlog::spdlog) + +Requirements: + C++17 or later + CMake 3.23 or later diff --git a/README.md b/README.md index 15a63c72..fe1eadbe 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,39 @@ see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v2.x/example/ * vcpkg: `vcpkg install spdlog` * conan: `conan install --requires=spdlog/[*]` * conda: `conda install -c conda-forge spdlog` -* build2: ```depends: spdlog ^1.8.2``` +* build2: ```depends: spdlog ^2.0.0``` +## What's new in v2.x + +spdlog v2.x is a major rewrite with cleaner design and a simpler API. Key changes from v1.x: + +* **C++17 required** (v1.x supported C++11). Uses `std::filesystem`, `std::string_view`, etc. +* **Compiled library only** - header-only mode has been removed. All `-inl.h` files are gone. +* **CMake minimum version raised to 3.23**. +* **`filename_t` is now `std::filesystem::path`** instead of `std::string`. +* **Log level type changed** - `level::level_enum` is now `enum class level : uint8_t`. +* **Simpler logger creation** - convenience functions like `spdlog::basic_logger_mt(...)` are replaced with a single template: `spdlog::create(logger_name, sink_args...)`. +* **New async model** - the global thread pool (`spdlog::init_thread_pool()`) and `spdlog::async_logger` are replaced by `async_sink`, a regular sink with its own worker thread and configurable overflow policy (`block`, `overrun_oldest`, `discard_new`). +* **No global registry** - `spdlog::get()`, `spdlog::register_logger()`, `spdlog::drop()`, `spdlog::drop_all()`, `spdlog::apply_all()` are all removed. Use `spdlog::global_logger()` to access the default logger, or hold your own `shared_ptr`. +* **Default logger renamed** - `spdlog::default_logger()` is now `spdlog::global_logger()`. +* **`std::format` support removed** - `SPDLOG_USE_STD_FORMAT` is gone. Uses [fmt](https://github.com/fmtlib/fmt) exclusively. +* **Bundled fmt headers moved** - `spdlog/fmt/bundled/` is gone. fmt is now fetched via CMake. Include `fmt/` headers directly. +* **Backtrace feature removed** - `spdlog::enable_backtrace()` and `spdlog::dump_backtrace()` are gone. +* **Configuration module removed** - `spdlog/cfg/env.h` and `spdlog/cfg/argv.h` (env/argv level loading) are gone. +* **Wide-char support removed** - `SPDLOG_WCHAR_TO_UTF8_SUPPORT` and `SPDLOG_WCHAR_FILENAMES` are gone. +* **`spdlog::flush_every()` removed**. +* **`SPDLOG_EOL` define removed**. +* **`tweakme.h` removed** - compile-time options are now in CMakeLists.txt. +* **Logger destructor is no longer virtual**. +* **`log_msg.level` field renamed to `log_msg.log_level`** (affects custom sinks). +* **All log methods are now `noexcept`**. + +See [CHANGELOG.md](CHANGELOG.md) for migration examples. + ## Features * Very fast (see [benchmarks](#benchmarks) below). -* Headers only or compiled +* Compiled library (requires C++17 or later) * Feature-rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library (bundled **12.1.0** by default; use `SPDLOG_FMT_EXTERNAL=ON` for a system **fmt**, **12.x** recommended). * Asynchronous mode (optional) * [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. @@ -55,7 +82,6 @@ see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v2.x/example/ * Log to Qt widgets ([example](#log-to-qt-with-nice-colors)). * Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets. * Log filtering - log levels can be modified at runtime as well as compile time. -* Support for loading log levels from argv or environment var. ## Usage samples @@ -94,10 +120,10 @@ int main() #include "spdlog/sinks/stdout_color_sinks.h" void stdout_example() { - // create a color multi-threaded logger - auto console = spdlog::stdout_color_mt("console"); - auto err_logger = spdlog::stderr_color_mt("stderr"); - spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)"); + // Create color multithreading logger + auto console = spdlog::create("console"); + // or for stderr: + // auto console = spdlog::create("console"); } ``` @@ -107,13 +133,13 @@ void stdout_example() #include "spdlog/sinks/basic_file_sink.h" void basic_logfile_example() { - try + try { - auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt"); + auto logger = spdlog::create("basic_logger", "logs/basic-log.txt", true); } catch (const spdlog::spdlog_ex &ex) { - std::cout << "Log init failed: " << ex.what() << std::endl; + std::printf("Log init failed: %s\n", ex.what()); } } ``` @@ -126,31 +152,27 @@ void rotating_example() // Create a file rotating logger with 5 MB size max and 3 rotated files auto max_size = 1048576 * 5; auto max_files = 3; - auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files); + auto logger = spdlog::create("some_logger_name", "logs/rotating.txt", max_size, max_files); } ``` --- #### Daily files ```c++ - #include "spdlog/sinks/daily_file_sink.h" void daily_example() { // Create a daily logger - a new file is created every day at 2:30 am - auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); + auto logger = spdlog::create("daily_logger", "logs/daily.txt", 2, 30); } - ``` --- -#### Periodic flush +#### Flush ```c++ -// periodically flush all *registered* loggers every 3 seconds: -// warning: only use if all your loggers are thread-safe ("_mt" loggers) -spdlog::flush_every(std::chrono::seconds(3)); - +// Set flush level for the global logger - all messages with level >= warn will be flushed automatically +spdlog::flush_on(spdlog::level::warn); ``` --- @@ -179,20 +201,21 @@ void stopwatch_example() // {:n} - don't split the output into lines. // {:a} - show ASCII if :n is not set. -#include "spdlog/fmt/bin_to_hex.h" +#include "spdlog/bin_to_hex.h" void binary_example() { - auto console = spdlog::get("console"); - std::array buf; - console->info("Binary example: {}", spdlog::to_hex(buf)); - console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); + std::vector buf; + for (int i = 0; i < 80; i++) { + buf.push_back(static_cast(i & 0xff)); + } + spdlog::info("Binary example: {}", spdlog::to_hex(buf)); + spdlog::info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); // more examples: // logger->info("uppercase: {:X}", spdlog::to_hex(buf)); // logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf)); // logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf)); } - ``` --- @@ -220,34 +243,32 @@ void multi_sink_example() --- #### User-defined callbacks about log events ```c++ - +#include "spdlog/sinks/callback_sink.h" // create a logger with a lambda function callback, the callback will be called // each time something is logged to the logger void callback_example() { - auto callback_sink = std::make_shared([](const spdlog::details::log_msg &msg) { - // for example you can be notified by sending an email to yourself - }); - callback_sink->set_level(spdlog::level::err); - - auto console_sink = std::make_shared(); - spdlog::logger logger("custom_callback_logger", {console_sink, callback_sink}); - - logger.info("some info log"); - logger.error("critical issue"); // will notify you + auto logger = spdlog::create("custom_callback_logger", + [](const spdlog::details::log_msg & /*msg*/) { + // do what you need to do with msg + }); } ``` --- #### Asynchronous logging ```c++ -#include "spdlog/async.h" +#include "spdlog/sinks/async_sink.h" #include "spdlog/sinks/basic_file_sink.h" void async_example() { - // TODO + using spdlog::sinks::async_sink; + auto sink = async_sink::with("logs/async_log.txt", true); + auto logger = std::make_shared("async_logger", sink); + for (int i = 1; i < 101; ++i) { + logger->info("Async message #{}", i); + } } - ``` --- @@ -305,10 +326,8 @@ void custom_flags_example() void err_handler_example() { // can be set globally or per logger(logger->set_error_handler(..)) - spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); }); - spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3); + spdlog::set_error_handler([](const std::string &msg) { printf("*** Custom log error handler: %s ***\n", msg.c_str()); }); } - ``` --- @@ -318,7 +337,7 @@ void err_handler_example() void syslog_example() { std::string ident = "spdlog-example"; - auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); + auto syslog_logger = spdlog::create("syslog", ident, LOG_PID); syslog_logger->warn("This is warning that will end up in syslog."); } ``` @@ -329,47 +348,27 @@ void syslog_example() void android_example() { std::string tag = "spdlog-android"; - auto android_logger = spdlog::android_logger_mt("android", tag); + auto android_logger = spdlog::create("android", tag); android_logger->critical("Use \"adb shell logcat\" to view this message."); } ``` ---- -#### Load log levels from the env variable or argv - -```c++ -#include "spdlog/cfg/env.h" -int main (int argc, char *argv[]) -{ - spdlog::cfg::load_env_levels(); - // or from the command line: - // ./example SPDLOG_LEVEL=info,mylogger=trace - // #include "spdlog/cfg/argv.h" // for loading levels from argv - // spdlog::cfg::load_argv_levels(argc, argv); -} -``` -So then you can: - -```console -$ export SPDLOG_LEVEL=info,mylogger=trace -$ ./example -``` - - --- #### Log file open/close event handlers ```c++ -// You can get callbacks from spdlog before/after a log file has been opened or closed. +// You can get callbacks from spdlog before/after a log file has been opened or closed. // This is useful for cleanup procedures or for adding something to the start/end of the log file. void file_events_example() { // pass the spdlog::file_event_handlers to file sinks for open/close log file notifications spdlog::file_event_handlers handlers; - handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); }; - handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); }; - handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); }; - handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); }; - auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers); + handlers.before_open = [](spdlog::filename_t) { spdlog::trace("Before opening logfile"); }; + handlers.after_open = [](spdlog::filename_t, std::FILE *fstream) { fputs("After opening\n", fstream); }; + handlers.before_close = [](spdlog::filename_t, std::FILE *fstream) { fputs("Before closing\n", fstream); }; + handlers.after_close = [](spdlog::filename_t) { spdlog::trace("After closing logfile"); }; + auto file_sink = std::make_shared("logs/events-sample.txt", true, handlers); + spdlog::logger my_logger("some_logger", file_sink); + my_logger.trace("Some log line"); } ``` @@ -378,7 +377,7 @@ void file_events_example() ```c++ void replace_global_logger_example() { - auto new_logger = spdlog::basic_logger_mt("new_global_logger", "logs/new-default-log.txt", true); + auto new_logger = spdlog::create("new_global_logger", "logs/new-default-log.txt", true); spdlog::set_global_logger(new_logger); spdlog::info("new logger log message"); } @@ -395,7 +394,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) auto log_widget = new QTextEdit(this); setCentralWidget(log_widget); int max_lines = 500; // keep the text widget to max 500 lines. remove old lines if needed. - auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget, max_lines); + auto logger = spdlog::create("qt_logger", log_widget, max_lines); logger->info("Some info message"); } ``` diff --git a/example/example.cpp b/example/example.cpp index acd93601..8cb49647 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -238,7 +238,7 @@ void syslog_example() { #include "spdlog/sinks/android_sink.h" void android_example() { std::string tag = "spdlog-android"; - auto android_logger = spdlog::android_logger_mt("android", tag); + auto android_logger = spdlog::create("android", tag); android_logger->critical("Use \"adb shell logcat\" to view this message."); } #endif