Merge branch 'v1.x' into feature-source_location

pull/2690/head
M. Galib Uludag 2 years ago committed by GitHub
commit 7b378662cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -185,7 +185,8 @@ target_include_directories(spdlog ${SPDLOG_INCLUDES_LEVEL} PUBLIC "$<BUILD_INTER
target_link_libraries(spdlog PUBLIC Threads::Threads)
spdlog_enable_warnings(spdlog)
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION ${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR})
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION
${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR})
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d)
if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH)
@ -199,7 +200,8 @@ endif()
add_library(spdlog_header_only INTERFACE)
add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only)
target_include_directories(spdlog_header_only ${SPDLOG_INCLUDES_LEVEL} INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
target_include_directories(
spdlog_header_only ${SPDLOG_INCLUDES_LEVEL} INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
@ -255,10 +257,19 @@ foreach(
endif()
endforeach()
if(SPDLOG_NO_EXCEPTIONS AND NOT MSVC)
# ---------------------------------------------------------------------------------------
# If exceptions are disabled, disable them in the bundled fmt as well
# ---------------------------------------------------------------------------------------
if(SPDLOG_NO_EXCEPTIONS)
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
target_compile_definitions(spdlog PUBLIC FMT_EXCEPTIONS=0)
endif()
if(NOT MSVC)
target_compile_options(spdlog PRIVATE -fno-exceptions)
else()
target_compile_options(spdlog PRIVATE /EHsc)
endif()
endif()
# ---------------------------------------------------------------------------------------
# Build binaries
# ---------------------------------------------------------------------------------------
@ -333,15 +344,11 @@ if(SPDLOG_INSTALL)
# ---------------------------------------------------------------------------------------
# Install CMake config files
# ---------------------------------------------------------------------------------------
export(
TARGETS spdlog
NAMESPACE spdlog::
FILE "${CMAKE_CURRENT_BINARY_DIR}/${config_targets_file}")
export(TARGETS spdlog NAMESPACE spdlog:: FILE "${CMAKE_CURRENT_BINARY_DIR}/${config_targets_file}")
install(EXPORT spdlog DESTINATION ${export_dest_dir} NAMESPACE spdlog:: FILE ${config_targets_file})
include(CMakePackageConfigHelpers)
configure_package_config_file("${project_config_in}" "${project_config_out}"
INSTALL_DESTINATION ${export_dest_dir})
configure_package_config_file("${project_config_in}" "${project_config_out}" INSTALL_DESTINATION ${export_dest_dir})
write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion)
install(FILES "${project_config_out}" "${version_config_file}" DESTINATION "${export_dest_dir}")

@ -50,6 +50,7 @@ $ cmake .. && make -j
* syslog.
* Windows event log.
* Windows debugger (```OutputDebugString(..)```).
* Log to Qt widgets ([example](#log-to-qt-with-nice-colors)).
* Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets.
* Log filtering - log levels can be modified at runtime as well as compile time.
* Support for loading log levels from argv or environment var.
@ -420,6 +421,22 @@ void replace_default_logger_example()
}
```
---
#### Log to Qt with nice colors
```c++
#include "spdlog/spdlog.h"
#include "spdlog/sinks/qt_sinks.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
setMinimumSize(640, 480);
auto log_widget = new QTextEdit(this);
setCentralWidget(log_widget);
int max_lines = 500; // keep the text widget to max 500 lines. remove old lines if needed.
auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget, max_lines);
logger->info("Some info message");
}
```
---
## Benchmarks

@ -116,9 +116,6 @@ int main(int argc, char *argv[])
tracing_null_logger_st->enable_backtrace(64);
benchmark::RegisterBenchmark("null_sink_st/backtrace", bench_logger, tracing_null_logger_st);
#ifdef __linux
bench_dev_null();
#endif // __linux__

@ -24,13 +24,8 @@ SPDLOG_INLINE spdlog::async_logger::async_logger(
{}
// send the log message to the thread pool
SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
{
SPDLOG_TRY
{
if (auto pool_ptr = thread_pool_.lock())
{
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg){
SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
}
else
{
@ -41,13 +36,8 @@ SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
}
// send flush request to the thread pool
SPDLOG_INLINE void spdlog::async_logger::flush_()
{
SPDLOG_TRY
{
if (auto pool_ptr = thread_pool_.lock())
{
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
SPDLOG_INLINE void spdlog::async_logger::flush_(){
SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){pool_ptr->post_flush(shared_from_this(), overflow_policy_);
}
else
{

@ -109,7 +109,8 @@
# define SPDLOG_TRY try
# define SPDLOG_THROW(ex) throw(ex)
# define SPDLOG_CATCH_STD \
catch (const std::exception &) {}
catch (const std::exception &) \
{}
#endif
#if SPDLOG_CPLUSPLUS > 201703L
@ -246,6 +247,15 @@ using format_string_t = format_string_wrapper<fmt::format_string<Args...>, char>
template<class T>
using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
template<typename Char>
# if FMT_VERSION >= 90101
using fmt_runtime_string = fmt::runtime_format_string<Char>;
# else
using fmt_runtime_string = fmt::basic_runtime<Char>;
# endif
// clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the condition from basic_format_string here,
// in addition, fmt::basic_runtime<Char> is only convertible to basic_format_string<Char> but not basic_string_view<Char>
template<class T, class Char = char>

@ -292,7 +292,8 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
return offset;
#else
# if defined(sun) || defined(__sun) || defined(_AIX) || (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
# if defined(sun) || defined(__sun) || defined(_AIX) || (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \
(!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
struct helper
{
@ -364,9 +365,12 @@ SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
# if (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || defined(__POWERPC__)
tid = pthread_mach_thread_np(pthread_self());
# elif MAC_OS_X_VERSION_MIN_REQUIRED < 1060
if (&pthread_threadid_np) {
if (&pthread_threadid_np)
{
pthread_threadid_np(nullptr, &tid);
} else {
}
else
{
tid = pthread_mach_thread_np(pthread_self());
}
# else

@ -370,7 +370,7 @@ protected:
{
memory_buf_t buf;
#ifdef SPDLOG_USE_STD_FORMAT
fmt_lib::vformat_to(std::back_inserter(buf), fmt, fmt_lib::make_format_args(std::forward<Args>(args)...));
fmt_lib::vformat_to(std::back_inserter(buf), fmt, fmt_lib::make_format_args(args...));
#else
fmt::vformat_to(fmt::appender(buf), fmt, fmt::make_format_args(args...));
#endif
@ -395,8 +395,7 @@ protected:
{
// format to wmemory_buffer and convert to utf8
wmemory_buf_t wbuf;
fmt_lib::vformat_to(
std::back_inserter(wbuf), fmt, fmt_lib::make_format_args<fmt_lib::wformat_context>(std::forward<Args>(args)...));
fmt_lib::vformat_to(std::back_inserter(wbuf), fmt, fmt_lib::make_format_args<fmt_lib::wformat_context>(args...));
memory_buf_t buf;
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);

@ -21,20 +21,20 @@ SPDLOG_INLINE ansicolor_sink<ConsoleMutex>::ansicolor_sink(FILE *target_file, co
{
set_color_mode(mode);
colors_[level::trace] = to_string_(white);
colors_[level::debug] = to_string_(cyan);
colors_[level::info] = to_string_(green);
colors_[level::warn] = to_string_(yellow_bold);
colors_[level::err] = to_string_(red_bold);
colors_[level::critical] = to_string_(bold_on_red);
colors_[level::off] = to_string_(reset);
colors_.at(level::trace) = to_string_(white);
colors_.at(level::debug) = to_string_(cyan);
colors_.at(level::info) = to_string_(green);
colors_.at(level::warn) = to_string_(yellow_bold);
colors_.at(level::err) = to_string_(red_bold);
colors_.at(level::critical) = to_string_(bold_on_red);
colors_.at(level::off) = to_string_(reset);
}
template<typename ConsoleMutex>
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color(level::level_enum color_level, string_view_t color)
{
std::lock_guard<mutex_t> lock(mutex_);
colors_[static_cast<size_t>(color_level)] = to_string_(color);
colors_.at(static_cast<size_t>(color_level)) = to_string_(color);
}
template<typename ConsoleMutex>
@ -52,7 +52,7 @@ SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::log(const details::log_msg &msg
// before color range
print_range_(formatted, 0, msg.color_range_start);
// in color range
print_ccode_(colors_[static_cast<size_t>(msg.level)]);
print_ccode_(colors_.at(static_cast<size_t>(msg.level)));
print_range_(formatted, msg.color_range_start, msg.color_range_end);
print_ccode_(reset);
// after color range

@ -3,7 +3,6 @@
#pragma once
#if defined(_WIN32)
# include <spdlog/details/null_mutex.h>

@ -7,11 +7,16 @@
// Custom sink for QPlainTextEdit or QTextEdit and its childs(QTextBrowser...
// etc) Building and using requires Qt library.
//
// Warning: the qt_sink won't be notified if the target widget is destroyed.
// If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject,
// and then use a standard signal/slot.
//
#include "spdlog/common.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/details/synchronous_factory.h"
#include "spdlog/sinks/base_sink.h"
#include <array>
#include <QTextEdit>
#include <QPlainTextEdit>
@ -53,15 +58,185 @@ private:
std::string meta_method_;
};
// QT color sink to QTextEdit.
// Color location is determined by the sink log pattern like in the rest of spdlog sinks.
// Colors can be modified if needed using sink->set_color(level, qtTextCharFormat).
// max_lines is the maximum number of lines that the sink will hold before removing the oldest lines.
// Note: Only ascii (latin1) is supported by this sink.
template<typename Mutex>
class qt_color_sink : public base_sink<Mutex>
{
public:
qt_color_sink(QTextEdit *qt_text_edit, int max_lines)
: qt_text_edit_(qt_text_edit), max_lines_(max_lines)
{
if (!qt_text_edit_)
{
throw_spdlog_ex("qt_color_text_sink: text_edit is null");
}
default_color_ = qt_text_edit_->currentCharFormat();
// set colors
QTextCharFormat format;
// trace
format.setForeground(Qt::gray);
colors_.at(level::trace) = format;
// debug
format.setForeground(Qt::cyan);
colors_.at(level::debug) = format;
// info
format.setForeground(Qt::green);
colors_.at(level::info) = format;
// warn
format.setForeground(Qt::yellow);
colors_.at(level::warn) = format;
// err
format.setForeground(Qt::red);
colors_.at(level::err) = format;
// critical
format.setForeground(Qt::white);
format.setBackground(Qt::red);
colors_.at(level::critical) = format;
}
~qt_color_sink()
{
flush_();
}
void set_default_color(QTextCharFormat format)
{
// std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
default_color_ = format;
}
void set_level_color(level::level_enum color_level, QTextCharFormat format)
{
// std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
colors_.at(static_cast<size_t>(color_level)) = format;
}
QTextCharFormat &get_level_color(level::level_enum color_level)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
return colors_.at(static_cast<size_t>(color_level));
}
QTextCharFormat &get_default_color()
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
return default_color_;
}
protected:
struct invoke_params
{
invoke_params(int max_lines, QTextEdit *q_text_edit, QString payload, QTextCharFormat default_color,
QTextCharFormat level_color, int color_range_start, int color_range_end)
: max_lines(max_lines),
q_text_edit(q_text_edit),
payload(std::move(payload)),
default_color(default_color),
level_color(level_color),
color_range_start(color_range_start),
color_range_end(color_range_end)
{
}
int max_lines;
QTextEdit *q_text_edit;
QString payload;
QTextCharFormat default_color;
QTextCharFormat level_color;
int color_range_start;
int color_range_end;
};
void sink_it_(const details::log_msg &msg) override
{
using this_type = qt_color_sink<Mutex>;
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
const string_view_t str = string_view_t(formatted.data(), formatted.size());
// apply the color to the color range in the formatted message.
auto payload = QString::fromLatin1(str.data(), static_cast<int>(str.size()));
invoke_params params {
max_lines_, // max lines
qt_text_edit_, // text edit to append to
std::move(payload), // text to append
default_color_, // default color
colors_.at(msg.level), // color to apply
msg.color_range_start, // color range start
msg.color_range_end}; // color range end
QMetaObject::invokeMethod(
qt_text_edit_,
[params]() {invoke_method_(params);},
Qt::AutoConnection);
}
void flush_() override {}
// Add colored text to the text edit widget. This method is invoked in the GUI thread.
// It is a static method to ensure that it is handled correctly even if the sink is destroyed prematurely
// before it is invoked.
static void invoke_method_(invoke_params params)
{
auto *document = params.q_text_edit->document();
QTextCursor cursor(document);
// remove first blocks if number of blocks exceeds max_lines
while(document->blockCount() > params.max_lines)
{
cursor.select(QTextCursor::BlockUnderCursor);
cursor.removeSelectedText();
cursor.deleteChar(); // delete the newline after the block
}
cursor.movePosition(QTextCursor::End);
cursor.setCharFormat(params.default_color);
// if color range not specified or not not valid, just append the text with default color
if(params.color_range_end <= params.color_range_start)
{
cursor.insertText(params.payload);
return;
}
// insert the text before the color range
cursor.insertText(params.payload.left(params.color_range_start));
// insert the colorized text
cursor.setCharFormat(params.level_color);
cursor.insertText(params.payload.mid(params.color_range_start, params.color_range_end - params.color_range_start));
// insert the text after the color range with default format
cursor.setCharFormat(params.default_color);
cursor.insertText(params.payload.mid(params.color_range_end));
}
QTextEdit *qt_text_edit_;
int max_lines_;
QTextCharFormat default_color_;
std::array<QTextCharFormat, level::n_levels> colors_;
};
#include "spdlog/details/null_mutex.h"
#include <mutex>
using qt_sink_mt = qt_sink<std::mutex>;
using qt_sink_st = qt_sink<spdlog::details::null_mutex>;
using qt_sink_st = qt_sink<details::null_mutex>;
using qt_color_sink_mt = qt_color_sink<std::mutex>;
using qt_color_sink_st = qt_color_sink<details::null_mutex>;
} // namespace sinks
//
// Factory functions
//
// log to QTextEdit
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append")
{
@ -74,6 +249,7 @@ inline std::shared_ptr<logger> qt_logger_st(const std::string &logger_name, QTex
return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
}
// log to QPlainTextEdit
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> qt_logger_mt(
const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText")
@ -87,7 +263,7 @@ inline std::shared_ptr<logger> qt_logger_st(
{
return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
}
// log to QObject
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method)
{
@ -99,4 +275,18 @@ inline std::shared_ptr<logger> qt_logger_st(const std::string &logger_name, QObj
{
return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
}
// log to QTextEdit with colorize output
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> qt_color_logger_mt(const std::string &logger_name, QTextEdit *qt_text_edit, int max_lines)
{
return Factory::template create<sinks::qt_color_sink_mt >(logger_name, qt_text_edit, max_lines);
}
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> qt_color_logger_st(const std::string &logger_name, QTextEdit *qt_text_edit, int max_lines)
{
return Factory::template create<sinks::qt_color_sink_st >(logger_name, qt_text_edit, max_lines);
}
} // namespace spdlog

@ -241,12 +241,12 @@ protected:
details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), buf);
LPCWSTR lp_wstr = buf.data();
succeeded = static_cast<bool>(::ReportEventW(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
current_user_sid_.as_sid(), 1, 0, &lp_wstr, nullptr));
succeeded = static_cast<bool>(::ReportEventW(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg),
event_id_, current_user_sid_.as_sid(), 1, 0, &lp_wstr, nullptr));
#else
LPCSTR lp_str = formatted.data();
succeeded = static_cast<bool>(::ReportEventA(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr));
succeeded = static_cast<bool>(::ReportEventA(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg),
event_id_, current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr));
#endif
if (!succeeded)

@ -19,11 +19,7 @@ if (Catch2_FOUND)
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_Declare(Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_TAG v3.3.2)
FetchContent_MakeAvailable(Catch2)
endif()

@ -21,7 +21,8 @@ protected:
throw std::runtime_error("some error happened during flush");
}
};
struct custom_ex {};
struct custom_ex
{};
#if !defined(SPDLOG_USE_STD_FORMAT) // std formt doesn't fully support tuntime strings
TEST_CASE("default_error_handler", "[errors]")
@ -39,7 +40,6 @@ TEST_CASE("default_error_handler", "[errors]")
REQUIRE(count_lines(SIMPLE_LOG) == 1);
}
TEST_CASE("custom_error_handler", "[errors]")
{
prepare_logdir();

Loading…
Cancel
Save