GithubHelp home page GithubHelp logo

odygrd / quill Goto Github PK

View Code? Open in Web Editor NEW
1.2K 27.0 133.0 6.45 MB

Asynchronous Low Latency C++ Logging Library

License: MIT License

CMake 2.12% C++ 97.61% Python 0.20% C 0.01% Starlark 0.04% Meson 0.03%
cpp low-latency logging-library logging cpp14 asynchronous async logger cpp17 log-library cpp20 cross-platform fmtlib high-performance

quill's Introduction


logo

Quill

Asynchronous Low Latency C++ Logging Library

🧭 Table of Contents

✨ Introduction

Quill is a high-performance asynchronous logging library. It is particularly suited for performance-critical applications where every microsecond counts.

  • Performance-Focused: Quill consistently outperforms many popular logging libraries.
  • Feature-Rich: Packed with advanced features to meet diverse logging needs.
  • Battle-Tested: Proven in demanding production environments.
  • Extensive Documentation: Comprehensive guides and examples available.
  • Community-Driven: Open to contributions, feedback, and feature requests.

⏩ Quick Start

Getting started is easy and straightforward. Follow these steps to integrate the library into your project:

Installation

You can install Quill using the package manager of your choice:

Package Manager Installation Command
vcpkg vcpkg install quill
Conan conan install quill
Homebrew brew install quill
Meson WrapDB meson wrap install quill
Conda conda install -c conda-forge quill
Bzlmod bazel_dep(name = "quill", version = "x.y.z")
xmake xrepo install quill
nix nix-shell -p quill-log

Setup

Once installed, you can start using Quill with the following code:

#include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogMacros.h"
#include "quill/Logger.h"
#include "quill/sinks/ConsoleSink.h"
#include <string_view>

int main()
{
  quill::Backend::start();

  quill::Logger* logger = quill::Frontend::create_or_get_logger(
    "root", quill::Frontend::create_or_get_sink<quill::ConsoleSink>("sink_id_1"));

  LOG_INFO(logger, "Hello from {}!", std::string_view{"Quill"});
}

🎯 Features

  • High-Performance: Ultra-low latency performance. View Benchmarks
  • Asynchronous Processing: Background thread handles formatting and I/O, keeping your main thread responsive.
  • Minimal Header Includes:
    • Frontend: Only Logger.h and LogMacros.h needed for logging. Lightweight with minimal dependencies.
    • Backend: Single .cpp file inclusion. No backend code injection into other translation units.
  • Compile-Time Optimization: Eliminate specific log levels at compile time.
  • Custom Formatters: Define your own log output patterns. See Formatters.
  • Timestamp-Ordered Logs: Simplify debugging of multithreaded applications with chronologically ordered logs.
  • Flexible Timestamps: Support for rdtsc, chrono, or custom clocks - ideal for simulations and more.
  • Backtrace Logging: Store messages in a ring buffer for on-demand display. See Backtrace Logging
  • Multiple Output Sinks: Console (with color), files (with rotation), JSON, ability to create custom sinks and more.
  • Log Filtering: Process only relevant messages. See Filters.
  • JSON Logging: Structured log output. See JSON Logging
  • Configurable Queue Modes: bounded/unbounded and blocking/dropping options with monitoring on dropped messages, queue reallocations, and blocked hot threads.
  • Crash Handling: Built-in signal handler for log preservation during crashes.
  • Huge Pages Support (Linux): Leverage huge pages on the hot path for optimized performance.
  • Wide Character Support (Windows): Compatible with ASCII-encoded wide strings and STL containers consisting of wide strings.
  • Exception-Free Option: Configurable builds with or without exception handling.
  • Clean Codebase: Maintained to high standards, warning-free even at strict levels.
  • Type-Safe API: Built on {fmt} library.

🚀 Performance

System Configuration

  • OS: Linux RHEL 9.4

  • CPU: Intel Core i5-12600 (12th Gen) @ 4.8 GHz

  • Compiler: GCC 13.1

  • Benchmark-Tuned System: The system is specifically tuned for benchmarking.

  • Command Line Parameters:

    $ cat /proc/cmdline
    BOOT_IMAGE=(hd0,gpt2)/vmlinuz-5.14.0-427.13.1.el9_4.x86_64 root=/dev/mapper/rhel-root ro crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M resume=/dev/mapper/rhel-swap rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet nohz=on nohz_full=1-5 rcu_nocbs=1-5 isolcpus=1-5 mitigations=off transparent_hugepage=never intel_pstate=disable nosoftlockup irqaffinity=0 processor.max_cstate=1 nosoftirqd sched_tick_offload=0 spec_store_bypass_disable=off spectre_v2=off iommu=pt

Latency

The results presented in the tables below are measured in nanoseconds (ns).

The tables are sorted by the 95th percentile

Logging Numbers

LOG_INFO(logger, "Logging int: {}, int: {}, double: {}", i, j, d).

1 Thread Logging
Library 50th 75th 90th 95th 99th 99.9th
Quill Bounded Dropping Queue 7 8 8 9 9 11
fmtlog 9 9 10 10 12 13
Quill Unbounded Queue 10 10 10 10 12 14
PlatformLab NanoLog 13 14 16 17 19 25
MS BinLog 21 21 22 22 56 93
XTR 7 7 29 30 33 53
Reckless 26 28 31 32 35 49
Iyengar NanoLog 83 96 117 125 152 197
spdlog 143 147 152 158 165 177
g3log 1161 1259 1329 1419 1602 1827
4 Threads Logging Simultaneously
Library 50th 75th 90th 95th 99th 99.9th
fmtlog 8 9 9 10 11 13
Quill Bounded Dropping Queue 8 9 10 10 12 14
XTR 7 8 9 11 31 38
Quill Unbounded Queue 10 11 11 12 13 15
PlatformLab NanoLog 15 17 20 23 27 32
Reckless 19 23 26 28 34 55
MS BinLog 21 22 22 23 62 100
Iyengar NanoLog 58 90 123 131 168 242
spdlog 210 243 288 313 382 694
g3log 1271 1337 1396 1437 1614 1899

