mirror of
https://github.com/gabime/spdlog.git
synced 2025-09-29 01:29:35 +08:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f1d748e5e3 | ||
![]() |
3edc8036db | ||
![]() |
9ecdf5c8a1 | ||
![]() |
737347d2df | ||
![]() |
4f2b3d52f9 | ||
![]() |
4397dac510 | ||
![]() |
6fd67ce169 | ||
![]() |
4619e18a16 | ||
![]() |
a6215527f4 | ||
![]() |
287333ee00 | ||
![]() |
ad725d34cc | ||
![]() |
e655dbb685 | ||
![]() |
b18a234ed6 | ||
![]() |
5d89b5b91c | ||
![]() |
37ff466454 | ||
![]() |
677a2d93e6 |
52
.github/workflows/coverity_scan.yml
vendored
Normal file
52
.github/workflows/coverity_scan.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
name: coverity-linux
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
coverity_scan:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Coverity Scan
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y curl build-essential cmake pkg-config libsystemd-dev
|
||||||
|
|
||||||
|
- name: Download Coverity Tool
|
||||||
|
run: |
|
||||||
|
curl -s -L --output coverity_tool.tgz "https://scan.coverity.com/download/linux64?token=${{ secrets.COVERITY_TOKEN }}&project=gabime%2Fspdlog"
|
||||||
|
mkdir coverity_tool
|
||||||
|
tar -C coverity_tool --strip-components=1 -xf coverity_tool.tgz
|
||||||
|
echo "$PWD/coverity_tool/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: Build with Coverity
|
||||||
|
run: |
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=17
|
||||||
|
cd ..
|
||||||
|
cov-build --dir cov-int make -C build -j4
|
||||||
|
|
||||||
|
- name: Submit results to Coverity
|
||||||
|
run: |
|
||||||
|
tar czf cov-int.tgz cov-int
|
||||||
|
response=$(curl --silent --show-error --fail \
|
||||||
|
--form email="${{ secrets.EMAIL }}" \
|
||||||
|
--form token="${{ secrets.COVERITY_TOKEN }}" \
|
||||||
|
--form file=@cov-int.tgz \
|
||||||
|
--form version="GitHub PR #${{ github.event.pull_request.number }}" \
|
||||||
|
--form description="CI run for PR" \
|
||||||
|
https://scan.coverity.com/builds?project=gabime%2Fspdlog)
|
||||||
|
|
||||||
|
echo "$response"
|
||||||
|
|
||||||
|
if echo "$response" | grep -qi "Build successfully submitted"; then
|
||||||
|
echo "Coverity upload succeeded"
|
||||||
|
else
|
||||||
|
echo "Coverity upload failed or was rejected"
|
||||||
|
exit 1
|
||||||
|
fi
|
5
.github/workflows/linux.yml
vendored
5
.github/workflows/linux.yml
vendored
@@ -18,9 +18,8 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
config:
|
config:
|
||||||
- { compiler: gcc, version: 7, build_type: Release, cppstd: 11 }
|
- { compiler: gcc, version: 9, build_type: Release, cppstd: 11 }
|
||||||
- { compiler: gcc, version: 9, build_type: Release, cppstd: 17 }
|
- { compiler: gcc, version: 11, build_type: Debug, cppstd: 17 }
|
||||||
- { compiler: gcc, version: 11, build_type: Debug, cppstd: 20 }
|
|
||||||
- { compiler: gcc, version: 12, build_type: Release, cppstd: 20 }
|
- { compiler: gcc, version: 12, build_type: Release, cppstd: 20 }
|
||||||
- { compiler: gcc, version: 12, build_type: Debug, cppstd: 20, asan: ON }
|
- { compiler: gcc, version: 12, build_type: Debug, cppstd: 20, asan: ON }
|
||||||
- { compiler: clang, version: 12, build_type: Debug, cppstd: 17 }
|
- { compiler: clang, version: 12, build_type: Debug, cppstd: 17 }
|
||||||
|
71
.github/workflows/windows.yml
vendored
71
.github/workflows/windows.yml
vendored
@@ -75,74 +75,5 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
build\tests\${{ matrix.config.BUILD_TYPE }}\spdlog-utests.exe
|
build\tests\${{ matrix.config.BUILD_TYPE }}\spdlog-utests.exe
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
# MSVC 2019 build matrix
|
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
build_2019:
|
|
||||||
runs-on: windows-2019
|
|
||||||
strategy:
|
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
|
||||||
config:
|
|
||||||
- GENERATOR: "Visual Studio 16 2019"
|
|
||||||
BUILD_TYPE: Release
|
|
||||||
BUILD_SHARED: 'ON'
|
|
||||||
FATAL_ERRORS: 'ON'
|
|
||||||
WCHAR: 'OFF'
|
|
||||||
WCHAR_FILES: 'OFF'
|
|
||||||
BUILD_EXAMPLE: 'ON'
|
|
||||||
USE_STD_FORMAT: 'OFF'
|
|
||||||
CXX_STANDARD: 17
|
|
||||||
- GENERATOR: "Visual Studio 16 2019"
|
|
||||||
BUILD_TYPE: Release
|
|
||||||
BUILD_SHARED: 'ON'
|
|
||||||
FATAL_ERRORS: 'ON'
|
|
||||||
WCHAR: 'OFF'
|
|
||||||
WCHAR_FILES: 'OFF'
|
|
||||||
BUILD_EXAMPLE: 'ON'
|
|
||||||
USE_STD_FORMAT: 'OFF'
|
|
||||||
CXX_STANDARD: 14
|
|
||||||
- GENERATOR: "Visual Studio 16 2019"
|
|
||||||
BUILD_TYPE: Release
|
|
||||||
BUILD_SHARED: 'ON'
|
|
||||||
FATAL_ERRORS: 'ON'
|
|
||||||
WCHAR: 'OFF'
|
|
||||||
WCHAR_FILES: 'OFF'
|
|
||||||
BUILD_EXAMPLE: 'ON'
|
|
||||||
USE_STD_FORMAT: 'OFF'
|
|
||||||
CXX_STANDARD: 11
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: CMake ${{ matrix.config.GENERATOR }} CXX=${{matrix.config.CXX_STANDARD}} WCHAR=${{matrix.config.WCHAR_FILES}} STD_FORMAT=${{matrix.config.USE_STD_FORMAT}}
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake -G "${{ matrix.config.GENERATOR }}" -A x64 `
|
|
||||||
-D CMAKE_BUILD_TYPE=${{ matrix.config.BUILD_TYPE }} `
|
|
||||||
-D BUILD_SHARED_LIBS=${{ matrix.config.BUILD_SHARED }} `
|
|
||||||
-D SPDLOG_WCHAR_SUPPORT=${{ matrix.config.WCHAR }} `
|
|
||||||
-D SPDLOG_WCHAR_FILENAMES=${{ matrix.config.WCHAR_FILES }} `
|
|
||||||
-D SPDLOG_BUILD_EXAMPLE=${{ matrix.config.BUILD_EXAMPLE }} `
|
|
||||||
-D SPDLOG_BUILD_EXAMPLE_HO=${{ matrix.config.BUILD_EXAMPLE }} `
|
|
||||||
-D SPDLOG_BUILD_TESTS=ON `
|
|
||||||
-D SPDLOG_BUILD_TESTS_HO=OFF `
|
|
||||||
-D SPDLOG_BUILD_WARNINGS=${{ matrix.config.FATAL_ERRORS }} `
|
|
||||||
-D SPDLOG_USE_STD_FORMAT=${{ matrix.config.USE_STD_FORMAT }} `
|
|
||||||
-D CMAKE_CXX_STANDARD=${{ matrix.config.CXX_STANDARD }} ..
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
cd build
|
|
||||||
cmake --build . --parallel --config ${{ matrix.config.BUILD_TYPE }}
|
|
||||||
|
|
||||||
- name: Run Tests
|
|
||||||
shell: pwsh
|
|
||||||
env:
|
|
||||||
PATH: ${{ env.PATH }};${{ github.workspace }}\build\_deps\catch2-build\src\${{ matrix.config.BUILD_TYPE }};${{ github.workspace }}\build\${{ matrix.config.BUILD_TYPE }}
|
|
||||||
run: |
|
|
||||||
build\tests\${{ matrix.config.BUILD_TYPE }}\spdlog-utests.exe
|
|
||||||
|
@@ -62,6 +62,9 @@ option(SPDLOG_ENABLE_PCH "Build static or shared library using precompiled heade
|
|||||||
# build position independent code
|
# build position independent code
|
||||||
option(SPDLOG_BUILD_PIC "Build position independent code (-fPIC)" OFF)
|
option(SPDLOG_BUILD_PIC "Build position independent code (-fPIC)" OFF)
|
||||||
|
|
||||||
|
# debug build postfix
|
||||||
|
set(SPDLOG_DEBUG_POSTFIX "d" CACHE STRING "Filename postfix for libraries in debug builds")
|
||||||
|
|
||||||
# example options
|
# example options
|
||||||
option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
|
option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
|
||||||
option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF)
|
option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF)
|
||||||
@@ -191,7 +194,7 @@ spdlog_enable_warnings(spdlog)
|
|||||||
|
|
||||||
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION
|
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION
|
||||||
${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR})
|
${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR})
|
||||||
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d)
|
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX ${SPDLOG_DEBUG_POSTFIX})
|
||||||
|
|
||||||
if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH)
|
if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH)
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY)
|
||||||
|
34
README.md
34
README.md
@@ -80,7 +80,7 @@ int main()
|
|||||||
spdlog::info("Positional args are {1} {0}..", "too", "supported");
|
spdlog::info("Positional args are {1} {0}..", "too", "supported");
|
||||||
spdlog::info("{:<30}", "left aligned");
|
spdlog::info("{:<30}", "left aligned");
|
||||||
|
|
||||||
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
|
spdlog::set_level(spdlog::level::debug); // Set *global* log level to debug
|
||||||
spdlog::debug("This message should be displayed..");
|
spdlog::debug("This message should be displayed..");
|
||||||
|
|
||||||
// change log pattern
|
// change log pattern
|
||||||
@@ -224,7 +224,7 @@ void binary_example()
|
|||||||
```c++
|
```c++
|
||||||
|
|
||||||
// create a logger with 2 targets, with different log levels and formats.
|
// create a 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.
|
||||||
void multi_sink_example()
|
void multi_sink_example()
|
||||||
{
|
{
|
||||||
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||||
@@ -241,6 +241,29 @@ void multi_sink_example()
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Register several loggers - change global level
|
||||||
|
```c++
|
||||||
|
|
||||||
|
// Creation of loggers. Set levels to all registered loggers.
|
||||||
|
void set_level_example()
|
||||||
|
{
|
||||||
|
auto logger1 = spdlog::basic_logger_mt("logger1", "logs/logger1.txt");
|
||||||
|
auto logger2 = spdlog::basic_logger_mt("logger2", "logs/logger2.txt");
|
||||||
|
|
||||||
|
spdlog::set_default_logger(logger2);
|
||||||
|
spdlog::default_logger()->set_level(spdlog::level::trace); // set level for the default logger (logger2) to trace
|
||||||
|
|
||||||
|
spdlog::trace("trace message to the logger2 (specified as default)");
|
||||||
|
|
||||||
|
spdlog::set_level(spdlog::level::off) // (sic!) set level for *all* registered loggers to off (disable)
|
||||||
|
|
||||||
|
logger1.warn("warn message will not appear because the level set to off");
|
||||||
|
logger2.warn("warn message will not appear because the level set to off");
|
||||||
|
spdlog::warn("warn message will not appear because the level set to off");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
#### User-defined callbacks about log events
|
#### User-defined callbacks about log events
|
||||||
```c++
|
```c++
|
||||||
@@ -524,6 +547,7 @@ Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating product licenses to help develop **spdlog** <a href="https://www.jetbrains.com/?from=spdlog"><img src="logos/jetbrains-variant-4.svg" width="94" align="center" /></a>
|
### Powered by
|
||||||
|
<a href="https://jb.gg/OpenSource">
|
||||||
|
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg" alt="JetBrains logo" width="200">
|
||||||
|
</a>
|
||||||
|
@@ -33,41 +33,41 @@ void mdc_example();
|
|||||||
#include "spdlog/fmt/ostr.h" // support for user defined types
|
#include "spdlog/fmt/ostr.h" // support for user defined types
|
||||||
|
|
||||||
int main(int, char *[]) {
|
int main(int, char *[]) {
|
||||||
// Log levels can be loaded from argv/env using "SPDLOG_LEVEL"
|
|
||||||
load_levels_example();
|
|
||||||
|
|
||||||
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");
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Backtrace support
|
|
||||||
// Loggers can store in a ring buffer all messages (including debug/trace) for later inspection.
|
|
||||||
// When needed, call dump_backtrace() to see what happened:
|
|
||||||
spdlog::enable_backtrace(10); // create ring buffer with capacity of 10 messages
|
|
||||||
for (int i = 0; i < 100; i++) {
|
|
||||||
spdlog::debug("Backtrace message {}", i); // not logged..
|
|
||||||
}
|
|
||||||
// e.g. if some error happened:
|
|
||||||
spdlog::dump_backtrace(); // log them now!
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Log levels can be loaded from argv/env using "SPDLOG_LEVEL"
|
||||||
|
load_levels_example();
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Backtrace support
|
||||||
|
// Loggers can store in a ring buffer all messages (including debug/trace) for later
|
||||||
|
// inspection. When needed, call dump_backtrace() to see what happened:
|
||||||
|
spdlog::enable_backtrace(10); // create ring buffer with capacity of 10 messages
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
spdlog::debug("Backtrace message {}", i); // not logged..
|
||||||
|
}
|
||||||
|
// e.g. if some error happened:
|
||||||
|
spdlog::dump_backtrace(); // log them now!
|
||||||
|
|
||||||
stdout_logger_example();
|
stdout_logger_example();
|
||||||
basic_example();
|
basic_example();
|
||||||
rotating_example();
|
rotating_example();
|
||||||
@@ -371,15 +371,13 @@ void replace_default_logger_example() {
|
|||||||
// store the old logger so we don't break other examples.
|
// store the old logger so we don't break other examples.
|
||||||
auto old_logger = spdlog::default_logger();
|
auto old_logger = spdlog::default_logger();
|
||||||
|
|
||||||
auto new_logger =
|
auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/somelog.txt", true);
|
||||||
spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true);
|
spdlog::set_default_logger(std::move(new_logger));
|
||||||
spdlog::set_default_logger(new_logger);
|
|
||||||
spdlog::set_level(spdlog::level::info);
|
spdlog::set_level(spdlog::level::info);
|
||||||
spdlog::debug("This message should not be displayed!");
|
spdlog::debug("This message should not be displayed!");
|
||||||
spdlog::set_level(spdlog::level::trace);
|
spdlog::set_level(spdlog::level::trace);
|
||||||
spdlog::debug("This message should be displayed..");
|
spdlog::debug("This message should be displayed..");
|
||||||
|
spdlog::set_default_logger(std::move(old_logger));
|
||||||
spdlog::set_default_logger(old_logger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mapped Diagnostic Context (MDC) is a map that stores key-value pairs (string values) in thread
|
// Mapped Diagnostic Context (MDC) is a map that stores key-value pairs (string values) in thread
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#include <spdlog/details/os.h>
|
#include <spdlog/details/os.h>
|
||||||
#include <spdlog/details/registry.h>
|
#include <spdlog/details/registry.h>
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -36,7 +35,7 @@ inline std::string &trim_(std::string &str) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return (name,value) trimmed pair from given "name=value" string.
|
// return (name,value) trimmed pair from the given "name = value" string.
|
||||||
// return empty string on missing parts
|
// return empty string on missing parts
|
||||||
// "key=val" => ("key", "val")
|
// "key=val" => ("key", "val")
|
||||||
// " key = val " => ("key", "val")
|
// " key = val " => ("key", "val")
|
||||||
@@ -55,7 +54,7 @@ inline std::pair<std::string, std::string> extract_kv_(char sep, const std::stri
|
|||||||
return std::make_pair(trim_(k), trim_(v));
|
return std::make_pair(trim_(k), trim_(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
|
// return vector of key/value pairs from a sequence of "K1=V1,K2=V2,.."
|
||||||
// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
|
// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
|
||||||
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str) {
|
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str) {
|
||||||
std::string token;
|
std::string token;
|
||||||
@@ -72,7 +71,7 @@ inline std::unordered_map<std::string, std::string> extract_key_vals_(const std:
|
|||||||
}
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE void load_levels(const std::string &input) {
|
SPDLOG_INLINE void load_levels(const std::string &input) {
|
||||||
if (input.empty() || input.size() > 512) {
|
if (input.empty() || input.size() >= 32768) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,14 +81,14 @@ SPDLOG_INLINE void load_levels(const std::string &input) {
|
|||||||
bool global_level_found = false;
|
bool global_level_found = false;
|
||||||
|
|
||||||
for (auto &name_level : key_vals) {
|
for (auto &name_level : key_vals) {
|
||||||
auto &logger_name = name_level.first;
|
const auto &logger_name = name_level.first;
|
||||||
auto level_name = to_lower_(name_level.second);
|
const auto &level_name = to_lower_(name_level.second);
|
||||||
auto level = level::from_str(level_name);
|
auto level = level::from_str(level_name);
|
||||||
// ignore unrecognized level names
|
// ignore unrecognized level names
|
||||||
if (level == level::off && level_name != "off") {
|
if (level == level::off && level_name != "off") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (logger_name.empty()) // no logger name indicate global level
|
if (logger_name.empty()) // no logger name indicates global level
|
||||||
{
|
{
|
||||||
global_level_found = true;
|
global_level_found = true;
|
||||||
global_level = level;
|
global_level = level;
|
||||||
|
@@ -11,10 +11,8 @@
|
|||||||
#include <spdlog/details/os.h>
|
#include <spdlog/details/os.h>
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <spdlog/details/windows_include.h>
|
#include <spdlog/details/windows_include.h>
|
||||||
#include <fileapi.h> // for FlushFileBuffers
|
|
||||||
#include <io.h> // for _get_osfhandle, _isatty, _fileno
|
#include <io.h> // for _get_osfhandle, _isatty, _fileno
|
||||||
#include <process.h> // for _get_pid
|
#include <process.h> // for _get_pid
|
||||||
|
|
||||||
@@ -563,21 +562,21 @@ SPDLOG_INLINE filename_t dir_name(const filename_t &path) {
|
|||||||
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
|
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4996)
|
||||||
|
#endif // _MSC_VER
|
||||||
std::string SPDLOG_INLINE getenv(const char *field) {
|
std::string SPDLOG_INLINE getenv(const char *field) {
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER) && defined(__cplusplus_winrt)
|
||||||
#if defined(__cplusplus_winrt)
|
|
||||||
return std::string{}; // not supported under uwp
|
return std::string{}; // not supported under uwp
|
||||||
#else
|
#else
|
||||||
size_t len = 0;
|
char *buf = std::getenv(field);
|
||||||
char buf[128];
|
|
||||||
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
|
|
||||||
return ok ? buf : std::string{};
|
|
||||||
#endif
|
|
||||||
#else // revert to getenv
|
|
||||||
char *buf = ::getenv(field);
|
|
||||||
return buf ? buf : std::string{};
|
return buf ? buf : std::string{};
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
// Do fsync by FILE handlerpointer
|
// Do fsync by FILE handlerpointer
|
||||||
// Return true on success
|
// Return true on success
|
||||||
|
@@ -101,7 +101,7 @@ SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger() {
|
|||||||
SPDLOG_INLINE logger *registry::get_default_raw() { return default_logger_.get(); }
|
SPDLOG_INLINE logger *registry::get_default_raw() { return default_logger_.get(); }
|
||||||
|
|
||||||
// set default logger.
|
// set default logger.
|
||||||
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
// the default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||||
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) {
|
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) {
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
if (new_default_logger != nullptr) {
|
if (new_default_logger != nullptr) {
|
||||||
|
@@ -57,9 +57,82 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
SOCKET fd() const { return socket_; }
|
SOCKET fd() const { return socket_; }
|
||||||
|
|
||||||
|
int connect_socket_with_timeout(SOCKET sockfd,
|
||||||
|
const struct sockaddr *addr,
|
||||||
|
int addrlen,
|
||||||
|
const timeval &tv) {
|
||||||
|
// If no timeout requested, do a normal blocking connect.
|
||||||
|
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
|
||||||
|
int rv = ::connect(sockfd, addr, addrlen);
|
||||||
|
if (rv == SOCKET_ERROR && WSAGetLastError() == WSAEISCONN) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to non‐blocking mode
|
||||||
|
u_long mode = 1UL;
|
||||||
|
if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) {
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rv = ::connect(sockfd, addr, addrlen);
|
||||||
|
int last_error = WSAGetLastError();
|
||||||
|
if (rv == 0 || last_error == WSAEISCONN) {
|
||||||
|
mode = 0UL;
|
||||||
|
if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) {
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (last_error != WSAEWOULDBLOCK) {
|
||||||
|
// Real error
|
||||||
|
mode = 0UL;
|
||||||
|
if (::ioctlsocket(sockfd, FIONBIO, &mode)) {
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until socket is writable or timeout expires
|
||||||
|
fd_set wfds;
|
||||||
|
FD_ZERO(&wfds);
|
||||||
|
FD_SET(sockfd, &wfds);
|
||||||
|
|
||||||
|
rv = ::select(0, nullptr, &wfds, nullptr, const_cast<timeval *>(&tv));
|
||||||
|
|
||||||
|
// Restore blocking mode regardless of select result
|
||||||
|
mode = 0UL;
|
||||||
|
if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) {
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rv == 0) {
|
||||||
|
WSASetLastError(WSAETIMEDOUT);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
if (rv == SOCKET_ERROR) {
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
int so_error = 0;
|
||||||
|
int len = sizeof(so_error);
|
||||||
|
if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&so_error), &len) ==
|
||||||
|
SOCKET_ERROR) {
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
if (so_error != 0 && so_error != WSAEISCONN) {
|
||||||
|
// connection failed
|
||||||
|
WSASetLastError(so_error);
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // success
|
||||||
|
}
|
||||||
|
|
||||||
// try to connect or throw on failure
|
// try to connect or throw on failure
|
||||||
void connect(const std::string &host, int port) {
|
void connect(const std::string &host, int port, int timeout_ms = 0) {
|
||||||
if (is_connected()) {
|
if (is_connected()) {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
@@ -71,6 +144,10 @@ public:
|
|||||||
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
||||||
hints.ai_protocol = 0;
|
hints.ai_protocol = 0;
|
||||||
|
|
||||||
|
timeval tv;
|
||||||
|
tv.tv_sec = timeout_ms / 1000;
|
||||||
|
tv.tv_usec = (timeout_ms % 1000) * 1000;
|
||||||
|
|
||||||
auto port_str = std::to_string(port);
|
auto port_str = std::to_string(port);
|
||||||
struct addrinfo *addrinfo_result;
|
struct addrinfo *addrinfo_result;
|
||||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||||
@@ -82,7 +159,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try each address until we successfully connect(2).
|
// Try each address until we successfully connect(2).
|
||||||
|
|
||||||
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) {
|
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) {
|
||||||
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||||
if (socket_ == INVALID_SOCKET) {
|
if (socket_ == INVALID_SOCKET) {
|
||||||
@@ -90,18 +166,24 @@ public:
|
|||||||
WSACleanup();
|
WSACleanup();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0) {
|
if (connect_socket_with_timeout(socket_, rp->ai_addr, (int)rp->ai_addrlen, tv) == 0) {
|
||||||
|
last_error = 0;
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
last_error = ::WSAGetLastError();
|
|
||||||
close();
|
|
||||||
}
|
}
|
||||||
|
last_error = WSAGetLastError();
|
||||||
|
::closesocket(socket_);
|
||||||
|
socket_ = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
::freeaddrinfo(addrinfo_result);
|
::freeaddrinfo(addrinfo_result);
|
||||||
if (socket_ == INVALID_SOCKET) {
|
if (socket_ == INVALID_SOCKET) {
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
throw_winsock_error_("connect failed", last_error);
|
throw_winsock_error_("connect failed", last_error);
|
||||||
}
|
}
|
||||||
|
if (timeout_ms > 0) {
|
||||||
|
DWORD tv = static_cast<DWORD>(timeout_ms);
|
||||||
|
::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
|
||||||
|
::setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv));
|
||||||
|
}
|
||||||
|
|
||||||
// set TCP_NODELAY
|
// set TCP_NODELAY
|
||||||
int enable_flag = 1;
|
int enable_flag = 1;
|
||||||
|
@@ -39,8 +39,72 @@ public:
|
|||||||
|
|
||||||
~tcp_client() { close(); }
|
~tcp_client() { close(); }
|
||||||
|
|
||||||
|
int connect_socket_with_timeout(int sockfd,
|
||||||
|
const struct sockaddr *addr,
|
||||||
|
socklen_t addrlen,
|
||||||
|
const timeval &tv) {
|
||||||
|
// Blocking connect if timeout is zero
|
||||||
|
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
|
||||||
|
int rv = ::connect(sockfd, addr, addrlen);
|
||||||
|
if (rv < 0 && errno == EISCONN) {
|
||||||
|
// already connected, treat as success
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-blocking path
|
||||||
|
int orig_flags = ::fcntl(sockfd, F_GETFL, 0);
|
||||||
|
if (orig_flags < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (::fcntl(sockfd, F_SETFL, orig_flags | O_NONBLOCK) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rv = ::connect(sockfd, addr, addrlen);
|
||||||
|
if (rv == 0 || (rv < 0 && errno == EISCONN)) {
|
||||||
|
// immediate connect or already connected
|
||||||
|
::fcntl(sockfd, F_SETFL, orig_flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (errno != EINPROGRESS) {
|
||||||
|
::fcntl(sockfd, F_SETFL, orig_flags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for writability
|
||||||
|
fd_set wfds;
|
||||||
|
FD_ZERO(&wfds);
|
||||||
|
FD_SET(sockfd, &wfds);
|
||||||
|
|
||||||
|
struct timeval tv_copy = tv;
|
||||||
|
rv = ::select(sockfd + 1, nullptr, &wfds, nullptr, &tv_copy);
|
||||||
|
if (rv <= 0) {
|
||||||
|
// timeout or error
|
||||||
|
::fcntl(sockfd, F_SETFL, orig_flags);
|
||||||
|
if (rv == 0) errno = ETIMEDOUT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check socket error
|
||||||
|
int so_error = 0;
|
||||||
|
socklen_t len = sizeof(so_error);
|
||||||
|
if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len) < 0) {
|
||||||
|
::fcntl(sockfd, F_SETFL, orig_flags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
::fcntl(sockfd, F_SETFL, orig_flags);
|
||||||
|
if (so_error != 0 && so_error != EISCONN) {
|
||||||
|
errno = so_error;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// try to connect or throw on failure
|
// try to connect or throw on failure
|
||||||
void connect(const std::string &host, int port) {
|
void connect(const std::string &host, int port, int timeout_ms = 0) {
|
||||||
close();
|
close();
|
||||||
struct addrinfo hints {};
|
struct addrinfo hints {};
|
||||||
memset(&hints, 0, sizeof(struct addrinfo));
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
@@ -49,6 +113,10 @@ public:
|
|||||||
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
||||||
hints.ai_protocol = 0;
|
hints.ai_protocol = 0;
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = timeout_ms / 1000;
|
||||||
|
tv.tv_usec = (timeout_ms % 1000) * 1000;
|
||||||
|
|
||||||
auto port_str = std::to_string(port);
|
auto port_str = std::to_string(port);
|
||||||
struct addrinfo *addrinfo_result;
|
struct addrinfo *addrinfo_result;
|
||||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||||
@@ -69,8 +137,9 @@ public:
|
|||||||
last_errno = errno;
|
last_errno = errno;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
|
::fcntl(socket_, F_SETFD, FD_CLOEXEC);
|
||||||
if (rv == 0) {
|
if (connect_socket_with_timeout(socket_, rp->ai_addr, rp->ai_addrlen, tv) == 0) {
|
||||||
|
last_errno = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
last_errno = errno;
|
last_errno = errno;
|
||||||
@@ -82,6 +151,12 @@ public:
|
|||||||
throw_spdlog_ex("::connect failed", last_errno);
|
throw_spdlog_ex("::connect failed", last_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (timeout_ms > 0) {
|
||||||
|
// Set timeouts for send and recv
|
||||||
|
::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
|
||||||
|
::setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv));
|
||||||
|
}
|
||||||
|
|
||||||
// set TCP_NODELAY
|
// set TCP_NODELAY
|
||||||
int enable_flag = 1;
|
int enable_flag = 1;
|
||||||
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag),
|
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag),
|
||||||
|
@@ -35,7 +35,7 @@ SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,
|
|||||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,
|
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,
|
||||||
size_t threads_n,
|
size_t threads_n,
|
||||||
std::function<void()> on_thread_start)
|
std::function<void()> on_thread_start)
|
||||||
: thread_pool(q_max_items, threads_n, on_thread_start, [] {}) {}
|
: thread_pool(q_max_items, threads_n, std::move(on_thread_start), [] {}) {}
|
||||||
|
|
||||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
|
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
|
||||||
: thread_pool(q_max_items, threads_n, [] {}, [] {}) {}
|
: thread_pool(q_max_items, threads_n, [] {}, [] {}) {}
|
||||||
@@ -94,8 +94,7 @@ void SPDLOG_INLINE thread_pool::worker_loop_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// process next message in the queue
|
// process next message in the queue
|
||||||
// return true if this thread should still be active (while no terminate msg
|
// returns true if this thread should still be active (while no terminated msg was received)
|
||||||
// was received)
|
|
||||||
bool SPDLOG_INLINE thread_pool::process_next_msg_() {
|
bool SPDLOG_INLINE thread_pool::process_next_msg_() {
|
||||||
async_msg incoming_async_msg;
|
async_msg incoming_async_msg;
|
||||||
q_.dequeue(incoming_async_msg);
|
q_.dequeue(incoming_async_msg);
|
||||||
|
@@ -20,11 +20,7 @@
|
|||||||
#ifndef FMT_USE_WINDOWS_H
|
#ifndef FMT_USE_WINDOWS_H
|
||||||
#define FMT_USE_WINDOWS_H 0
|
#define FMT_USE_WINDOWS_H 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <spdlog/fmt/bundled/base.h>
|
|
||||||
#include <spdlog/fmt/bundled/format.h>
|
#include <spdlog/fmt/bundled/format.h>
|
||||||
|
|
||||||
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
|
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
|
||||||
#include <fmt/base.h>
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#endif
|
#endif
|
||||||
|
@@ -57,7 +57,7 @@ SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT {
|
|||||||
std::swap(tracer_, other.tracer_);
|
std::swap(tracer_, other.tracer_);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE void swap(logger &a, logger &b) { a.swap(b); }
|
SPDLOG_INLINE void swap(logger &a, logger &b) noexcept { a.swap(b); }
|
||||||
|
|
||||||
SPDLOG_INLINE void logger::set_level(level::level_enum log_level) { level_.store(log_level); }
|
SPDLOG_INLINE void logger::set_level(level::level_enum log_level) { level_.store(log_level); }
|
||||||
|
|
||||||
@@ -163,12 +163,12 @@ SPDLOG_INLINE void logger::dump_backtrace_() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg) {
|
SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg) const {
|
||||||
auto flush_level = flush_level_.load(std::memory_order_relaxed);
|
auto flush_level = flush_level_.load(std::memory_order_relaxed);
|
||||||
return (msg.level >= flush_level) && (msg.level != level::off);
|
return (msg.level >= flush_level) && (msg.level != level::off);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE void logger::err_handler_(const std::string &msg) {
|
SPDLOG_INLINE void logger::err_handler_(const std::string &msg) const {
|
||||||
if (custom_err_handler_) {
|
if (custom_err_handler_) {
|
||||||
custom_err_handler_(msg);
|
custom_err_handler_(msg);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -363,14 +363,14 @@ protected:
|
|||||||
virtual void sink_it_(const details::log_msg &msg);
|
virtual void sink_it_(const details::log_msg &msg);
|
||||||
virtual void flush_();
|
virtual void flush_();
|
||||||
void dump_backtrace_();
|
void dump_backtrace_();
|
||||||
bool should_flush_(const details::log_msg &msg);
|
bool should_flush_(const details::log_msg &msg) const;
|
||||||
|
|
||||||
// handle errors during logging.
|
// handle errors during logging.
|
||||||
// default handler prints the error to stderr at max rate of 1 message/sec.
|
// default handler prints the error to stderr at max rate of 1 message/sec.
|
||||||
void err_handler_(const std::string &msg);
|
void err_handler_(const std::string &msg) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
void swap(logger &a, logger &b);
|
void swap(logger &a, logger &b) noexcept;
|
||||||
|
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
@@ -40,7 +40,7 @@ public:
|
|||||||
|
|
||||||
void log(const details::log_msg &msg) override;
|
void log(const details::log_msg &msg) override;
|
||||||
void flush() override;
|
void flush() override;
|
||||||
void set_pattern(const std::string &pattern) final override;
|
void set_pattern(const std::string &pattern) override;
|
||||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
|
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
|
||||||
|
|
||||||
// Formatting codes
|
// Formatting codes
|
||||||
|
@@ -21,7 +21,11 @@ template <typename Mutex>
|
|||||||
class ringbuffer_sink final : public base_sink<Mutex> {
|
class ringbuffer_sink final : public base_sink<Mutex> {
|
||||||
public:
|
public:
|
||||||
explicit ringbuffer_sink(size_t n_items)
|
explicit ringbuffer_sink(size_t n_items)
|
||||||
: q_{n_items} {}
|
: q_{n_items} {
|
||||||
|
if (n_items == 0) {
|
||||||
|
throw_spdlog_ex("ringbuffer_sink: n_items cannot be zero");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<details::log_msg_buffer> last_raw(size_t lim = 0) {
|
std::vector<details::log_msg_buffer> last_raw(size_t lim = 0) {
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
@@ -31,6 +31,7 @@ namespace sinks {
|
|||||||
struct tcp_sink_config {
|
struct tcp_sink_config {
|
||||||
std::string server_host;
|
std::string server_host;
|
||||||
int server_port;
|
int server_port;
|
||||||
|
int timeout_ms = 0; // The timeout for all 3 major socket operations that is connect, send, and recv
|
||||||
bool lazy_connect = false; // if true connect on first log call instead of on construction
|
bool lazy_connect = false; // if true connect on first log call instead of on construction
|
||||||
|
|
||||||
tcp_sink_config(std::string host, int port)
|
tcp_sink_config(std::string host, int port)
|
||||||
@@ -44,10 +45,22 @@ public:
|
|||||||
// connect to tcp host/port or throw if failed
|
// connect to tcp host/port or throw if failed
|
||||||
// host can be hostname or ip address
|
// host can be hostname or ip address
|
||||||
|
|
||||||
|
explicit tcp_sink(const std::string &host,
|
||||||
|
int port,
|
||||||
|
int timeout_ms = 0,
|
||||||
|
bool lazy_connect = false)
|
||||||
|
: config_{host, port} {
|
||||||
|
config_.timeout_ms = timeout_ms;
|
||||||
|
config_.lazy_connect = lazy_connect;
|
||||||
|
if (!config_.lazy_connect) {
|
||||||
|
client_.connect(config_.server_host, config_.server_port, config_.timeout_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
explicit tcp_sink(tcp_sink_config sink_config)
|
explicit tcp_sink(tcp_sink_config sink_config)
|
||||||
: config_{std::move(sink_config)} {
|
: config_{std::move(sink_config)} {
|
||||||
if (!config_.lazy_connect) {
|
if (!config_.lazy_connect) {
|
||||||
this->client_.connect(config_.server_host, config_.server_port);
|
client_.connect(config_.server_host, config_.server_port, config_.timeout_ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +71,7 @@ protected:
|
|||||||
spdlog::memory_buf_t formatted;
|
spdlog::memory_buf_t formatted;
|
||||||
spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
|
spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
if (!client_.is_connected()) {
|
if (!client_.is_connected()) {
|
||||||
client_.connect(config_.server_host, config_.server_port);
|
client_.connect(config_.server_host, config_.server_port, config_.timeout_ms);
|
||||||
}
|
}
|
||||||
client_.send(formatted.data(), formatted.size());
|
client_.send(formatted.data(), formatted.size());
|
||||||
}
|
}
|
||||||
|
@@ -31,10 +31,10 @@ public:
|
|||||||
|
|
||||||
// change the color for the given level
|
// change the color for the given level
|
||||||
void set_color(level::level_enum level, std::uint16_t color);
|
void set_color(level::level_enum level, std::uint16_t color);
|
||||||
void log(const details::log_msg &msg) final override;
|
void log(const details::log_msg &msg) override;
|
||||||
void flush() final override;
|
void flush() override;
|
||||||
void set_pattern(const std::string &pattern) override final;
|
void set_pattern(const std::string &pattern) override;
|
||||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final;
|
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
|
||||||
void set_color_mode(color_mode mode);
|
void set_color_mode(color_mode mode);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@@ -49,7 +49,8 @@ set(SPDLOG_UTESTS_SOURCES
|
|||||||
test_time_point.cpp
|
test_time_point.cpp
|
||||||
test_stopwatch.cpp
|
test_stopwatch.cpp
|
||||||
test_circular_q.cpp
|
test_circular_q.cpp
|
||||||
test_bin_to_hex.cpp)
|
test_bin_to_hex.cpp
|
||||||
|
test_ringbuffer.cpp)
|
||||||
|
|
||||||
if(NOT SPDLOG_NO_EXCEPTIONS)
|
if(NOT SPDLOG_NO_EXCEPTIONS)
|
||||||
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)
|
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)
|
||||||
@@ -74,7 +75,7 @@ function(spdlog_prepare_test test_target spdlog_lib)
|
|||||||
elseif(SPDLOG_SANITIZE_THREAD)
|
elseif(SPDLOG_SANITIZE_THREAD)
|
||||||
spdlog_enable_thread_sanitizer(${test_target})
|
spdlog_enable_thread_sanitizer(${test_target})
|
||||||
endif()
|
endif()
|
||||||
add_test(NAME ${test_target} COMMAND ${test_target})
|
add_test(NAME ${test_target} COMMAND ${test_target} --order decl)
|
||||||
set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON)
|
set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
53
tests/test_ringbuffer.cpp
Normal file
53
tests/test_ringbuffer.cpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#include "includes.h"
|
||||||
|
#include "spdlog/sinks/ringbuffer_sink.h"
|
||||||
|
|
||||||
|
TEST_CASE("ringbuffer invalid size", "[ringbuffer]") {
|
||||||
|
REQUIRE_THROWS_AS(spdlog::sinks::ringbuffer_sink_mt(0), spdlog::spdlog_ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ringbuffer stores formatted messages", "[ringbuffer]") {
|
||||||
|
spdlog::sinks::ringbuffer_sink_st sink(3);
|
||||||
|
sink.set_pattern("%v");
|
||||||
|
|
||||||
|
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg1"});
|
||||||
|
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg2"});
|
||||||
|
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg3"});
|
||||||
|
|
||||||
|
auto formatted = sink.last_formatted();
|
||||||
|
REQUIRE(formatted.size() == 3);
|
||||||
|
using spdlog::details::os::default_eol;
|
||||||
|
REQUIRE(formatted[0] == spdlog::fmt_lib::format("msg1{}", default_eol));
|
||||||
|
REQUIRE(formatted[1] == spdlog::fmt_lib::format("msg2{}", default_eol));
|
||||||
|
REQUIRE(formatted[2] == spdlog::fmt_lib::format("msg3{}", default_eol));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ringbuffer overrun keeps last items", "[ringbuffer]") {
|
||||||
|
spdlog::sinks::ringbuffer_sink_st sink(2);
|
||||||
|
sink.set_pattern("%v");
|
||||||
|
|
||||||
|
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "first"});
|
||||||
|
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "second"});
|
||||||
|
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "third"});
|
||||||
|
|
||||||
|
auto formatted = sink.last_formatted();
|
||||||
|
REQUIRE(formatted.size() == 2);
|
||||||
|
using spdlog::details::os::default_eol;
|
||||||
|
REQUIRE(formatted[0] == spdlog::fmt_lib::format("second{}", default_eol));
|
||||||
|
REQUIRE(formatted[1] == spdlog::fmt_lib::format("third{}", default_eol));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ringbuffer retrieval limit", "[ringbuffer]") {
|
||||||
|
spdlog::sinks::ringbuffer_sink_st sink(3);
|
||||||
|
sink.set_pattern("%v");
|
||||||
|
|
||||||
|
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "A"});
|
||||||
|
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "B"});
|
||||||
|
sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "C"});
|
||||||
|
|
||||||
|
auto formatted = sink.last_formatted(2);
|
||||||
|
REQUIRE(formatted.size() == 2);
|
||||||
|
using spdlog::details::os::default_eol;
|
||||||
|
REQUIRE(formatted[0] == spdlog::fmt_lib::format("B{}", default_eol));
|
||||||
|
REQUIRE(formatted[1] == spdlog::fmt_lib::format("C{}", default_eol));
|
||||||
|
}
|
||||||
|
|
@@ -5,7 +5,7 @@
|
|||||||
TEST_CASE("stopwatch1", "[stopwatch]") {
|
TEST_CASE("stopwatch1", "[stopwatch]") {
|
||||||
using std::chrono::milliseconds;
|
using std::chrono::milliseconds;
|
||||||
using clock = std::chrono::steady_clock;
|
using clock = std::chrono::steady_clock;
|
||||||
milliseconds wait_ms(200);
|
milliseconds wait_ms(500);
|
||||||
milliseconds tolerance_ms(250);
|
milliseconds tolerance_ms(250);
|
||||||
auto start = clock::now();
|
auto start = clock::now();
|
||||||
spdlog::stopwatch sw;
|
spdlog::stopwatch sw;
|
||||||
@@ -22,7 +22,7 @@ TEST_CASE("stopwatch2", "[stopwatch]") {
|
|||||||
using std::chrono::milliseconds;
|
using std::chrono::milliseconds;
|
||||||
using clock = std::chrono::steady_clock;
|
using clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
clock::duration wait_duration(milliseconds(200));
|
clock::duration wait_duration(milliseconds(500));
|
||||||
clock::duration tolerance_duration(milliseconds(250));
|
clock::duration tolerance_duration(milliseconds(250));
|
||||||
|
|
||||||
auto test_sink = std::make_shared<test_sink_st>();
|
auto test_sink = std::make_shared<test_sink_st>();
|
||||||
|
Reference in New Issue
Block a user