Merge remote-tracking branch 'origin/attributes' into log_attr-fix

pull/2673/head
Felix Heitmann 3 years ago
commit 047d68d126

@ -9,14 +9,13 @@ jobs:
run:
shell: bash
strategy:
fail-fast: false
matrix:
config:
- { compiler: gcc, version: 4.9, build_type: Release, cppstd: 11, examples: OFF, asan: OFF }
- { compiler: gcc, version: 7, build_type: Release, cppstd: 11 }
- { compiler: gcc, version: 9, build_type: Release, cppstd: 17 }
- { compiler: gcc, version: 11, build_type: Debug, cppstd: 20 }
- { compiler: gcc, version: 12, build_type: Release, cppstd: 20 }
- { compiler: clang, version: 3.5, build_type: Release, cppstd: 11, asan: OFF }
- { compiler: clang, version: 10, build_type: Release, cppstd: 11 }
- { compiler: clang, version: 10, build_type: Debug, cppstd: 17, asan: OFF }
- { compiler: clang, version: 12, build_type: Debug, cppstd: 17, asan: OFF }
@ -28,7 +27,7 @@ jobs:
- uses: actions/checkout@main
- name: Setup
run: |
apt-get update && apt-get install -y curl
apt-get update && apt-get install -y curl git pkg-config libsystemd-dev
CMAKE_VERSION="3.24.2"
curl -sSL https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.sh -o install-cmake.sh
chmod +x install-cmake.sh

@ -2,42 +2,6 @@ version: 1.0.{build}
image: Visual Studio 2017
environment:
matrix:
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Debug
BUILD_SHARED: 'OFF'
FATAL_ERRORS: 'ON'
WCHAR: 'OFF'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
USE_STD_FORMAT: 'OFF'
CXX_STANDARD: 11
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Release
BUILD_SHARED: 'OFF'
FATAL_ERRORS: 'ON'
WCHAR: 'ON'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
USE_STD_FORMAT: 'OFF'
CXX_STANDARD: 11
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Debug
BUILD_SHARED: 'OFF'
FATAL_ERRORS: 'ON'
WCHAR: 'ON'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
USE_STD_FORMAT: 'OFF'
CXX_STANDARD: 11
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Release
BUILD_SHARED: 'OFF'
FATAL_ERRORS: 'ON'
WCHAR: 'ON'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
USE_STD_FORMAT: 'OFF'
CXX_STANDARD: 11
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Debug
BUILD_SHARED: 'OFF'
@ -119,7 +83,7 @@ build_script:
cmake --build . --config %BUILD_TYPE%
before_test:
- set PATH=%PATH%;C:\projects\spdlog\build\%BUILD_TYPE%
- set PATH=%PATH%;C:\projects\spdlog\build\_deps\catch2-build\src\%BUILD_TYPE%;C:\projects\spdlog\build\%BUILD_TYPE%
test_script:
- C:\projects\spdlog\build\tests\%BUILD_TYPE%\spdlog-utests.exe