Logging Large Strings

Logging std::string over 35 characters to prevent the short string optimization.

LOG_INFO(logger, "Logging int: {}, int: {}, string: {}", i, j, large_string).

1 Thread Logging
Library 50th 75th 90th 95th 99th 99.9th
Quill Bounded Dropping Queue 11 13 13 14 15 16
fmtlog 11 12 13 14 15 17
Quill Unbounded Queue 14 15 16 17 18 19
MS BinLog 22 23 24 25 61 100
PlatformLab NanoLog 15 17 21 27 33 39
XTR 8 9 29 31 35 54
Reckless 91 107 115 118 124 135
Iyengar NanoLog 86 97 119 128 159 268
spdlog 120 124 128 132 141 151
g3log 881 956 1018 1089 1264 1494
4 Threads Logging Simultaneously
Library 50th 75th 90th 95th 99th 99.9th
XTR 9 11 13 14 32 40
fmtlog 11 12 13 14 16 19
Quill Bounded Dropping Queue 13 14 15 16 17 19
Quill Unbounded Queue 15 16 17 18 19 21
MS BinLog 23 25 27 28 65 105
PlatformLab NanoLog 16 20 32 38 44 51
Reckless 79 94 104 107 114 132
Iyengar NanoLog 85 93 125 133 168 237
spdlog 178 218 261 281 381 651
g3log 992 1055 1121 1178 1360 1600

Logging Complex Types

Logging std::vector<std::string> containing 16 large strings, each ranging from 50 to 60 characters.

LOG_INFO(logger, "Logging int: {}, int: {}, vector: {}", i, j, v).

1 Thread Logging
Library 50th 75th 90th 95th 99th 99.9th
Quill Bounded Dropping Queue 48 50 53 55 58 62
Quill Unbounded Queue 54 56 57 58 61 66
MS BinLog 68 69 72 74 79 281
XTR 284 294 340 346 356 575
fmtlog 711 730 754 770 804 834
spdlog 6191 6261 6330 6386 6633 7320
4 Threads Logging Simultaneously
Library 50th 75th 90th 95th 99th 99.9th
Quill Bounded Dropping Queue 50 52 54 56 60 82
MS BinLog 70 72 75 79 88 286
Quill Unbounded Queue 97 107 116 122 135 148
XTR 512 711 761 791 865 945
fmtlog 780 804 823 835 860 896
spdlog 6469 6549 6641 6735 7631 9430

The benchmark methodology involves logging 20 messages in a loop, calculating and storing the average latency for those 20 messages, then waiting around ~2 milliseconds, and repeating this process for a specified number of iterations.

In the Quill Bounded Dropping benchmarks, the dropping queue size is set to 262,144 bytes, which is double the default size of 131,072 bytes.

You can find the benchmark code on the logger_benchmarks repository.

Throughput

The maximum throughput is measured by determining the maximum number of log messages the backend logging thread can write to the log file per second.

When measured on the same system as the latency benchmarks mentioned above the average throughput of the backend logging thread when formatting a log message consisting of an int and a double is ~4.50 million msgs/sec

While the primary focus of the library is not on throughput, it does provide efficient handling of log messages across multiple threads. The backend logging thread, responsible for formatting and ordering log messages from the frontend threads, ensures that all queues are emptied on a high priority basis. The backend thread internally buffers the log messages and then writes them later when the caller thread queues are empty or when a predefined limit, BackendOptions::transit_events_soft_limit, is reached. This approach prevents the need for allocating new queues or dropping messages on the hot path.

Comparing throughput with other logging libraries in an asynchronous logging scenario has proven challenging. Some libraries may drop log messages, resulting in smaller log files than expected, while others only offer asynchronous flush, making it difficult to determine when the logging thread has finished processing all messages. In contrast, Quill provides a blocking flush log guarantee, ensuring that every log message from the frontend threads up to that point is flushed to the file.

For benchmarking purposes, you can find the code here.

Compilation Time

Compile times are measured using clang 15 and for Release build.

Below, you can find the additional headers that the library will include when you need to log, following the recommended_usage example

quill_v5_1_compiler_profile.speedscope.png

There is also a compile-time benchmark measuring the compilation time of 2000 auto-generated log statements with various arguments. You can find it here. It takes approximately 30 seconds to compile.

quill_v5_1_compiler_bench.speedscope.png

🧩 Usage

#include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogMacros.h"
#include "quill/Logger.h"
#include "quill/sinks/ConsoleSink.h"
#include "quill/std/Array.h"

#include <string>
#include <utility>

int main()
{
  // Backend  
  quill::BackendOptions backend_options;
  quill::Backend::start(backend_options);

  // Frontend
  auto console_sink = quill::Frontend::create_or_get_sink<quill::ConsoleSink>("sink_id_1");
  quill::Logger* logger = quill::Frontend::create_or_get_logger("root", std::move(console_sink));

  // Change the LogLevel to print everything
  logger->set_log_level(quill::LogLevel::TraceL3);

  // A log message with number 123
  int a = 123;
  std::string l = "log";
  LOG_INFO(logger, "A {} message with number {}", l, a);

  // libfmt formatting language is supported 3.14e+00
  double pi = 3.141592653589793;
  LOG_INFO(logger, "libfmt formatting language is supported {:.2e}", pi);

  // Logging STD types is supported [1, 2, 3]
  std::array<int, 3> arr = {1, 2, 3};
  LOG_INFO(logger, "Logging STD types is supported {}", arr);

  // Logging STD types is supported [arr: [1, 2, 3]]
  LOGV_INFO(logger, "Logging STD types is supported", arr);

  // A message with two variables [a: 123, b: 3.17]
  double b = 3.17;
  LOGV_INFO(logger, "A message with two variables", a, b);

  for (uint32_t i = 0; i < 10; ++i)
  {
    // Will only log the message once per second
    LOG_INFO_LIMIT(std::chrono::seconds{1}, logger, "A {} message with number {}", l, a);
    LOGV_INFO_LIMIT(std::chrono::seconds{1}, logger, "A message with two variables", a, b);
  }

  LOG_TRACE_L3(logger, "Support for floats {:03.2f}", 1.23456);
  LOG_TRACE_L2(logger, "Positional arguments are {1} {0} ", "too", "supported");
  LOG_TRACE_L1(logger, "{:>30}", "right aligned");
  LOG_DEBUG(logger, "Debugging foo {}", 1234);
  LOG_INFO(logger, "Welcome to Quill!");
  LOG_WARNING(logger, "A warning message.");
  LOG_ERROR(logger, "An error message. error code {}", 123);
  LOG_CRITICAL(logger, "A critical error.");
}

