mirror of
https://github.com/boostorg/redis.git
synced 2026-01-19 04:42:09 +00:00
Updates the Logger interface to allow extensibility and type erasure (#273)
Removes all the logger::on_xxx functions Removes the Logger template parameter to async_run Adds a logger constructor that allows passing a std::function to customize logging behavior Adds constructors to connection and basic_connection taking a logger Deprecates config::logger_prefix Deprecates the async_run overload taking a logger parameter Deprecates the basic_connection::async_run overload not taking any config object Deprecates the basic_connection::next_layer_type typedef Makes the default log level logger::info Makes the logging thread-safe Cleans up deprecated functionality from examples Adds docs on logging Adds an example on how to integrate spdlog into Boost.Redis logging close #213
This commit is contained in:
committed by
GitHub
parent
7304d99bf6
commit
f04d97ffa5
@@ -5,11 +5,10 @@ target_link_libraries(boost_redis_project_options INTERFACE boost_redis)
|
||||
if (MSVC)
|
||||
# C4459: name hides outer scope variable is issued by Asio
|
||||
target_compile_options(boost_redis_project_options INTERFACE /bigobj /W4 /WX /wd4459)
|
||||
target_compile_definitions(boost_redis_project_options INTERFACE _WIN32_WINNT=0x0601)
|
||||
target_compile_definitions(boost_redis_project_options INTERFACE _WIN32_WINNT=0x0601 _CRT_SECURE_NO_WARNINGS=1)
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_compile_options(boost_redis_project_options INTERFACE -Wall -Wextra -Werror)
|
||||
endif()
|
||||
target_compile_definitions(boost_redis_project_options INTERFACE BOOST_ALLOW_DEPRECATED=1) # we need to still test deprecated fns
|
||||
|
||||
add_library(boost_redis_src STATIC boost_redis.cpp)
|
||||
target_compile_features(boost_redis_src PRIVATE cxx_std_17)
|
||||
@@ -29,6 +28,7 @@ macro(make_test TEST_NAME)
|
||||
boost_redis_project_options
|
||||
Boost::unit_test_framework
|
||||
)
|
||||
target_compile_definitions(${EXE_NAME} PRIVATE BOOST_ALLOW_DEPRECATED=1) # we need to still test deprecated fns
|
||||
add_test(${EXE_NAME} ${EXE_NAME})
|
||||
endmacro()
|
||||
|
||||
@@ -38,6 +38,8 @@ make_test(test_request)
|
||||
make_test(test_low_level_sync_sans_io)
|
||||
make_test(test_any_adapter)
|
||||
make_test(test_exec_fsm)
|
||||
make_test(test_log_to_file)
|
||||
make_test(test_conn_logging)
|
||||
|
||||
# Tests that require a real Redis server
|
||||
make_test(test_conn_quit)
|
||||
|
||||
@@ -14,7 +14,8 @@ local requirements =
|
||||
<define>BOOST_ASIO_DISABLE_BOOST_BIND=1
|
||||
<define>BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1
|
||||
<define>BOOST_ASIO_DISABLE_BOOST_REGEX=1
|
||||
<define>BOOST_ALLOW_DEPRECATED=1 # we need to test deprecated fns
|
||||
<define>BOOST_ALLOW_DEPRECATED=1 # we need to test deprecated fns
|
||||
<define>_CRT_SECURE_NO_WARNINGS=1 # suppress MSVC warnings
|
||||
<toolset>msvc:<cxxflags>"/bigobj"
|
||||
<target-os>windows:<define>_WIN32_WINNT=0x0601
|
||||
[ requires
|
||||
@@ -53,6 +54,8 @@ local tests =
|
||||
test_low_level_sync_sans_io
|
||||
test_any_adapter
|
||||
test_exec_fsm
|
||||
test_log_to_file
|
||||
test_conn_logging
|
||||
;
|
||||
|
||||
# Build and run the tests
|
||||
|
||||
@@ -25,10 +25,9 @@ void run(
|
||||
std::shared_ptr<boost::redis::connection> conn,
|
||||
boost::redis::config cfg,
|
||||
boost::system::error_code ec,
|
||||
boost::redis::operation op,
|
||||
boost::redis::logger::level l)
|
||||
boost::redis::operation op)
|
||||
{
|
||||
conn->async_run(cfg, {l}, run_callback{conn, op, ec});
|
||||
conn->async_run(cfg, run_callback{conn, op, ec});
|
||||
}
|
||||
|
||||
static std::string safe_getenv(const char* name, const char* default_value)
|
||||
|
||||
@@ -33,5 +33,4 @@ void run(
|
||||
std::shared_ptr<boost::redis::connection> conn,
|
||||
boost::redis::config cfg = make_test_config(),
|
||||
boost::system::error_code ec = boost::asio::error::operation_aborted,
|
||||
boost::redis::operation op = boost::redis::operation::receive,
|
||||
boost::redis::logger::level l = boost::redis::logger::level::debug);
|
||||
boost::redis::operation op = boost::redis::operation::receive);
|
||||
|
||||
159
test/test_conn_logging.cpp
Normal file
159
test/test_conn_logging.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
//
|
||||
// Copyright (c) 2025 Marcelo Zimbres Silva (mzimbres@gmail.com),
|
||||
// Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <boost/redis/config.hpp>
|
||||
#include <boost/redis/connection.hpp>
|
||||
#include <boost/redis/logger.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using boost::system::error_code;
|
||||
namespace net = boost::asio;
|
||||
using namespace boost::redis;
|
||||
|
||||
namespace {
|
||||
|
||||
// user tests
|
||||
// logging can be disabled
|
||||
// logging can be changed verbosity
|
||||
|
||||
template <class Conn>
|
||||
void run_with_invalid_config(net::io_context& ioc, Conn& conn)
|
||||
{
|
||||
config cfg;
|
||||
cfg.use_ssl = true;
|
||||
cfg.unix_socket = "/tmp/sock";
|
||||
conn.async_run(cfg, [](error_code ec) {
|
||||
BOOST_TEST_NE(ec, error_code());
|
||||
});
|
||||
ioc.run_for(test_timeout);
|
||||
}
|
||||
|
||||
template <class Conn>
|
||||
void test_connection_constructor_executor_1()
|
||||
{
|
||||
// Setup
|
||||
net::io_context ioc;
|
||||
std::vector<std::string> messages;
|
||||
logger lgr(logger::level::info, [&](logger::level, std::string_view msg) {
|
||||
messages.emplace_back(msg);
|
||||
});
|
||||
Conn conn{ioc.get_executor(), std::move(lgr)};
|
||||
|
||||
// Produce some logging
|
||||
run_with_invalid_config(ioc, conn);
|
||||
|
||||
// Some logging was produced
|
||||
BOOST_TEST_EQ(messages.size(), 1u);
|
||||
}
|
||||
|
||||
template <class Conn>
|
||||
void test_connection_constructor_context_1()
|
||||
{
|
||||
// Setup
|
||||
net::io_context ioc;
|
||||
std::vector<std::string> messages;
|
||||
logger lgr(logger::level::info, [&](logger::level, std::string_view msg) {
|
||||
messages.emplace_back(msg);
|
||||
});
|
||||
Conn conn{ioc, std::move(lgr)};
|
||||
|
||||
// Produce some logging
|
||||
run_with_invalid_config(ioc, conn);
|
||||
|
||||
// Some logging was produced
|
||||
BOOST_TEST_EQ(messages.size(), 1u);
|
||||
}
|
||||
|
||||
template <class Conn>
|
||||
void test_connection_constructor_executor_2()
|
||||
{
|
||||
// Setup
|
||||
net::io_context ioc;
|
||||
std::vector<std::string> messages;
|
||||
logger lgr(logger::level::info, [&](logger::level, std::string_view msg) {
|
||||
messages.emplace_back(msg);
|
||||
});
|
||||
Conn conn{
|
||||
ioc.get_executor(),
|
||||
net::ssl::context{net::ssl::context::tlsv12_client},
|
||||
std::move(lgr)};
|
||||
|
||||
// Produce some logging
|
||||
run_with_invalid_config(ioc, conn);
|
||||
|
||||
// Some logging was produced
|
||||
BOOST_TEST_EQ(messages.size(), 1u);
|
||||
}
|
||||
|
||||
template <class Conn>
|
||||
void test_connection_constructor_context_2()
|
||||
{
|
||||
// Setup
|
||||
net::io_context ioc;
|
||||
std::vector<std::string> messages;
|
||||
logger lgr(logger::level::info, [&](logger::level, std::string_view msg) {
|
||||
messages.emplace_back(msg);
|
||||
});
|
||||
Conn conn{ioc, net::ssl::context{net::ssl::context::tlsv12_client}, std::move(lgr)};
|
||||
|
||||
// Produce some logging
|
||||
run_with_invalid_config(ioc, conn);
|
||||
|
||||
// Some logging was produced
|
||||
BOOST_TEST_EQ(messages.size(), 1u);
|
||||
}
|
||||
|
||||
void test_disable_logging()
|
||||
{
|
||||
// Setup
|
||||
net::io_context ioc;
|
||||
std::vector<std::string> messages;
|
||||
logger lgr(logger::level::disabled, [&](logger::level, std::string_view msg) {
|
||||
messages.emplace_back(msg);
|
||||
});
|
||||
connection conn{ioc, std::move(lgr)};
|
||||
|
||||
// Produce some logging
|
||||
run_with_invalid_config(ioc, conn);
|
||||
|
||||
// Some logging was produced
|
||||
BOOST_TEST_EQ(messages.size(), 0u);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
// basic_connection
|
||||
using basic_conn_t = basic_connection<net::io_context::executor_type>;
|
||||
test_connection_constructor_executor_1<basic_conn_t>();
|
||||
test_connection_constructor_executor_2<basic_conn_t>();
|
||||
test_connection_constructor_context_1<basic_conn_t>();
|
||||
test_connection_constructor_context_2<basic_conn_t>();
|
||||
|
||||
// connection
|
||||
test_connection_constructor_executor_1<connection>();
|
||||
test_connection_constructor_executor_2<connection>();
|
||||
test_connection_constructor_context_1<connection>();
|
||||
test_connection_constructor_context_2<connection>();
|
||||
|
||||
test_disable_logging();
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
123
test/test_log_to_file.cpp
Normal file
123
test/test_log_to_file.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// Copyright (c) 2025 Marcelo Zimbres Silva (mzimbres@gmail.com),
|
||||
// Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <boost/redis/impl/log_to_file.hpp>
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
using namespace boost::redis;
|
||||
|
||||
namespace {
|
||||
|
||||
// RAII helpers for working with C FILE*
|
||||
struct file_deleter {
|
||||
void operator()(FILE* f) const { std::fclose(f); }
|
||||
};
|
||||
using unique_file = std::unique_ptr<FILE, file_deleter>;
|
||||
|
||||
unique_file create_temporary()
|
||||
{
|
||||
unique_file f{std::tmpfile()};
|
||||
if (!BOOST_TEST_NE(f.get(), nullptr))
|
||||
exit(1);
|
||||
return f;
|
||||
}
|
||||
|
||||
std::string get_file_contents(FILE* f)
|
||||
{
|
||||
if (!BOOST_TEST_EQ(std::fseek(f, 0, SEEK_END), 0))
|
||||
exit(1);
|
||||
long fsize = std::ftell(f);
|
||||
if (!BOOST_TEST_GE(fsize, 0))
|
||||
exit(1);
|
||||
std::rewind(f);
|
||||
std::string res(fsize, 0);
|
||||
if (!BOOST_TEST_EQ(std::fread(res.data(), 1u, res.size(), f), fsize))
|
||||
exit(1);
|
||||
return res;
|
||||
}
|
||||
|
||||
void test_regular()
|
||||
{
|
||||
auto f = create_temporary();
|
||||
detail::log_to_file(f.get(), "something happened");
|
||||
BOOST_TEST_EQ(get_file_contents(f.get()), "(Boost.Redis) something happened\n");
|
||||
}
|
||||
|
||||
void test_empty_message()
|
||||
{
|
||||
auto f = create_temporary();
|
||||
detail::log_to_file(f.get(), {});
|
||||
BOOST_TEST_EQ(get_file_contents(f.get()), "(Boost.Redis) \n");
|
||||
}
|
||||
|
||||
void test_empty_prefix()
|
||||
{
|
||||
auto f = create_temporary();
|
||||
detail::log_to_file(f.get(), {}, "");
|
||||
BOOST_TEST_EQ(get_file_contents(f.get()), "\n");
|
||||
}
|
||||
|
||||
void test_message_not_null_terminated()
|
||||
{
|
||||
constexpr std::string_view str = "some_string";
|
||||
auto f = create_temporary();
|
||||
detail::log_to_file(f.get(), str.substr(0, 4));
|
||||
BOOST_TEST_EQ(get_file_contents(f.get()), "(Boost.Redis) some\n");
|
||||
}
|
||||
|
||||
// NULL bytes don't cause UB. None of our messages have
|
||||
// them, so this is an edge case
|
||||
void test_message_null_bytes()
|
||||
{
|
||||
char buff[] = {'a', 'b', 'c', 0, 'l', 0};
|
||||
auto f = create_temporary();
|
||||
detail::log_to_file(f.get(), std::string_view(buff, sizeof(buff)));
|
||||
BOOST_TEST_EQ(get_file_contents(f.get()), "(Boost.Redis) abc\n");
|
||||
}
|
||||
|
||||
// Internally, sizes are converted to int because of C APIs. Check that this
|
||||
// does not cause trouble. We impose a sanity limit of 0xffff bytes for all messages
|
||||
void test_message_very_long()
|
||||
{
|
||||
// Setup. Allocating a string of size INT_MAX causes trouble, so we pass a string_view
|
||||
// with that size, but with only the first 0xffff bytes being valid
|
||||
std::string msg(0xffffu + 1u, 'a');
|
||||
const auto msg_size = static_cast<std::size_t>((std::numeric_limits<int>::max)()) + 1u;
|
||||
auto f = create_temporary();
|
||||
|
||||
// Log
|
||||
detail::log_to_file(f.get(), std::string_view(msg.data(), msg_size));
|
||||
|
||||
// Check
|
||||
std::string expected = "(Boost.Redis) ";
|
||||
expected += std::string_view(msg.data(), 0xffffu);
|
||||
expected += '\n';
|
||||
BOOST_TEST_EQ(get_file_contents(f.get()), expected);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
test_regular();
|
||||
test_empty_message();
|
||||
test_empty_prefix();
|
||||
test_message_not_null_terminated();
|
||||
test_message_null_bytes();
|
||||
test_message_very_long();
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
Reference in New Issue
Block a user