@ -11,44 +11,124 @@ namespace details {
// template<typename T>
// concept composable = std::same_as<T, bool> || std::integral<T> || std::floating_point<T> || std::convertible_to<T, std::string_view>;
struct attr
struct Key
{
std::string key;
std::string value;
std::string _key;
public:
attr(std::initializer_list<string_view_t> l)
Key(string_view_t k)
{
if (l.size() != 2)
return; // throw exception if not kv pair?
scramble(key, *l.begin());
scramble(value, *(l.begin() + 1));
scramble(_key, k);
}
attr(string_view_t k, bool v)
: value{v ? "true" : "false"}
Key(std::string &&k)
{
scramble(_key, k);
}
Key(std::string const &k)
{
scramble(_key, k);
}
Key(const char *k)
{
key = std::string{k.data(), k.size()};
scramble(_key, k);
}
};
attr(string_view_t k, string_view_t v)
struct Value
{
std::string _value;
// string types
Value(string_view_t v)
{
key = std::string{k.data(), k.size()};
value = std::string{v.data(), v.size()};
scramble(_value, v);
}
Value(std::string &&v)
{
scramble(_value, v);
}
Value(std::string const &v)
{
scramble(_value, v);
}
Value(const char *v)
{
scramble(_value, v);
}
attr(std::string k, std::string v)
: key{k}
, value{v}
{}
// integer types
// probably better to do this with templates, but constraints are needed
// concepts would be nice here, but spdlog is c++11
// SFINAE is also an option, but it's a bit more complicated
// https://stackoverflow.com/questions/41552514/is-overloading-on-all-of-the-fundamental-integer-types-is-sufficient-to-capture
// basing the types off of MSVC, GCC, and Clang (https://en.cppreference.com/w/cpp/language/types)
template<typename T, std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>, bool> = true>
attr(string_view_t k, T v)
: value{std::to_string(v)}
// chars are already strings (single character)
Value(signed char v)
{
_value = v;
}
Value(unsigned char v)
{
_value = v;
}
// these are overloads, which match the overloads in to_string for msvc, gcc, and clang
Value(int v)
{
_value = std::to_string(v);
}
Value(unsigned int v)
{
key = std::string{k.data(), k.size()};
_value = std::to_string(v);
}
Value(long v)
{
_value = std::to_string(v);
}
Value(unsigned long v)
{
_value = std::to_string(v);
}
Value(long long v)
{
_value = std::to_string(v);
}
Value(unsigned long long v)
{
_value = std::to_string(v);
}
Value(float v)
{
_value = std::to_string(v);
}
Value(double v)
{
_value = std::to_string(v);
}
Value(long double v)
{
_value = std::to_string(v);
}
Value(bool v)
{
_value = v ? "true" : "false";
}
};
struct attr
{
std::string key;
std::string value;
public:
attr(Key &&k, Value &&v)
: key(std::move(k._key))
, value(std::move(v._value))
{}
attr(Key const &k, Value const &v)
: key(k._key)
, value(v._value)
{}
};
} // namespace details

@ -34,6 +34,7 @@
# if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
# include <limits>
# include <cassert>
# endif
# include <direct.h> // for _mkdir/_wmkdir
@ -501,20 +502,16 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target)
return;
}
int result_size = static_cast<int>(target.capacity());
if (str_size + 1 > result_size)
{
result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
}
// find the size to allocate for the result buffer
int result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
if (result_size > 0)
{
target.resize(result_size);
result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size);
if (result_size > 0)
if (result_size > 0)
{
target.resize(result_size);
assert(result_size == target.size());
return;
}
}

