Added support for binary logging using to_hex(..)

This commit is contained in:
gabime 2018-09-25 01:03:57 +03:00
parent 2e7b3cae2a
commit 41d879e292
4 changed files with 253 additions and 4 deletions

View File

@ -32,7 +32,7 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int howmany = 1000000; int howmany = 100000;
int queue_size = howmany + 2; int queue_size = howmany + 2;
int threads = 10; int threads = 10;
size_t file_size = 30 * 1024 * 1024; size_t file_size = 30 * 1024 * 1024;
@ -54,12 +54,20 @@ int main(int argc, char *argv[])
cout << "******************************************************************" cout << "******************************************************************"
"*************\n"; "*************\n";
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
bench(howmany, basic_st); bench(howmany, spdlog::create<null_sink_st>("null_st"));
return 0;
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files); auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
bench(howmany, rotating_st); bench(howmany, rotating_st);
return 0;
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
bench(howmany, basic_st);
//auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
//bench(howmany, rotating_st);
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log"); auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
bench(howmany, daily_st); bench(howmany, daily_st);
@ -103,15 +111,17 @@ int main(int argc, char *argv[])
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
#include "spdlog/extra/to_hex.h"
void bench(int howmany, std::shared_ptr<spdlog::logger> log) void bench(int howmany, std::shared_ptr<spdlog::logger> log)
{ {
using std::chrono::high_resolution_clock; using std::chrono::high_resolution_clock;
cout << log->name() << "...\t\t" << flush; cout << log->name() << "...\t\t" << flush;
char buf[1024];
auto start = high_resolution_clock::now(); auto start = high_resolution_clock::now();
for (auto i = 0; i < howmany; ++i) for (auto i = 0; i < howmany; ++i)
{ {
log->info("Hello logger: msg number {}", i); log->info("Hello logger: msg number {}", spdlog::to_hex(buf));
} }
auto delta = high_resolution_clock::now() - start; auto delta = high_resolution_clock::now() - start;

View File

@ -14,6 +14,7 @@ void basic_example();
void rotating_example(); void rotating_example();
void daily_example(); void daily_example();
void async_example(); void async_example();
void binary_example();
void multi_sink_example(); void multi_sink_example();
void user_defined_example(); void user_defined_example();
void err_handler_example(); void err_handler_example();
@ -40,6 +41,9 @@ int main(int, char *[])
// async logging using a backing thread pool // async logging using a backing thread pool
async_example(); async_example();
// log binary data
binary_example();
// a logger can have multiple targets with different formats // a logger can have multiple targets with different formats
multi_sink_example(); multi_sink_example();
@ -97,6 +101,7 @@ void stdout_example()
// Customize msg format for all loggers // Customize msg format for all loggers
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v"); spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
console->info("This an info message with custom format"); console->info("This an info message with custom format");
spdlog::set_pattern("%+"); // back to default format
// Compile time log levels // Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
@ -148,6 +153,30 @@ void async_example()
} }
} }
// log binary data as hex.
// many types of std::container<char> types can be used.
// 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()
{
auto console = spdlog::get("console");
std::array<char, 80> 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));
// 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));
}
// create logger with 2 targets with different log levels and formats // create logger with 2 targets with different log levels and formats
// the console will show only warnings or errors, while the file will log all // the console will show only warnings or errors, while the file will log all

View File

@ -0,0 +1,167 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// Support for logging binary data as hex
// 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.
//
// Examples:
//
// std::vector<char> v(200, 0x0b);
// logger->info("Some buffer {}", spdlog::to_hex(v));
// char buf[128];
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
namespace spdlog {
namespace details {
template<typename It>
class bytes_range
{
public:
bytes_range(It range_begin, It range_end)
: begin_(range_begin), end_(range_end)
{}
It begin() const {return begin_;}
It end() const {return end_;}
private:
It begin_, end_;
};
} // namespace details
// create a bytes_range that wraps the given container
template <typename Container>
inline details::bytes_range<typename Container::const_iterator> to_hex(const Container &container)
{
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
using Iter = typename Container::const_iterator;
return details::bytes_range<Iter>(std::begin(container), std::end(container));
}
// create bytes_range from ranges
template<typename It>
inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
{
return details::bytes_range<It>(range_begin, range_end);
}
} // namespace spdlog
namespace fmt {
template<typename T>
struct formatter<spdlog::details::bytes_range<T>>
{
const std::size_t line_size = 100;
const char delimiter = ' ';
bool put_newlines = true;
bool put_delimiters = true;
bool use_uppercase = false;
bool put_positions = true; // position on start of each line
// parse the format string flags
template<typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
auto it = ctx.begin();
while (*it && *it != '}')
{
switch (*it)
{
case 'X':
use_uppercase = true;
break;
case 's':
put_delimiters = false;
break;
case 'p':
put_positions = false;
break;
case 'n':
put_newlines = false;
break;
}
++it;
}
return it;
}
// format the given bytes range as hex
template<typename FormatContext, typename Container>
auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
{
const char *hex_upper = "0123456789ABCDEF";
const char *hex_lower = "0123456789abcdef";
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
std::size_t pos = 0;
std::size_t column = line_size;
auto inserter = ctx.begin();
for (auto &item:the_range)
{
auto ch = static_cast<unsigned char>(item);
pos++;
if (put_newlines && column >= line_size)
{
column = put_newline(inserter, pos);
// put first byte without delimiter in front of it
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
*inserter++ = hex_chars[ch & 0x0f];
column+= 2;
continue;
}
if (put_delimiters)
{
*inserter++ = delimiter;
++column;
}
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
*inserter++ = hex_chars[ch & 0x0f];
column+= 2;
}
return inserter;
}
// put newline(and position header)
// return the next column
template<typename It>
std::size_t put_newline(It inserter, std::size_t pos)
{
#ifdef _WIN32
*inserter++ = '\r';
#endif
*inserter++ = '\n';
if (put_positions)
{
fmt::format_to(inserter, "{:<04X}: ", pos-1);
return 7;
}
else
{
return 1;
}
}
};
} // namespace fmt

View File

@ -134,3 +134,46 @@ TEST_CASE("clone async", "[clone]")
spdlog::drop_all(); spdlog::drop_all();
} }
#include "spdlog/fmt/bin_to_hex.h"
TEST_CASE("to_hex", "[to_hex]")
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
std::vector<unsigned char> v {9, 0xa, 0xb, 0xc, 0xff, 0xff};
oss_logger.info("{}", spdlog::to_hex(v));
auto output = oss.str();
REQUIRE(ends_with(output, "0000: 09 0a 0b 0c ff ff" + std::string(spdlog::details::os::default_eol)));
}
TEST_CASE("to_hex_upper", "[to_hex]")
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
std::vector<unsigned char> v {9, 0xa, 0xb, 0xc, 0xff, 0xff};
oss_logger.info("{:X}", spdlog::to_hex(v));
auto output = oss.str();
REQUIRE(ends_with(output, "0000: 09 0A 0B 0C FF FF" + std::string(spdlog::details::os::default_eol)));
}
TEST_CASE("to_hex_no_delimiter", "[to_hex]")
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
std::vector<unsigned char> v {9, 0xa, 0xb, 0xc, 0xff, 0xff};
oss_logger.info("{:sX}", spdlog::to_hex(v));
auto output = oss.str();
REQUIRE(ends_with(output, "0000: 090A0B0CFFFF" + std::string(spdlog::details::os::default_eol)));
}