Output

example_output.png

External CMake

Building and Installing Quill

To get started with Quill, clone the repository and install it using CMake:

git clone http://github.com/odygrd/quill.git
mkdir cmake_build
cd cmake_build
cmake ..
make install
  • Custom Installation: Specify a custom directory with -DCMAKE_INSTALL_PREFIX=/path/to/install/dir.
  • Build Examples: Include examples with -DQUILL_BUILD_EXAMPLES=ON.

Next, add Quill to your project using find_package():

find_package(quill REQUIRED)
target_link_libraries(your_target PUBLIC quill::quill)

Sample Directory Structure

Organize your project directory like this:

my_project/
├── CMakeLists.txt
├── main.cpp

Sample CMakeLists.txt

Here’s a sample CMakeLists.txt to get you started:

# If Quill is in a non-standard directory, specify its path.
set(CMAKE_PREFIX_PATH /path/to/quill)

# Find and link the Quill library.
find_package(quill REQUIRED)
add_executable(example main.cpp)
target_link_libraries(example PUBLIC quill::quill)

Embedded CMake

For a more integrated approach, embed Quill directly into your project:

Sample Directory Structure

my_project/
├── quill/            # Quill repo folder
├── CMakeLists.txt
├── main.cpp

Sample CMakeLists.txt

Use this CMakeLists.txt to include Quill directly:

cmake_minimum_required(VERSION 3.1.0)
project(my_project)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_subdirectory(quill)
add_executable(my_project main.cpp)
target_link_libraries(my_project PUBLIC quill::quill)

Android NDK

Building Quill for Android? Add this flag during configuration:

-DQUILL_NO_THREAD_NAME_SUPPORT:BOOL=ON

Meson

Using WrapDB

Easily integrate Quill with Meson’s wrapdb:

meson wrap install quill

Manual Integration

Copy the repository contents to your subprojects directory and add the following to your meson.build:

quill = subproject('quill')
quill_dep = quill.get_variable('quill_dep')
my_build_target = executable('name', 'main.cpp', dependencies : [quill_dep], install : true)

Bazel

Using Blzmod

Quill is available on BLZMOD for easy integration.

Manual Integration

For manual setup, add Quill to your BUILD.bazel file like this:

cc_binary(name = "app", srcs = ["main.cpp"], deps = ["//quill_path:quill"])

📐 Design

Frontend (caller-thread)

When invoking a LOG_ macro:

  1. Creates a static constexpr metadata object to store Metadata such as the format string and source location.

  2. Pushes the data SPSC lock-free queue. For each log message, the following variables are pushed

Variable Description
timestamp Current timestamp
Metadata* Pointer to metadata information
Logger* Pointer to the logger instance
DecodeFunc A pointer to a templated function containing all the log message argument types, used for decoding the message
Args... A serialized binary copy of each log message argument that was passed to the LOG_ macro

Backend

Consumes each message from the SPSC queue, retrieves all the necessary information and then formats the message. Subsequently, forwards the log message to all Sinks associated with the Logger.

design.jpg

🚨 Caveats

Quill may not work well with fork() since it spawns a background thread and fork() doesn't work well with multithreading.

If your application uses fork() and you want to log in the child processes as well, you should call quill::start() after the fork() call. Additionally, you should ensure that you write to different files in the parent and child processes to avoid conflicts.

For example :

#include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogMacros.h"
#include "quill/Logger.h"
#include "quill/sinks/FileSink.h"

int main()
{
  // DO NOT CALL THIS BEFORE FORK
  // quill::Backend::start();

  if (fork() == 0)
  {
    quill::Backend::start();
        
    // Get or create a handler to the file - Write to a different file
    auto file_sink = quill::Frontend::create_or_get_sink<quill::FileSink>(
      "child.log");
    
    quill::Logger* logger = quill::Frontend::create_or_get_logger("root", std::move(file_sink));

    QUILL_LOG_INFO(logger, "Hello from Child {}", 123);
  }
  else
  {
    quill::Backend::start();
          
    // Get or create a handler to the file - Write to a different file
    auto file_sink = quill::Frontend::create_or_get_sink<quill::FileSink>(
      "parent.log");
    
    quill::Logger* logger = quill::Frontend::create_or_get_logger("root", std::move(file_sink));
    
    QUILL_LOG_INFO(logger, "Hello from Parent {}", 123);
  }
}

📝 License

Quill is licensed under the MIT License

Quill depends on third party libraries with separate copyright notices and license terms. Your use of the source code for these subcomponents is subject to the terms and conditions of the following licenses.

quill's People

Contributors

anluoma avatar azur-ia avatar bl-ue avatar borisovss avatar brettdong avatar byrdman32 avatar dpocock avatar e-kwsm avatar egelja avatar freeacetone avatar hailios avatar islc avatar ivafanas avatar jf-0 avatar kerjoe avatar lucpelletier avatar naums avatar neomantra avatar odygrd avatar rt2code avatar schoppenglas avatar stephanie-eng avatar tetsuh avatar tillalex avatar tocic avatar usefulcat avatar weissle avatar zhuoqiang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

quill's Issues

Single logger with multiple handlers

Is it possible to have a single logger with multiple handlers? For example, I would like to have a logger with two handlers: one to stdout (with colors), and the other to a file (without colors).