@ -287,6 +287,14 @@ SPDLOG_INLINE registry &registry::instance()
return s_instance;
}
SPDLOG_INLINE void registry::apply_logger_env_levels(std::shared_ptr<logger> new_logger)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
auto it = log_levels_.find(new_logger->name());
auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
new_logger->set_level(new_level);
}
SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
{
if (loggers_.find(logger_name) != loggers_.end())

@ -91,6 +91,8 @@ public:
static registry &instance();
void apply_logger_env_levels(std::shared_ptr<logger> new_logger);
private:
registry();
~registry();

@ -0,0 +1,133 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
//
// Custom sink for kafka
// Building and using requires librdkafka library.
// For building librdkafka library check the url below
// https://github.com/confluentinc/librdkafka
//
#include <spdlog/common.h>
#include "spdlog/details/log_msg.h"
#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/synchronous_factory.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/async.h"
#include <mutex>
// kafka header
#include <librdkafka/rdkafkacpp.h>
namespace spdlog {
namespace sinks {
struct kafka_sink_config
{
std::string server_addr;
std::string produce_topic;
int32_t flush_timeout_ms = 1000;
kafka_sink_config(std::string addr, std::string topic, int flush_timeout_ms = 1000)
: server_addr{std::move(addr)}
,produce_topic{std::move(topic)}
,flush_timeout_ms(flush_timeout_ms)
{}
};
template<typename Mutex>
class kafka_sink : public base_sink<Mutex>
{
public:
kafka_sink(kafka_sink_config config)
: config_{std::move(config)}
{
try
{
std::string errstr;
conf_.reset(RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL));
RdKafka::Conf::ConfResult confRes = conf_->set("bootstrap.servers", config_.server_addr, errstr);
if (confRes != RdKafka::Conf::CONF_OK)
{
throw_spdlog_ex(fmt_lib::format("conf set bootstrap.servers failed err:{}", errstr));
}
tconf_.reset(RdKafka::Conf::create(RdKafka::Conf::CONF_TOPIC));
if (tconf_ == nullptr)
{
throw_spdlog_ex(fmt_lib::format("create topic config failed"));
}
producer_.reset(RdKafka::Producer::create(conf_.get(), errstr));
if (producer_ == nullptr)
{
throw_spdlog_ex(fmt_lib::format("create producer failed err:{}", errstr));
}
topic_.reset(RdKafka::Topic::create(producer_.get(), config_.produce_topic, tconf_.get(), errstr));
if (topic_ == nullptr)
{
throw_spdlog_ex(fmt_lib::format("create topic failed err:{}", errstr));
}
}
catch (const std::exception &e)
{
throw_spdlog_ex(fmt_lib::format("error create kafka instance: {}", e.what()));
}
}
~kafka_sink()
{
producer_->flush(config_.flush_timeout_ms);
}
protected:
void sink_it_(const details::log_msg &msg) override
{
producer_->produce(topic_.get(), 0, RdKafka::Producer::RK_MSG_COPY, (void *)msg.payload.data(), msg.payload.size(), NULL, NULL);
}
void flush_() override
{
producer_->flush(config_.flush_timeout_ms);
}
private:
kafka_sink_config config_;
std::unique_ptr<RdKafka::Producer> producer_ = nullptr;
std::unique_ptr<RdKafka::Conf> conf_ = nullptr;
std::unique_ptr<RdKafka::Conf> tconf_ = nullptr;
std::unique_ptr<RdKafka::Topic> topic_ = nullptr;
};
using kafka_sink_mt = kafka_sink<std::mutex>;
using kafka_sink_st = kafka_sink<spdlog::details::null_mutex>;
} // namespace sinks
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> kafka_logger_mt(const std::string &logger_name, spdlog::sinks::kafka_sink_config config)
{
return Factory::template create<sinks::kafka_sink_mt>(logger_name, config);
}
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> kafka_logger_st(const std::string &logger_name, spdlog::sinks::kafka_sink_config config)
{
return Factory::template create<sinks::kafka_sink_st>(logger_name, config);
}
template<typename Factory = spdlog::async_factory>
inline std::shared_ptr<spdlog::logger> kafka_logger_async_mt(std::string logger_name, spdlog::sinks::kafka_sink_config config)
{
return Factory::template create<sinks::kafka_sink_mt>(logger_name, config);
}
template<typename Factory = spdlog::async_factory>
inline std::shared_ptr<spdlog::logger> kafka_logger_async_st(std::string logger_name, spdlog::sinks::kafka_sink_config config)
{
return Factory::template create<sinks::kafka_sink_st>(logger_name, config);
}
} // namespace spdlog

@ -7,13 +7,20 @@
#if defined(_WIN32)
# include <spdlog/details/null_mutex.h>
# if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
# include <spdlog/details/os.h>
# endif
# include <spdlog/sinks/base_sink.h>
# include <mutex>
# include <string>
// Avoid including windows.h (https://stackoverflow.com/a/30741042)
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringW(const wchar_t *lpOutputString);
#else
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString);
#endif
extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
namespace spdlog {
@ -38,8 +45,14 @@ protected:
}
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
formatted.push_back('\0'); // add a null terminator for OutputDebugStringA
formatted.push_back('\0'); // add a null terminator for OutputDebugString
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
wmemory_buf_t wformatted;
details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), wformatted);
OutputDebugStringW(wformatted.data());
#else
OutputDebugStringA(formatted.data());
#endif
}
void flush_() override {}

