mirror of
https://github.com/gabime/spdlog.git
synced 2025-09-29 01:29:35 +08:00
Compare commits
8 Commits
v1.15.3
...
codex/fix-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0d5d8140ec | ||
![]() |
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
|
@@ -524,6 +524,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
|
||||
|
||||
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 {
|
||||
// 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();
|
||||
basic_example();
|
||||
rotating_example();
|
||||
@@ -371,15 +371,13 @@ void replace_default_logger_example() {
|
||||
// store the old logger so we don't break other examples.
|
||||
auto old_logger = spdlog::default_logger();
|
||||
|
||||
auto new_logger =
|
||||
spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true);
|
||||
spdlog::set_default_logger(new_logger);
|
||||
auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/somelog.txt", true);
|
||||
spdlog::set_default_logger(std::move(new_logger));
|
||||
spdlog::set_level(spdlog::level::info);
|
||||
spdlog::debug("This message should not be displayed!");
|
||||
spdlog::set_level(spdlog::level::trace);
|
||||
spdlog::debug("This message should be displayed..");
|
||||
|
||||
spdlog::set_default_logger(old_logger);
|
||||
spdlog::set_default_logger(std::move(old_logger));
|
||||
}
|
||||
|
||||
// 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/registry.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
@@ -36,7 +35,7 @@ inline std::string &trim_(std::string &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
|
||||
// "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 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"),...}
|
||||
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str) {
|
||||
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) {
|
||||
if (input.empty() || input.size() > 512) {
|
||||
if (input.empty() || input.size() >= 32768) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -82,14 +81,14 @@ SPDLOG_INLINE void load_levels(const std::string &input) {
|
||||
bool global_level_found = false;
|
||||
|
||||
for (auto &name_level : key_vals) {
|
||||
auto &logger_name = name_level.first;
|
||||
auto level_name = to_lower_(name_level.second);
|
||||
const auto &logger_name = name_level.first;
|
||||
const auto &level_name = to_lower_(name_level.second);
|
||||
auto level = level::from_str(level_name);
|
||||
// ignore unrecognized level names
|
||||
if (level == level::off && level_name != "off") {
|
||||
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 = level;
|
||||
|
@@ -11,10 +11,8 @@
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
|
||||
namespace spdlog {
|
||||
|
@@ -563,21 +563,21 @@ SPDLOG_INLINE filename_t dir_name(const filename_t &path) {
|
||||
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) {
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(__cplusplus_winrt)
|
||||
#if defined(_MSC_VER) && defined(__cplusplus_winrt)
|
||||
return std::string{}; // not supported under uwp
|
||||
#else
|
||||
size_t len = 0;
|
||||
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);
|
||||
#else
|
||||
char *buf = std::getenv(field);
|
||||
return buf ? buf : std::string{};
|
||||
#endif
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif // _MSC_VER
|
||||
|
||||
// Do fsync by FILE handlerpointer
|
||||
// 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(); }
|
||||
|
||||
// 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) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
if (new_default_logger != nullptr) {
|
||||
|
@@ -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,
|
||||
size_t threads_n,
|
||||
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)
|
||||
: thread_pool(q_max_items, threads_n, [] {}, [] {}) {}
|
||||
@@ -94,8 +94,7 @@ void SPDLOG_INLINE thread_pool::worker_loop_() {
|
||||
}
|
||||
|
||||
// process next message in the queue
|
||||
// return true if this thread should still be active (while no terminate msg
|
||||
// was received)
|
||||
// returns true if this thread should still be active (while no terminated msg was received)
|
||||
bool SPDLOG_INLINE thread_pool::process_next_msg_() {
|
||||
async_msg incoming_async_msg;
|
||||
q_.dequeue(incoming_async_msg);
|
||||
|
@@ -20,11 +20,7 @@
|
||||
#ifndef FMT_USE_WINDOWS_H
|
||||
#define FMT_USE_WINDOWS_H 0
|
||||
#endif
|
||||
|
||||
#include <spdlog/fmt/bundled/base.h>
|
||||
#include <spdlog/fmt/bundled/format.h>
|
||||
|
||||
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
|
||||
#include <fmt/base.h>
|
||||
#include <fmt/format.h>
|
||||
#endif
|
||||
|
@@ -57,7 +57,7 @@ SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT {
|
||||
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); }
|
||||
|
||||
@@ -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);
|
||||
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_) {
|
||||
custom_err_handler_(msg);
|
||||
} else {
|
||||
|
@@ -363,14 +363,14 @@ protected:
|
||||
virtual void sink_it_(const details::log_msg &msg);
|
||||
virtual void flush_();
|
||||
void dump_backtrace_();
|
||||
bool should_flush_(const details::log_msg &msg);
|
||||
bool should_flush_(const details::log_msg &msg) const;
|
||||
|
||||
// handle errors during logging.
|
||||
// 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
|
||||
|
||||
|
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
void log(const details::log_msg &msg) 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;
|
||||
|
||||
// Formatting codes
|
||||
|
@@ -21,7 +21,11 @@ template <typename Mutex>
|
||||
class ringbuffer_sink final : public base_sink<Mutex> {
|
||||
public:
|
||||
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::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||
|
@@ -31,10 +31,10 @@ public:
|
||||
|
||||
// change the color for the given level
|
||||
void set_color(level::level_enum level, std::uint16_t color);
|
||||
void log(const details::log_msg &msg) final override;
|
||||
void flush() final override;
|
||||
void set_pattern(const std::string &pattern) override final;
|
||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final;
|
||||
void log(const details::log_msg &msg) override;
|
||||
void flush() override;
|
||||
void set_pattern(const std::string &pattern) override;
|
||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
|
||||
void set_color_mode(color_mode mode);
|
||||
|
||||
protected:
|
||||
|
@@ -49,7 +49,8 @@ set(SPDLOG_UTESTS_SOURCES
|
||||
test_time_point.cpp
|
||||
test_stopwatch.cpp
|
||||
test_circular_q.cpp
|
||||
test_bin_to_hex.cpp)
|
||||
test_bin_to_hex.cpp
|
||||
test_ringbuffer.cpp)
|
||||
|
||||
if(NOT SPDLOG_NO_EXCEPTIONS)
|
||||
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)
|
||||
|
50
tests/test_ringbuffer.cpp
Normal file
50
tests/test_ringbuffer.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#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);
|
||||
REQUIRE(formatted[0] == "msg1");
|
||||
REQUIRE(formatted[1] == "msg2");
|
||||
REQUIRE(formatted[2] == "msg3");
|
||||
}
|
||||
|
||||
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);
|
||||
REQUIRE(formatted[0] == "second");
|
||||
REQUIRE(formatted[1] == "third");
|
||||
}
|
||||
|
||||
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);
|
||||
REQUIRE(formatted[0] == "B");
|
||||
REQUIRE(formatted[1] == "C");
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
TEST_CASE("stopwatch1", "[stopwatch]") {
|
||||
using std::chrono::milliseconds;
|
||||
using clock = std::chrono::steady_clock;
|
||||
milliseconds wait_ms(200);
|
||||
milliseconds wait_ms(500);
|
||||
milliseconds tolerance_ms(250);
|
||||
auto start = clock::now();
|
||||
spdlog::stopwatch sw;
|
||||
@@ -22,7 +22,7 @@ TEST_CASE("stopwatch2", "[stopwatch]") {
|
||||
using std::chrono::milliseconds;
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
clock::duration wait_duration(milliseconds(200));
|
||||
clock::duration wait_duration(milliseconds(500));
|
||||
clock::duration tolerance_duration(milliseconds(250));
|
||||
|
||||
auto test_sink = std::make_shared<test_sink_st>();
|
||||
|
Reference in New Issue
Block a user