diff --git a/example/example.cpp b/example/example.cpp index 60a91f76..750d2743 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -31,295 +31,10 @@ void replace_default_logger_example(); #include "spdlog/spdlog.h" #include "spdlog/version.h" -int main(int, char *[]) { - // Log levels can be loaded from argv/env using "SPDLOG_LEVEL" - load_levels_example(); - SPDLOG_INFO("This message should be displayed.."); - spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH); - spdlog::warn("Easy padding in numbers like {:08d}", 12); - spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); - spdlog::info("Support for floats {:03.2f}", 1.23456); - spdlog::info("Positional args are {1} {0}..", "too", "supported"); - spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left"); +#include "fmt/xchar.h" +int main(int, char *[]) { + std::wstring wideStr = L"Hello, Wide World!"; - // Runtime log levels - spdlog::set_level(spdlog::level::info); // Set global log level to info - spdlog::debug("This message should not be displayed!"); - spdlog::set_level(spdlog::level::trace); // Set specific logger's log level - spdlog::debug("This message should be displayed.."); - - // Customize msg format for all loggers - spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v"); - spdlog::info("This an info message with custom format"); - spdlog::set_pattern("%+"); // back to default format - spdlog::set_level(spdlog::level::info); - - try { - stdout_logger_example(); - basic_example(); - rotating_example(); - daily_example(); - callback_example(); - async_example(); - binary_example(); - vector_example(); - multi_sink_example(); - user_defined_example(); - err_handler_example(); - trace_example(); - stopwatch_example(); - udp_example(); - custom_flags_example(); - file_events_example(); - replace_default_logger_example(); - - // Flush all *registered* loggers using a worker thread every 3 seconds. - // note: registered loggers *must* be thread safe for this to work correctly! - spdlog::flush_every(std::chrono::seconds(3)); - - // Apply some function on all registered loggers - spdlog::apply_all([&](std::shared_ptr l) { l->info("End of example."); }); - - // Release all spdlog resources, and drop all loggers in the registry. - // This is optional (only mandatory if using windows + async log). - spdlog::shutdown(); - } - - // Exceptions will only be thrown upon failed logger or sink construction (not during logging). - catch (const spdlog::spdlog_ex &ex) { - std::printf("Log initialization failed: %s\n", ex.what()); - return 1; - } -} - -#include "spdlog/sinks/stdout_color_sinks.h" -// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed. -void stdout_logger_example() { - // Create color multithreading logger. - auto console = spdlog::stdout_color_mt("console"); - // or for stderr: - // auto console = spdlog::stderr_color_mt("error-logger"); -} - -#include "spdlog/sinks/basic_file_sink.h" -void basic_example() { - // Create basic file logger (not rotated). - auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt", true); -} - -#include "spdlog/sinks/rotating_file_sink.h" -void rotating_example() { - // Create a file rotating logger with 5mb size max and 3 rotated files. - auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); -} - -#include "spdlog/sinks/daily_file_sink.h" -void daily_example() { - // Create a daily logger - a new file is created every day on 2:30am. - auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); -} - -#include "spdlog/sinks/callback_sink.h" -void callback_example() { - // Create the logger - auto logger = spdlog::callback_logger_mt("custom_callback_logger", [](const spdlog::details::log_msg & /*msg*/) { - // do what you need to do with msg - }); -} - -void load_levels_example() { - // Set the log level to "info" and mylogger to "trace": - // SPDLOG_LEVEL=info,mylogger=trace && ./example - // must #include "spdlog/cfg/env.h" - spdlog::cfg::load_env_levels(); - // or from command line: - // ./example SPDLOG_LEVEL=info,mylogger=trace - // #include "spdlog/cfg/argv.h" // for loading levels from argv - // spdlog::cfg::load_argv_levels(args, argv); -} - -#include "spdlog/async.h" -void async_example() { - // Default thread pool settings can be modified *before* creating the async logger: - // spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread. - auto async_file = spdlog::basic_logger_mt("async_file_logger", "logs/async_log.txt"); - // alternatively: - // auto async_file = - // spdlog::create_async("async_file_logger", - // "logs/async_log.txt"); - - for (int i = 1; i < 101; ++i) { - async_file->info("Async message #{}", i); - } -} - -// Log binary data as hex. -// Many types of std::container types can be used. -// Iterator ranges are supported too. -// Format flags: -// {:X} - print in uppercase. -// {:s} - don't separate each byte with space. -// {:p} - don't print the position on each line start. -// {:n} - don't split the output to lines. - -#include "spdlog/fmt/bin_to_hex.h" -void binary_example() { - 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)); - // logger->info("hexdump style: {:a}", spdlog::to_hex(buf)); - // logger->info("hexdump style, 20 chars per line {:a}", spdlog::to_hex(buf, 20)); -} - -// Log a vector of numbers -#include "fmt/ranges.h" -void vector_example() { - std::vector vec = {1, 2, 3}; - spdlog::info("Vector example: {}", vec); -} - -// Compile time log levels. -// define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE) -void trace_example() { - // trace from default logger - SPDLOG_TRACE("Some trace message.. {} ,{}", 1, 3.23); - // debug from default logger - SPDLOG_DEBUG("Some debug message.. {} ,{}", 1, 3.23); - - // trace from logger object - auto logger = spdlog::get("file_logger"); - SPDLOG_LOGGER_TRACE(logger, "another trace message"); -} - -// stopwatch example -#include - -#include "spdlog/stopwatch.h" -void stopwatch_example() { - spdlog::stopwatch sw; - std::this_thread::sleep_for(std::chrono::milliseconds(123)); - spdlog::info("Stopwatch: {} seconds", sw); -} - -#include "spdlog/sinks/udp_sink.h" -void udp_example() { - spdlog::sinks::udp_sink_config cfg("127.0.0.1", 11091); - auto my_logger = spdlog::udp_logger_mt("udplog", cfg); - my_logger->set_level(spdlog::level::debug); - my_logger->info("hello world"); -} - -// A logger with multiple sinks (stdout and file) - each with a different format and log level. -void multi_sink_example() { - auto console_sink = std::make_shared(); - console_sink->set_level(spdlog::level::warn); - console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); - - auto file_sink = std::make_shared("logs/multisink.txt", true); - file_sink->set_level(spdlog::level::trace); - - spdlog::logger logger("multi_sink", {console_sink, file_sink}); - logger.set_level(spdlog::level::debug); - logger.warn("this should appear in both console and file"); - logger.info("this message should not appear in the console, only in the file"); -} - -// User defined types logging -struct my_type { - int i = 0; - explicit my_type(int i) - : i(i) {} -}; - -template <> -struct fmt::formatter : fmt::formatter { - auto format(my_type my, format_context &ctx) const -> decltype(ctx.out()) { - return fmt::format_to(ctx.out(), "[my_type i={}]", my.i); - } -}; - -void user_defined_example() { spdlog::info("user defined type: {}", my_type(14)); } - -// Custom error handler. Will be triggered on log failure. -void err_handler_example() { - // can be set globally or per logger(logger->set_error_handler(..)) - spdlog::set_error_handler([](const std::string &msg) { printf("*** Custom log error handler: %s ***\n", msg.c_str()); }); -} - -// syslog example (linux/osx/freebsd) -#ifndef _WIN32 - #include "spdlog/sinks/syslog_sink.h" -void syslog_example() { - std::string ident = "spdlog-example"; - auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); - syslog_logger->warn("This is warning that will end up in syslog."); -} -#endif - -// Android example. -#if defined(__ANDROID__) - #include "spdlog/sinks/android_sink.h" -void android_example() { - std::string tag = "spdlog-android"; - auto android_logger = spdlog::android_logger_mt("android", tag); - android_logger->critical("Use \"adb shell logcat\" to view this message."); -} -#endif - -// Log patterns can contain custom flags. -// this will add custom flag '%*' which will be bound to a instance -#include "spdlog/pattern_formatter.h" -class my_formatter_flag : public spdlog::custom_flag_formatter { -public: - void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override { - std::string some_txt = "custom-flag"; - dest.append(some_txt.data(), some_txt.data() + some_txt.size()); - } - - [[nodiscard]] std::unique_ptr clone() const override { return std::make_unique(); } -}; - -void custom_flags_example() { - auto formatter = std::make_unique(); - formatter->add_flag('*').set_pattern("[%n] [%*] [%^%l%$] %v"); - // set the new formatter using spdlog::set_formatter(formatter) or - // logger->set_formatter(formatter) spdlog::set_formatter(std::move(formatter)); -} - -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.u8string()); }; - handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { - spdlog::info("After opening {}", filename.u8string()); - fputs("After opening\n", fstream); - }; - handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { - spdlog::info("Before closing {}", filename.u8string()); - fputs("Before closing\n", fstream); - }; - handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename.u8string()); }; - auto file_sink = std::make_shared("logs/events-sample.txt", true, handlers); - spdlog::logger my_logger("some_logger", file_sink); - my_logger.info("Some log line"); -} - -void replace_default_logger_example() { - // store the old logger so we don't break other examples. - auto old_logger = spdlog::default_logger(); - - auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true); - spdlog::set_default_logger(new_logger); - spdlog::set_level(spdlog::level::info); - spdlog::debug("This message should not be displayed!"); - spdlog::set_level(spdlog::level::trace); - spdlog::debug("This message should be displayed.."); - spdlog::set_default_logger(old_logger); + // Print directly to the console + fmt::print(L"{}\n", wideStr); } \ No newline at end of file diff --git a/include/spdlog/fmt/fmt.h b/include/spdlog/fmt/fmt.h index a261e14b..4db29253 100644 --- a/include/spdlog/fmt/fmt.h +++ b/include/spdlog/fmt/fmt.h @@ -6,3 +6,4 @@ #pragma once #include "fmt/format.h" +#include "fmt/xchar.h" diff --git a/tests/test_daily_and_rotation_loggers.cpp b/tests/test_daily_and_rotation_loggers.cpp index 8d08d2fa..32958080 100644 --- a/tests/test_daily_and_rotation_loggers.cpp +++ b/tests/test_daily_and_rotation_loggers.cpp @@ -6,8 +6,7 @@ #include "spdlog/sinks/daily_file_sink.h" #include "spdlog/sinks/rotating_file_sink.h" -using file_t_sstream = std::basic_stringstream; -using spdlog::details::os::filename_to_str; +using filename_memory_buf_t = spdlog::memory_buf_t; TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") { using sink_type = spdlog::sinks::daily_file_sink; @@ -16,12 +15,9 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") { // calculate filename (time based) spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly"); - std::tm now_tm = spdlog::details::os::localtime(); - - - - //spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, - // tm.tm_mon + 1, tm.tm_mday); + std::tm tm = spdlog::details::os::localtime(); + auto w = spdlog::fmt_lib::format(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename.native(), tm.tm_year + 1900, + tm.tm_mon + 1, tm.tm_mday); auto logger = spdlog::create("logger", basename, 0, 0); for (int i = 0; i < 10; ++i) { @@ -29,17 +25,39 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") { } logger->flush(); - // calcluate expected filename and check if it exists with the expected message count - file_t_sstream stream; - auto sep = SPDLOG_FILENAME_T('-'); - stream << basename.native() << SPDLOG_FILENAME_T('_') << std::setfill(SPDLOG_FILENAME_T('0')) << std::setw(4) - << now_tm.tm_year + 1900 << sep << std::setw(2) << now_tm.tm_mon + 1 << sep << std::setw(2) << now_tm.tm_mday; - - require_message_count(filename_to_str(stream.str()), 10); + require_message_count(w, 10); } +struct custom_daily_file_name_calculator { + static spdlog::filename_t calc_filename(const spdlog::filename_t &basename, const tm &now_tm) { + auto w = spdlog::fmt_lib::format(SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename.native(), now_tm.tm_year + 1900, + now_tm.tm_mon + 1, now_tm.tm_mday); + return w; + } +}; +TEST_CASE("daily_logger with custom calculator", "[daily_logger]") { + using sink_type = spdlog::sinks::daily_file_sink; + + prepare_logdir(); + + // calculate filename (time based) + spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly"); + std::tm tm = spdlog::details::os::localtime(); + + auto w = spdlog::fmt_lib::format(SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename.native(), tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday); + + auto logger = spdlog::create("logger", basename, 0, 0); + for (int i = 0; i < 10; ++i) { + logger->info("Test message {}", i); + } + + logger->flush(); + require_message_count(w, 10); +} /* * File name calculations @@ -67,19 +85,14 @@ TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]") { TEST_CASE("daily_file_sink::daily_filename_calculator", "[daily_file_sink]") { // daily_YYYY-MM-DD_hh-mm.txt - auto now = spdlog::details::os::localtime(); - auto filename = - spdlog::sinks::daily_filename_calculator::calc_filename(SPDLOG_FILENAME_T("daily.txt"), now); - - file_t_sstream stream; - stream << "daily_" << now.tm_year + 1900 << "-" << std::setw(2) << std::setfill(SPDLOG_FILENAME_T('0')) << now.tm_mon + 1 << "-" - << std::setw(2) << std::setfill(SPDLOG_FILENAME_T('0')) << now.tm_mday << ".txt"; - REQUIRE(filename == std::filesystem::path(stream.str())); + // auto filename = + // spdlog::sinks::daily_filename_calculator::calc_filename(SPDLOG_FILENAME_T("daily.txt"), + // spdlog::details::os::localtime()); //// date regex based on https://www.regular-expressions.info/dates.html - //std::basic_regex re( - // SPDLOG_FILENAME_T(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)")); - //std::match_results match; - //REQUIRE(std::regex_match(filename, match, re)); + // std::basic_regex re( + // SPDLOG_FILENAME_T(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)")); + // std::match_results match; + // REQUIRE(std::regex_match(filename, match, re)); } #endif @@ -88,13 +101,8 @@ TEST_CASE("daily_file_sink::daily_filename_format_calculator", "[daily_file_sink // example-YYYY-MM-DD.log auto filename = spdlog::sinks::daily_filename_format_calculator::calc_filename(SPDLOG_FILENAME_T("example-%Y-%m-%d.log"), tm); - // calc excpected filename - file_t_sstream stream; - stream << "example-" << tm.tm_year + 1900 << "-" << std::setw(2) << std::setfill(SPDLOG_FILENAME_T('0')) << tm.tm_mon + 1 - << "-" << std::setw(2) << std::setfill(SPDLOG_FILENAME_T('0')) << tm.tm_mday << ".log"; - - - REQUIRE(filename == std::filesystem::path(stream.str())); + REQUIRE(filename == spdlog::fmt_lib::format(SPDLOG_FILENAME_T("example-{:04d}-{:02d}-{:02d}.log"), tm.tm_year + 1900, + tm.tm_mon + 1, tm.tm_mday)); } /* Test removal of old files */ @@ -140,4 +148,4 @@ TEST_CASE("daily_logger rotate", "[daily_file_sink]") { test_rotate(days_to_run, 10, 10); test_rotate(days_to_run, 11, 10); test_rotate(days_to_run, 20, 10); -} +} \ No newline at end of file diff --git a/tests/utils.cpp b/tests/utils.cpp index 6ca82df4..cd594644 100644 --- a/tests/utils.cpp +++ b/tests/utils.cpp @@ -27,7 +27,7 @@ std::string file_contents(const std::string &filename) { return std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); } -std::size_t count_lines(const std::string &filename) { +std::size_t count_lines(const spdlog::filename_t &filename) { std::ifstream ifs(filename); if (!ifs) { throw std::runtime_error("Failed open file "); @@ -39,7 +39,7 @@ std::size_t count_lines(const std::string &filename) { return counter; } -void require_message_count(const std::string &filename, const std::size_t messages) { +void require_message_count(const std::filesystem::path &filename, const std::size_t messages) { if (strlen(spdlog::details::os::default_eol) == 0) { REQUIRE(count_lines(filename) == 1); } else { diff --git a/tests/utils.h b/tests/utils.h index 53c09b46..f935fb83 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -2,6 +2,7 @@ #include #include +#include std::size_t count_files(const std::string &folder); @@ -9,9 +10,10 @@ void prepare_logdir(); std::string file_contents(const std::string &filename); -std::size_t count_lines(const std::string &filename); +//std::size_t count_lines(const std::string &filename); +std::size_t count_lines(const std::filesystem::path &filename); -void require_message_count(const std::string &filename, const std::size_t messages); +void require_message_count(const std::filesystem::path &filename, const std::size_t messages); std::size_t get_filesize(const std::string &filename);