@ -4,6 +4,7 @@
#pragma once
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/os.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/details/synchronous_factory.h>
@ -75,11 +76,17 @@ protected:
{
// Note: function call inside '()' to avoid macro expansion
err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level),
#ifndef SPDLOG_NO_THREAD_ID
"TID=%zu", details::os::thread_id(),
#endif
"SYSLOG_IDENTIFIER=%.*s", static_cast<int>(syslog_identifier.size()), syslog_identifier.data(), nullptr);
}
else
{
err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level),
#ifndef SPDLOG_NO_THREAD_ID
"TID=%zu", details::os::thread_id(),
#endif
"SYSLOG_IDENTIFIER=%.*s", static_cast<int>(syslog_identifier.size()), syslog_identifier.data(), "CODE_FILE=%s",
msg.source.filename, "CODE_LINE=%d", msg.source.line, "CODE_FUNC=%s", msg.source.funcname, nullptr);
}

@ -117,4 +117,9 @@ SPDLOG_INLINE void set_default_logger(std::shared_ptr<spdlog::logger> default_lo
details::registry::instance().set_default_logger(std::move(default_logger));
}
SPDLOG_INLINE void apply_logger_env_levels(std::shared_ptr<logger> logger)
{
details::registry::instance().apply_logger_env_levels(std::move(logger));
}
} // namespace spdlog

@ -131,6 +131,15 @@ SPDLOG_API spdlog::logger *default_logger_raw();
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
// Initialize logger level based on environment configs.
//
// Useful for applying SPDLOG_LEVEL to manually created loggers.
//
// Example:
// auto mylogger = std::make_shared<spdlog::logger>("mylogger", ...);
// spdlog::apply_logger_env_levels(mylogger);
SPDLOG_API void apply_logger_env_levels(std::shared_ptr<logger> logger);
inline void push_context(attribute_list attrs) {
default_logger_raw()->push_context(std::move(attrs));
}