calculation should ignored when should_log() = false

e.g.

std::string very_cost_calc() {
    // ...
    return "costly result";
}

LOG_TRACE("{}", very_cost_calc());

When we set log level in info, then the very_cost_calc() shouldn't be called.

The problem is check_format(FMT_STRING(fmt), ##__VA_ARGS__); in QUILL_LOGGER_CALL. We need to remove or fix the implement.

to support compiling dynamic library

Hi Quill team,

We are currently using your quill logger for our C++ projects which works brilliantly. Thanks.

Now we would like to utilize quill logger for JAVA applications as well by loading it as a dynamic library. In this way, Quill need to dynamically depend on fmt lib as well instead of being bundled together, as we are also using fmt-cpp's dynamic library.

Is that possible you can help to add the support of generating dynamic library as well, together with existing support of static library for quill?

Thanks in advance,
Xiong

std::bad_alloc exception on Mingw-w64 - Invalid timestamp formatting due to strftime failure on MinGw

My application crashes when compiled with Mingw-w64 (GCC 9.3) on Windows 10 when run using the recent commit 6248396.
An unexpected std::bad_alloc exception is thrown.
I am able to re-create the issue by running the example_custom_formatter example target.
I tried to run the tests on Mingw-w64 but there were compilation issues with the use of std::ifstream and std::wstring in the quill::testing::file_contents function.

QUILL_USE_BOUNDED_QUEUE cause assertion error : "Object should always be cache aligned"

Thank you for fast development. I love using this library.
When I try to use QUILL_USE_BOUNDED_QUEUE in In quill 1.3.1,
I met the assertion error message:

.../include/quill/detail/BoundedSPSCQueue.h:362: quill::detail::BoundedSPSCQueue<TBaseObject, Capacity>::Handle quill::detail::BoundedSPSCQueue<TBaseObject, Capacity>::try_pop() [with TBaseObject = quill::detail::RecordBase; long unsigned int Capacity = 262144]: Assertion `(reinterpret_cast<uintptr_t>(buffer_pos) % CACHELINE_SIZE == 0) && "Object should always be cache aligned"' failed.

Please tell me workaround.

my environment : Fedora 32, gcc 10.1, glibc 2.31
quill version: 1.3.1

Support safe and unsafe logging modes as a workaround to formatting classes with mutable objects gotchas

Currently Quill copies all user defined types and then formats them on a backend thread. This leads problems when user defined types include a mutable reference or pointer to another object.

I am thinking to have two modes available. It should be able to switch between them with a preprocessor definition.

Safe Logging

If this mode is used without providing a tag to all the safe objects it will degrade performance.
However, if used correctly it can achieve the same performance as unsafe logging in a safer way.

  • This mode will copy all safe to copy types that accepted by printf to the spsc queue and format them later on the backend logger thread.

  • For any user defined types check if copy_loggable_t tag is defined and copy the object without formatting, otherwise if the tag is not defined fallback to calling operator<< on the caller thread.

  • For all related stl objects and containers check the underlying type against 1) and 2) and decide whether to copy or format the container on the caller thread.

example :

class foo
{
    public:

      // Enables copying the object instead of formatting it in the caller thread. 
      // This object is safe to copy. But we are in SAFE_LOGGING and it would 
      // be formatted on the caller thread without this tag.

      using copy_loggable_t = std::true_type; 

      foo(int m) : mo(m) {};
      friend ostream& operator<<(ostream& os, const foo& f)
      {
          os << f.mo;
          return os;
      }

    int mo;
};

.

class foo
{
    public:

      // With SAFE_LOGGING this class will always be formatted by default 
      // on the caller thread and a string will be copied to the queue

      foo(int m) : mo(m) {};
      friend ostream& operator<<(ostream& os, const foo& f)
      {
          os << f.mo;
          return os;
      }

    int mo;
};

Unsafe Logging (Current Mode)

This mode will always attempt to copy all objects and format them in a backend logger thread.
The user has to be extra careful about objects that contain a mutable reference or a pointer as those members can change at any time :

  • after LOG_() was called but before formatting happened
  • during formatting

Cannot compile example_trivial.cpp in Linux

I try to compile example_trivial.cpp in Linux using clang++-9.0.1 and get below compilation errors. Do you have any suggestion?

Thanks,
Hung

[ 50%] Building CXX object CMakeFiles/test_quill.dir/test_quill.cpp.o                                                                                                                                                                 
In file included from /home/hungptit/working/cppidioms/src/test_quill.cpp:1:                                                                                                                                                          
In file included from /home/hungptit/working/cppidioms/src/../3p/include/quill/Quill.h:8:                                                                                                                                             
/home/hungptit/working/cppidioms/src/../3p/include/quill/detail/LogMacros.h:39:62: error: token pasting of ',' and                                                                                                                    
      __VA_ARGS__ is a GNU extension [-Werror,-Wgnu-zero-variadic-macro-arguments]                                                                                                                                                    
    QUILL_LOGGER_CALL(logger, quill::LogLevel::TraceL3, fmt, ##__VA_ARGS__)                                                                                                                                                           
                                                             ^                                                                                                                                                                        
/home/hungptit/working/cppidioms/src/../3p/include/quill/detail/LogMacros.h:33:54: error: token pasting of ',' and                                                                                                                    
      __VA_ARGS__ is a GNU extension [-Werror,-Wgnu-zero-variadic-macro-arguments]                                                                                                                                                    
    logger->log<log_statement_level>(&log_line_info, ##__VA_ARGS__)...                                                                                                                                                                
                                                     ^                                                                                                                                                                                
/home/hungptit/working/cppidioms/src/../3p/include/quill/detail/LogMacros.h:46:62: error: token pasting of ',' and                                                                                                                    
      __VA_ARGS__ is a GNU extension [-Werror,-Wgnu-zero-variadic-macro-arguments]                                                                                                                                                    
    QUILL_LOGGER_CALL(logger, quill::LogLevel::TraceL2, fmt, ##__VA_ARGS__)                                                                                                                                                           
                                                             ^                                                                                                                                                                        
/home/hungptit/working/cppidioms/src/../3p/include/quill/detail/LogMacros.h:33:54: error: token pasting of ',' and                                                                                                                    
      __VA_ARGS__ is a GNU extension [-Werror,-Wgnu-zero-variadic-macro-arguments]                                                                                                                                                    
    logger->log<log_statement_level>(&log_line_info, ##__VA_ARGS__)...                                                              

And this is the compilation command

{                                                                                                                                                                                                                                     
  "directory": "/home/hungptit/working/cppidioms/src",                                                                                                                                                                                
  "command": "clang++   -I/home/hungptit/working/cppidioms/src/../3p/include -I/home/hungptit/working/cppidioms/src/../src    -O3  -march=native  -std=c++17  -Wall  -pedantic  -pedantic-errors  -fstrict-aliasing  -Wstrict-aliasing  -DFMT_HEADER_ONLY  -DCEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS  -flto  -static -O3 -DNDEBUG   -o CMakeFiles/test_quill.dir/test_quill.cpp.o -c /home/hungptit/working/cppidioms/src/test_quill.cpp",                                  
  "file": "/home/hungptit/working/cppidioms/src/test_quill.cpp"                                                                                                                                                                       
},

Add CMAKE_INSTALL_PREFIX in README for installation directory?

This is to save some users(who didn't know cmake well enough) some google time for this CMake feature. My first google solution is 'make DESTDIR=/home/john install', then /usr/local is still created under /home/john. Not a big deal, I manually copied them from /home/john/usr/local.

My current google solution is 'cmake -DCMAKE_INSTALL_PREFIX=/home/john/quill-1.0/'. I didn't try the command as my installation was done. It will be nice to add this to README if it works.

Please add prefix QUILL_ to log macros like LOG_INFO

This is causing macro conflicts for me, as 1 of the libraries I use has the same log macro. It will be better to always have a prefix for macros like LOG_INFO, which is too easy to have a naming conflict without prefix.

Thanks a lot.

Compile Time Checking of Log Messages

The {fmt} library provides the FMT_STRING macro to validate formatting constructs at compile-time. Is it possible to check the log message format at compile-time in Quill?

Run benchmark failed

BENCHMARK_quill_hot_path_rdtsc_clock

terminate called after throwing an instance of 'quill::QuillError'
what(): failed to call set_cpu_affinity, with error message "Invalid argument", errno "22"
Aborted (core dumped)

Pattern Format Segfault

Using the set_pattern function handler appears to cause a runtime crash.
A stack trace of the segmentation fault is below.
This occurs on GCC 9.1.1 on CentOS 7.
I'm using the recent commit eb56fe8, but it occurs on v1.2.0 as well.
The issue does not occur using Clang 9.0.1 on macOS 10.15 Catalina.
My program crashes on launch.
Perhaps it has to do with the QUILL_STRING macro?

#0  0x0000000000498114 in __gnu_cxx::__exchange_and_add (__mem=0x200543c, __val=-1) at /opt/rh/devtoolset-9/root/usr/include/c++/9/ext/atomicity.h:49
#1  0x00000000004981ab in __gnu_cxx::__exchange_and_add_dispatch (__mem=0x200543c, __val=-1) at /opt/rh/devtoolset-9/root/usr/include/c++/9/ext/atomicity.h:82
#2  0x00000000004a5340 in std::string::_Rep::_M_dispose (this=0x200542c, __a=...) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/basic_string.h:3311
#3  0x000000000049e39a in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string (this=0x2122458, __in_chrg=<optimized out>) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/basic_string.h:3706
#4  0x000000000049bd6e in quill::detail::TimestampFormatter::~TimestampFormatter (this=0x2122418, __in_chrg=<optimized out>) at _deps/quill-src/quill/include/quill/detail/TimestampFormatter.h:29
#5  0x00000000004a8a34 in quill::PatternFormatter::~PatternFormatter (this=0x21221f0, __in_chrg=<optimized out>) at _deps/quill-src/quill/include/quill/PatternFormatter.h:188
#6  0x00000000004a8a86 in std::default_delete<quill::PatternFormatter>::operator() (this=0x2121608, __ptr=0x21221f0) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/unique_ptr.h:81
#7  0x00000000004a166e in std::unique_ptr<quill::PatternFormatter, std::default_delete<quill::PatternFormatter> >::~unique_ptr (this=0x2121608 = {...}, __in_chrg=<optimized out>) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/unique_ptr.h:289
#8  0x00000000004f84aa in quill::Handler::~Handler (this=0x2121600, __in_chrg=<optimized out>) at _deps/quill-src/quill/include/quill/handlers/Handler.h:29
#9  0x00000000004fd416 in quill::StreamHandler::~StreamHandler (this=0x2121600, __in_chrg=<optimized out>) at _deps/quill-src/quill/include/quill/handlers/StreamHandler.h:26
#10 0x00000000004fd432 in quill::StreamHandler::~StreamHandler (this=0x2121600, __in_chrg=<optimized out>) at _deps/quill-src/quill/include/quill/handlers/StreamHandler.h:26
#11 0x000000000050553a in std::default_delete<quill::StreamHandler>::operator() (this=0x2121670, __ptr=0x2121600) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/unique_ptr.h:81
#12 0x000000000050345c in std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> >::~unique_ptr (this=0x2121670 = {...}, __in_chrg=<optimized out>) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/unique_ptr.h:289
#13 0x000000000050b1dc in std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > >::~pair (this=0x2121668, __in_chrg=<optimized out>) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/stl_pair.h:208
#14 0x000000000050b208 in __gnu_cxx::new_allocator<std::__detail::_Hash_node<std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > >, true> >::destroy<std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > > > (this=0x5794b8 <quill::detail::LogManagerSingleton::instance()::instance+56>, __p=0x2121668) at /opt/rh/devtoolset-9/root/usr/include/c++/9/ext/new_allocator.h:153
#15 0x0000000000509f6f in std::allocator_traits<std::allocator<std::__detail::_Hash_node<std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > >, true> > >::destroy<std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > > > (__a=..., __p=0x2121668) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/alloc_traits.h:497
#16 0x00000000005085f9 in std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > >, true> > >::_M_deallocate_node (this=0x5794b8 <quill::detail::LogManagerSingleton::instance()::instance+56>, __n=0x2121660) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/hashtable_policy.h:2102
#17 0x0000000000507c24 in std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > >, true> > >::_M_deallocate_nodes (this=0x5794b8 <quill::detail::LogManagerSingleton::instance()::instance+56>, __n=0x0) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/hashtable_policy.h:2124
#18 0x0000000000505e06 in std::_Hashtable<std::string, std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > >, std::allocator<std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > > >, std::__detail::_Select1st, std::equal_to<std::string>, std::hash<std::string>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::clear (this=0x5794b8 <quill::detail::LogManagerSingleton::instance()::instance+56>) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/hashtable.h:2028
#19 0x0000000000503d98 in std::_Hashtable<std::string, std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > >, std::allocator<std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > > >, std::__detail::_Select1st, std::equal_to<std::string>, std::hash<std::string>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::~_Hashtable (this=0x5794b8 <quill::detail::LogManagerSingleton::instance()::instance+56>, __in_chrg=<optimized out>) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/hashtable.h:1352
#20 0x0000000000502324 in std::unordered_map<std::string, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> >, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, std::unique_ptr<quill::StreamHandler, std::default_delete<quill::StreamHandler> > > > >::~unordered_map (this=0x5794b8 <quill::detail::LogManagerSingleton::instance()::instance+56> = {...}, __in_chrg=<optimized out>) at /opt/rh/devtoolset-9/root/usr/include/c++/9/bits/unordered_map.h:102
#21 0x0000000000502344 in quill::detail::HandlerCollection::~HandlerCollection (this=0x5794a0 <quill::detail::LogManagerSingleton::instance()::instance+32>, __in_chrg=<optimized out>) at _deps/quill-src/quill/include/quill/detail/HandlerCollection.h:24
#22 0x00000000005023b6 in quill::detail::LogManager::~LogManager (this=0x579480 <quill::detail::LogManagerSingleton::instance()::instance>, __in_chrg=<optimized out>) at _deps/quill-src/quill/include/quill/detail/LogManager.h:27
#23 0x00000000004ff350 in quill::detail::LogManagerSingleton::~LogManagerSingleton (this=0x579480 <quill::detail::LogManagerSingleton::instance()::instance>, __in_chrg=<optimized out>) at _deps/quill-src/quill/src/detail/LogManagerSingleton.cpp:9
#24 0x00007faa19fd2ce9 in __run_exit_handlers () from /lib64/libc.so.6
#25 0x00007faa19fd2d37 in exit () from /lib64/libc.so.6
#26 0x00007faa19fbb55c in __libc_start_main () from /lib64/libc.so.6
#27 0x00000000004761c9 in _start ()

quill flush policy: call flush() every X seconds

I am very new to quill. I am not sure if this is an issue for quill.

If an application prints just 1 log message ("program starts"), will quill use '\n' or std::endl here?
Will the log be buffered for a long time if the application doesn't log any more messages?
Sometimes it is not obvious to see whether a program doesn't hit that line of code or the log message is buffered, which causes some debugging anxiety.

Probably it will be better if quill backend thread can have an option to flush every X seconds, this can minimize lost messages during an unexpected crash. quill backend will do some unnecessary work here, but the main app thread can avoid paying the cost of calling quill::flush(). So main app thread can be fast, without worrying too much about logging messages being buffered.

Shadowed field in TransitLogRecord constructor

As of Quill v1.3.1, Clang 10.0.0 generates warnings for two shadowed fields in the TransitLogRecord constructor.
I've pasted the output below.

In file included from _deps/quill-src/quill/include/quill/Quill.h:10:
_deps/quill-src/quill/include/quill/detail/BackendWorker.h:145:37: error: constructor parameter 'thread_context' shadows the field 'thread_context' of 'TransitLogRecord' [-Werror,-Wshadow-field-in-constructor]
    TransitLogRecord(ThreadContext* thread_context, std::unique_ptr<RecordBase> base_record)
                                    ^
_deps/quill-src/quill/include/quill/detail/BackendWorker.h:155:20: note: previous declaration is here
    ThreadContext* thread_context;
                   ^
_deps/quill-src/quill/include/quill/detail/BackendWorker.h:145:81: error: constructor parameter 'base_record' shadows the field 'base_record' of 'TransitLogRecord' [-Werror,-Wshadow-field-in-constructor]
    TransitLogRecord(ThreadContext* thread_context, std::unique_ptr<RecordBase> base_record)
                                                                                ^
_deps/quill-src/quill/include/quill/detail/BackendWorker.h:156:33: note: previous declaration is here
    std::unique_ptr<RecordBase> base_record;

"emit" function names conflict with Qt macro

There are many compilation errors when used with Qt.
I guess emit function names conflict with the Qt macro emit.

[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/Handler.h(59): error C2143: syntax error: missing ')' before 'const'
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/Handler.h(59): error C2143: syntax error: missing ';' before 'const'
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/Handler.h(59): error C2838: 'memory_buffer': illegal qualified name in member declaration
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/Handler.h(59): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/Handler.h(60): error C3646: 'log_record_timestamp': unknown override specifier
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/Handler.h(60): error C2059: syntax error: ')'
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/Handler.h(60): error C2838: 'nanoseconds': illegal qualified name in member declaration
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/StreamHandler.h(32): error C2143: syntax error: missing ')' before 'const'
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/StreamHandler.h(32): error C2143: syntax error: missing ';' before 'const'
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/StreamHandler.h(32): error C2838: 'memory_buffer': illegal qualified name in member declaration
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/StreamHandler.h(32): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/StreamHandler.h(32): error C3646: 'log_record_timestamp': unknown override specifier
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/StreamHandler.h(32): error C2059: syntax error: ')'
[build] D:\tests\cmaketest\quill\quill\include\quill/handlers/StreamHandler.h(32): error C2838: 'nanoseconds': illegal qualified name in member declaration

Support {fmt} v7.1.1

Recently, {fmt} version 7.1.0 was released.

When using quill with vcpkg after microsoft/vcpkg#14217 and microsoft/vcpkg#14239, I am now experiencing the following issues when building:

quill.lib(FileUtilities.cpp.obj) : error LNK2019: unresolved external symbol "struct fmt::v7::detail::dragonbox::decimal_fp<float> __cdecl fmt::v7::detail::dragonbox::to_decimal<float>(float)" (??$to_decimal@M@dragonbox@detail@v7@fmt@@YA?AU?$decimal_fp@M@0123@M@Z) referenced in function "class fmt::v7::detail::buffer_appender<wchar_t> __cdecl fmt::v7::detail::write<wchar_t,class fmt::v7::detail::buffer_appender<wchar_t>,float,0>(class fmt::v7::detail::buffer_appender<wchar_t>,float)" (??$write@_WV?$buffer_appender@_W@detail@v7@fmt@@M$0A@@detail@v7@fmt@@YA?AV?$buffer_appender@_W@012@V3012@M@Z)
quill.lib(FileUtilities.cpp.obj) : error LNK2019: unresolved external symbol "struct fmt::v7::detail::dragonbox::decimal_fp<double> __cdecl fmt::v7::detail::dragonbox::to_decimal<double>(double)" (??$to_decimal@N@dragonbox@detail@v7@fmt@@YA?AU?$decimal_fp@N@0123@N@Z) referenced in function "class fmt::v7::detail::buffer_appender<wchar_t> __cdecl fmt::v7::detail::write<wchar_t,class fmt::v7::detail::buffer_appender<wchar_t>,double,0>(class fmt::v7::detail::buffer_appender<wchar_t>,double)" (??$write@_WV?$buffer_appender@_W@detail@v7@fmt@@N$0A@@detail@v7@fmt@@YA?AV?$buffer_appender@_W@012@V3012@N@Z)

...which I believe to be related to the switch from Grisu3 to Dragonbox for the default floating-point formatting, as quoted from the release page:

Switched from Grisu3 to Dragonbox for the default floating-point formatting which gives the shortest decimal representation with round-trip guarantee and correct rounding (#1882, #1887, #1894).

I've opened an issue on vcpkg (microsoft/vcpkg#14331) thinking the issue was vcpkg, but now I realize that a new version of {fmt} was released and I think that is the problem.

Missing Documentation for Function Arguments

When compiling with Clang's "-Wdocumentation" flag, there is a slew of warnings output for Doxygen documentation for function arguments which are missing documentation strings. I would like to have these warnings treated as errors when compiling my application, but the included headers cause my build to fail. I can ignore the warning for now, but would it be possible to either remove the undocumented function parameters or document them to fix this warning?

timestamp point

Hi,

At what point does Quill calculate the timestamp? is it at the time the caller sends to the log thread? or when the worker thread eventually gets it from the work queue?

example_custom_formatter.cpp causes assertion failure

example_custom_formatter.cpp fails with the following message:

$ ./example_custom_formatter
libc++abi.dylib: terminating with uncaught exception of type quill::QuillError: set_default_logger_handler needs to be called before quill::start()
zsh: abort      ./example_custom_formatter

Support {fmt} Version 7

Quill needs updated to support {fmt} 7.

When using a an external {fmt} of version 7 or higher, compilation fails.

_deps/quill-src/quill/include/quill/detail/LogMacros.h:37:8: fatal error: no member named 'internal' in namespace 'fmt'
  fmt::internal::check_format_string<std::remove_reference_t<Args>...>(format_str);
  ~~~~~^

The internal namespace was renamed to detail in {fmt} version 7.
Updating the namespace used in LogMacros.h fixes the issue, as shown below.

/**
 * Check in compile time the correctness of a format string
 */
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
constexpr void check_format(const S& format_str, Args&&...)
{
  fmt::detail::check_format_string<std::remove_reference_t<Args>...>(format_str);
}

Is it possible to create multiple stdout loggers with different formatters?

As the question says, I want to create multiple quill::Loggers, that all log to stdout, but have different formatters. Basically such that:

LOG_INFO(log_a, "hello"); // [A] hello
LOG_INFO(log_b, "hello"); // [B] hello

But all to stdout, not to different files.

The documentation seems to suggest there is a singleton stdout handler with a single formatter. Is it possible to do what I'm suggesting or do I have to manually write the [A] and [B] for each log line?

A huge time gap between the first log and the second log

Thanks for your great work! I can compile quill using make now. I write a wrapper for using quill in my project. It works well.
But now I notice some performance issues, for example:

13:29:44.906777105 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906781955 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906781971 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906781987 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906781996 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782006 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782015 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782025 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782034 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782044 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782054 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782063 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782072 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782082 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782090 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782098 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782106 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782115 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782125 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782132 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782141 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782148 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782157 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782165 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782173 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782183 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782190 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782198 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782205 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782214 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782222 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782231 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782238 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782249 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782256 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782266 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782274 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782282 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782290 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782300 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .
13:29:44.906782308 [27908] index_base.cpp:99            LOG_INFO      root         - ok  this is index base .

Why is there a huge time gap between the first log and the second log? Hope your help, thx.

[feature request] user defined type support member function `std::string to_string() const`

Currently, quill only support operator<<() for user defined type. But << is rarely used (and is not compatiable with fmt and quill grammar). Could you support std::string to_string() const?

Here is an example:

struct User {
    std::string name;
    std::string password;

    std::string to_string() const {
        return fmt::format("{{user:{}, password:{}}}", user, password);
   }
};

int main() {
    // ....
    User user{"foo", "xxx"};
    LOG_INFO("{}", user);
}

[feature request] bind log level to handlers

Hi, I use a single log with two handlers, one is file handle, the other is stdout.

I need to log all to file (set_log_level=TraceL3) but only log error (and critical) on stdout(set_log_level=Error).

However, currently, log levels are binded to log instance but not handlers. It will be great if log levels are binded to handlers (just like formatter, and also in Python).

Don't automatically use verbose makefiles

Currently, that flag is set unconditionally if quill detects that it's the primary project. Can it... not?

If I want verbose makefiles, that's easy for me to specify.

Add fmt library version number in changelog

Our application also uses fmt library, which is a great library. Quill has its copy of fmt in include/bundled/ directory. Right now our fmt library(6.1.2) seems to co-exist well with bundled/fmt. But in the future, quill can upgrade to a different fmt version. Users would like to know what version of fmt is supported/tested with a new quill release. It will be convenient to just read changelog.md for this information.

Compile on arm/arm64

Attempt to compile and test the logging library on arm/arm64.
At the moment it won't compile.

Recent change breaks logging in nested generic lambdas

Here's a fairly minimal repo (both lambdas must be generic). On gcc 8.3.0, the following compiles on v1.2.3 but breaks on master (bisect points to c3cd06c as the breaking commit)

#include <quill/Quill.h>

void f(quill::Logger* log) {
    [log](auto){
        [log](auto){
            LOG_INFO(log, "Hi");
        }(1);
    }(0);
}

This is because (for whatever reason), __FUNCTION__ isn't a constant expression in that context on gcc 8.3 (I think this is gcc bug 66639):

void f() {
    [](auto){
        [](auto){
            static constexpr char const* function_name = __FUNCTION__;
            static constexpr int value = function_name[0]; // error on 8.3, ok on 9.2
            (void)value;
        }(1);
    }(0);
}

Support non-guaranteed logging (drop messages)

Add an option to drop messages instead. In low-latency settings, allocating is not always an option.
In this case use a bounded fixed size queue instead of an unbounded one.
Notify the user how many messages were dropped.

Backend worker thread, read all messages from the queue as a batch, to keep the queue as empty as possible

At the moment the backend worker thread processes the log record messages from each caller via a queue, but it only reads one message each time.

In situations where there is a burst of messages to the queue, the queue can get full quickly and cause a re-allocation in the hot path.

The backend logging thread should read everything from the queue first as a batch, then store it locally and then process the messages one by one from the local copy instead.
This will keep the queue shared with the hot patch as empty as possible eliminating the possibility of the queue being full.

Support external fmt

At the moment Quill is using a bundled version of lib fmt.

If a project is already using it's own fmt version, both fmt libraries have to be of the same version. Otherwise it won't work

There can be a CMake option FMT_EXTERNAL that when specified Quill will try to find the user's already installed fmt library and use that instead of the bundled one

Document gotcha, unsafe classes [Documentation]

Logging Mutable classes with std::shared_ptr even on a single thread or classes that contain a raw pointer or reference that is mutated can be problematic since the backend worker thread might log the updated value.

Those classes need to be converted to a string first before being logged

A similar case to this : https://github.com/odygrd/quill/wiki/5.-FAQ

The data can be mutated after the log call and before the formatting.
The data can be mutated (surprise) during formatting.

Formatting timestamp fractional seconds in PatternFormatter

Currently, formatting the time always appends the fractional part of the seconds to the string formatted with strftime. I want to include the fractional seconds immediately after the seconds, but also include time zone information at the end of the date-time.

The following example should illustrate this.
I have the date-time format string "%FT%T%z%Z".
This produces 2020-04-15T11:31:08-0500CDT.543.
I would like to be able to format the date-time to look like this: 2020-04-15T11:31:08.543-0500CDT.

Conflicting declaration of strlen with Mingw-w64

There is a conflicting declaration of the function strlen with C-Linkage when compiling with Mingw-w64 on Windows 10.
This occurs in version v1.2.3 and on the most recent commit a4ae296.
The compiler error is as follows.

C:/msys64/mingw64/x86_64-w64-mingw32/include/intrin.h:269:5: error: conflicting declaration of 'size_t quill::detail::strlen(const char*)' with 'C' linkage
  269 |     __MACHINE(size_t __cdecl strlen(const char *))
      |     ^~~~~~~~~
compilation terminated due to -Wfatal-errors.

The conflicting function is defined in Utilities.h.

Compilation for __ARM_ARCH < 6

Currently quill does not compile for __ARM_ARCH < 6 because x86 intrinsics are used in rdtsc.h. Using chrono clock does not help. Perhaps defaulting to chrono clock in this case is a solution.

Please support local timezone

Thank you for your great work!
I love this library.

One thing to prevent using quill is supporting local time zone.
Please support local timezone.
Currently quill::PatternFormatter::_timezone_type is private and hard coded.

Custom handlers

Is there support for custom handlers? I am not sure if I just missed them in the documentation / examples. If not, are you planning to support custom handlers?

wstring_to_utf8 is not a member of quill::detail

On Mingw-w64, using GCC 10.1.0, the following error comes up.

In file included from _deps/quill-src/quill/include/quill/handlers/Handler.h:8,
                 from _deps/quill-src/quill/include/quill/handlers/StreamHandler.h:11,
                 from _deps/quill-src/quill/src/handlers/StreamHandler.cpp:1,
                 from _deps/quill-build/quill/CMakeFiles/quill.dir/Unity/unity_2_cxx.cxx:3:
_deps/quill-src/quill/include/quill/PatternFormatter.h: In member function 'std::enable_if_t<quill::detail::any_is_same<std::__cxx11::basic_string<wchar_t>, void, Args ...>::value, void> quill::PatternFormatter::format(std::chrono::nanoseconds, const char*, const char*, const quill::detail::LogRecordMetadata&, const Args& ...) const':
_deps/quill-src/quill/include/quill/PatternFormatter.h:442:11: error: 'wstring_to_utf8' is not a member of 'quill::detail'
  442 |   detail::wstring_to_utf8(_w_memory_buffer, _formatted_log_record);
      |           ^~~~~~~~~~~~~~~

I'm using commit a5ad7b0 at the moment.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.