@ -13,6 +13,20 @@ if(PkgConfig_FOUND)
pkg_check_modules(systemd libsystemd)
endif()
find_package(Catch2 3 QUIET)
if (Catch2_FOUND)
message(STATUS "Packaged version of Catch will be used.")
else()
message(STATUS "Bundled version of Catch will be downloaded and used.")
include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.3.2
)
FetchContent_MakeAvailable(Catch2)
endif()
set(SPDLOG_UTESTS_SOURCES
test_file_helper.cpp
test_file_logging.cpp
@ -54,6 +68,7 @@ function(spdlog_prepare_test test_target spdlog_lib)
if(systemd_FOUND)
target_link_libraries(${test_target} PRIVATE ${systemd_LIBRARIES})
endif()
target_link_libraries(${test_target} PRIVATE Catch2::Catch2WithMain)
if(SPDLOG_SANITIZE_ADDRESS)
spdlog_enable_sanitizer(${test_target})
endif()

File diff suppressed because it is too large Load Diff

@ -1,23 +0,0 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

@ -4,7 +4,7 @@
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // Workaround for GCC 12
#endif
#include "catch.hpp"
#include <catch2/catch_all.hpp>
#if defined(__GNUC__) && __GNUC__ == 12
# pragma GCC diagnostic pop
#endif

@ -3,8 +3,7 @@
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // Workaround for GCC 12
#endif
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include <catch2/catch_all.hpp>
#if defined(__GNUC__) && __GNUC__ == 12
# pragma GCC diagnostic pop

@ -7,7 +7,7 @@
#include "spdlog/async.h"
#include "spdlog/common.h"
TEST_CASE("custom_callback_logger", "[custom_callback_logger]]")
TEST_CASE("custom_callback_logger", "[custom_callback_logger]")
{
std::vector<std::string> lines;
spdlog::pattern_formatter formatter;

@ -87,19 +87,19 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]")
* File name calculations
*/
TEST_CASE("rotating_file_sink::calc_filename1", "[rotating_file_sink]]")
TEST_CASE("rotating_file_sink::calc_filename1", "[rotating_file_sink]")
{
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated.txt"), 3);
REQUIRE(filename == SPDLOG_FILENAME_T("rotated.3.txt"));
}
TEST_CASE("rotating_file_sink::calc_filename2", "[rotating_file_sink]]")
TEST_CASE("rotating_file_sink::calc_filename2", "[rotating_file_sink]")
{
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated"), 3);
REQUIRE(filename == SPDLOG_FILENAME_T("rotated.3"));
}
TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]")
{
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated.txt"), 0);
REQUIRE(filename == SPDLOG_FILENAME_T("rotated.txt"));
@ -110,7 +110,7 @@ TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
# include <regex>
TEST_CASE("daily_file_sink::daily_filename_calculator", "[daily_file_sink]]")
TEST_CASE("daily_file_sink::daily_filename_calculator", "[daily_file_sink]")
{
// daily_YYYY-MM-DD_hh-mm.txt
auto filename =
@ -123,7 +123,7 @@ TEST_CASE("daily_file_sink::daily_filename_calculator", "[daily_file_sink]]")
}
#endif
TEST_CASE("daily_file_sink::daily_filename_format_calculator", "[daily_file_sink]]")
TEST_CASE("daily_file_sink::daily_filename_format_calculator", "[daily_file_sink]")
{
std::tm tm = spdlog::details::os::localtime();
// example-YYYY-MM-DD.log

@ -1,6 +1,6 @@
/*
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
*/
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
*/
#include "includes.h"
#include <iostream>
@ -11,111 +11,114 @@
class failing_sink : public spdlog::sinks::base_sink<std::mutex>
{
protected:
void sink_it_(const spdlog::details::log_msg &) final
{
throw std::runtime_error("some error happened during log");
}
void sink_it_(const spdlog::details::log_msg &) final
{
throw std::runtime_error("some error happened during log");
}
void flush_() final
{
throw std::runtime_error("some error happened during flush");
}
void flush_() final
{
throw std::runtime_error("some error happened during flush");
}
};
struct custom_ex {};
TEST_CASE("default_error_handler", "[errors]]")
#if !defined(SPDLOG_USE_STD_FORMAT) // std formt doesn't fully support tuntime strings
TEST_CASE("default_error_handler", "[errors]")
{
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("test-error", filename, true);
logger->set_pattern("%v");
logger->info(SPDLOG_FMT_RUNTIME("Test message {} {}"), 1);
logger->info("Test message {}", 2);
logger->flush();
using spdlog::details::os::default_eol;
REQUIRE(file_contents(SIMPLE_LOG) == spdlog::fmt_lib::format("Test message 2{}", default_eol));
REQUIRE(count_lines(SIMPLE_LOG) == 1);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("test-error", filename, true);
logger->set_pattern("%v");
logger->info(SPDLOG_FMT_RUNTIME("Test message {} {}"), 1);
logger->info("Test message {}", 2);
logger->flush();
using spdlog::details::os::default_eol;
REQUIRE(file_contents(SIMPLE_LOG) == spdlog::fmt_lib::format("Test message 2{}", default_eol));
REQUIRE(count_lines(SIMPLE_LOG) == 1);
}
struct custom_ex
{};
TEST_CASE("custom_error_handler", "[errors]]")
TEST_CASE("custom_error_handler", "[errors]")
{
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
logger->flush_on(spdlog::level::info);
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
logger->info("Good message #1");
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
logger->flush_on(spdlog::level::info);
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
logger->info("Good message #1");
REQUIRE_THROWS_AS(logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"), custom_ex);
logger->info("Good message #2");
require_message_count(SIMPLE_LOG, 2);
REQUIRE_THROWS_AS(logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"), custom_ex);
logger->info("Good message #2");
require_message_count(SIMPLE_LOG, 2);
}
#endif
TEST_CASE("default_error_handler2", "[errors]]")
TEST_CASE("default_error_handler2", "[errors]")
{
spdlog::drop_all();
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
spdlog::drop_all();
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
}
TEST_CASE("flush_error_handler", "[errors]]")
TEST_CASE("flush_error_handler", "[errors]")
{
spdlog::drop_all();
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
REQUIRE_THROWS_AS(logger->flush(), custom_ex);
spdlog::drop_all();
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
REQUIRE_THROWS_AS(logger->flush(), custom_ex);
}
TEST_CASE("async_error_handler", "[errors]]")
#if !defined(SPDLOG_USE_STD_FORMAT)
TEST_CASE("async_error_handler", "[errors]")
{
prepare_logdir();
std::string err_msg("log failed with some msg");
prepare_logdir();
std::string err_msg("log failed with some msg");
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_ASYNC_LOG);
{
spdlog::init_thread_pool(128, 1);
auto logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
logger->set_error_handler([=](const std::string &) {
std::ofstream ofs("test_logs/custom_err.txt");
if (!ofs)
{
throw std::runtime_error("Failed open test_logs/custom_err.txt");
}
ofs << err_msg;
});
logger->info("Good message #1");
logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx");
logger->info("Good message #2");
spdlog::drop("logger"); // force logger to drain the queue and shutdown
}
spdlog::init_thread_pool(128, 1);
require_message_count(SIMPLE_ASYNC_LOG, 2);
REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg);
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_ASYNC_LOG);
{
spdlog::init_thread_pool(128, 1);
auto logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
logger->set_error_handler([=](const std::string &) {
std::ofstream ofs("test_logs/custom_err.txt");
if (!ofs)
{
throw std::runtime_error("Failed open test_logs/custom_err.txt");
}
ofs << err_msg;
});
logger->info("Good message #1");
logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx");
logger->info("Good message #2");
spdlog::drop("logger"); // force logger to drain the queue and shutdown
}
spdlog::init_thread_pool(128, 1);
require_message_count(SIMPLE_ASYNC_LOG, 2);
REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg);
}
#endif
// Make sure async error handler is executed
TEST_CASE("async_error_handler2", "[errors]]")
TEST_CASE("async_error_handler2", "[errors]")
{
prepare_logdir();
std::string err_msg("This is async handler error message");
{
spdlog::details::os::create_dir(SPDLOG_FILENAME_T("test_logs"));
spdlog::init_thread_pool(128, 1);
auto logger = spdlog::create_async<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string &) {
std::ofstream ofs("test_logs/custom_err2.txt");
if (!ofs)
throw std::runtime_error("Failed open test_logs/custom_err2.txt");
ofs << err_msg;
});
logger->info("Hello failure");
spdlog::drop("failed_logger"); // force logger to drain the queue and shutdown
}
prepare_logdir();
std::string err_msg("This is async handler error message");
{
spdlog::details::os::create_dir(SPDLOG_FILENAME_T("test_logs"));
spdlog::init_thread_pool(128, 1);
auto logger = spdlog::create_async<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string &) {
std::ofstream ofs("test_logs/custom_err2.txt");
if (!ofs)
throw std::runtime_error("Failed open test_logs/custom_err2.txt");
ofs << err_msg;
});
logger->info("Hello failure");
spdlog::drop("failed_logger"); // force logger to drain the queue and shutdown
}
spdlog::init_thread_pool(128, 1);
REQUIRE(file_contents("test_logs/custom_err2.txt") == err_msg);
spdlog::init_thread_pool(128, 1);
REQUIRE(file_contents("test_logs/custom_err2.txt") == err_msg);
}

@ -15,7 +15,7 @@ static void write_with_helper(file_helper &helper, size_t howmany)
helper.flush();
}
TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
TEST_CASE("file_helper_filename", "[file_helper::filename()]")
{
prepare_logdir();
@ -25,7 +25,7 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
REQUIRE(helper.filename() == target_filename);
}
TEST_CASE("file_helper_size", "[file_helper::size()]]")
TEST_CASE("file_helper_size", "[file_helper::size()]")
{
prepare_logdir();
spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
@ -39,7 +39,7 @@ TEST_CASE("file_helper_size", "[file_helper::size()]]")
REQUIRE(get_filesize(TEST_FILENAME) == expected_size);
}
TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
TEST_CASE("file_helper_reopen", "[file_helper::reopen()]")
{
prepare_logdir();
spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
@ -51,7 +51,7 @@ TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
REQUIRE(helper.size() == 0);
}
TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]")
{
prepare_logdir();
spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
@ -78,7 +78,7 @@ static void test_split_ext(const spdlog::filename_t::value_type *fname, const sp
REQUIRE(ext == expected_ext);
}
TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()]]")
TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()]")
{
test_split_ext(SPDLOG_FILENAME_T("mylog.txt"), SPDLOG_FILENAME_T("mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T(".mylog.txt"), SPDLOG_FILENAME_T(".mylog"), SPDLOG_FILENAME_T(".txt"));

@ -6,7 +6,7 @@
#define SIMPLE_LOG "test_logs/simple_log"
#define ROTATING_LOG "test_logs/rotating_log"
TEST_CASE("simple_file_logger", "[simple_logger]]")
TEST_CASE("simple_file_logger", "[simple_logger]")
{
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
@ -23,7 +23,7 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
REQUIRE(file_contents(SIMPLE_LOG) == spdlog::fmt_lib::format("Test message 1{}Test message 2{}", default_eol, default_eol));
}
TEST_CASE("flush_on", "[flush_on]]")
TEST_CASE("flush_on", "[flush_on]")
{
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
@ -44,7 +44,7 @@ TEST_CASE("flush_on", "[flush_on]]")
spdlog::fmt_lib::format("Should not be flushed{}Test message 1{}Test message 2{}", default_eol, default_eol, default_eol));
}
TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
TEST_CASE("rotating_file_logger1", "[rotating_logger]")
{
prepare_logdir();
size_t max_size = 1024 * 10;
@ -60,7 +60,7 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
require_message_count(ROTATING_LOG, 10);
}
TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
TEST_CASE("rotating_file_logger2", "[rotating_logger]")
{
prepare_logdir();
size_t max_size = 1024 * 10;
@ -100,7 +100,7 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
}
// test that passing max_size=0 throws
TEST_CASE("rotating_file_logger3", "[rotating_logger]]")
TEST_CASE("rotating_file_logger3", "[rotating_logger]")
{
prepare_logdir();
size_t max_size = 0;

@ -10,7 +10,7 @@
#define TEST_FILENAME "test_logs/simple_log"
TEST_CASE("debug and trace w/o format string", "[macros]]")
TEST_CASE("debug and trace w/o format string", "[macros]")
{
prepare_logdir();

@ -6,7 +6,7 @@ TEST_CASE("stopwatch1", "[stopwatch]")
{
using std::chrono::milliseconds;
using clock = std::chrono::steady_clock;
milliseconds wait_ms(250);
milliseconds wait_ms(200);
milliseconds tolerance_ms(250);
auto start = clock::now();
spdlog::stopwatch sw;
@ -24,7 +24,7 @@ TEST_CASE("stopwatch2", "[stopwatch]")
using std::chrono::milliseconds;
using clock = std::chrono::steady_clock;
clock::duration wait_duration(milliseconds(250));
clock::duration wait_duration(milliseconds(200));
clock::duration tolerance_duration(milliseconds(250));
auto test_sink = std::make_shared<test_sink_st>();

Loading…
Cancel
Save