fmtlog is a performant fmtlib-style logging library with latency in nanoseconds.

Overview

fmtlog

fmtlog is a performant asynchronous logging library using fmt library format.

Features

  • Faster - lower runtime latency than NanoLog and higher throughput than spdlog (see Performance below).
  • Headers only or compiled
  • Feature rich formatting on top of excellent fmt library.
  • Asynchronous multi-threaded logging in time order and can also be used synchronously in single thread.
  • Custom formatting
  • Custom handling - user can set a callback function to handle log msgs in addition to writing into file.
  • Log filtering - log levels can be modified in runtime as well as in compile time.
  • Log frequency limitation - specific logs can be set a minimum logging interval.

Platforms

  • Linux (GCC 10.2 tested)
  • Windows (MSVC 2019 tested)

Install

C++17 is required, and fmtlog is dependent on fmtlib, you need to install fmtlib first if you haven't.

Header only version

Just copy fmtlog.h and fmtlog-inl.h to your project, and define macro FMTLOG_HEADER_ONLY before including fmtlog.h. You might also want to define FMT_HEADER_ONLY if you are using fmtlib in header-only way.

Static/Shared lib version built by CMake

$ git clone https://github.com/MengRao/fmtlog.git
$ cd fmtlog
$ git submodule init
$ git submodule update
$ ./build.sh

Then copy fmtlog.h and libfmtlog-static.a/libfmtlog-shared.so generated in .build dir.

Usage

#include "fmtlog/fmtlog.h"
int main() 
{
  FMTLOG(fmtlog::INF, "The answer is {}.", 42);
}

There're also shortcut macros logd, logi, logw and loge defined for logging DBG, INF, WRN and ERR msgs respectively:

logi("A info msg");
logd("This msg will not be logged as the default log level is INF");
fmtlog::setLogLevel(fmtlog::DBG);
logd("Now debug msg is shown");

Note that fmtlog is asynchronous in nature, msgs are not written into file/console immediately after the log statements: they are simply pushed into a queue. You need to call fmtlog::poll() to collect data from log queues, format and write it out:

fmtlog::setThreadName("aaa");
logi("Thread name is bbb in this msg");
fmtlog::setThreadName("bbb");
fmtlog::poll();
fmtlog::setThreadName("ccc");
logi("Thread name is ccc in this msg");
fmtlog::poll();
fmtlog::setThreadName("ddd");

fmtlog supports multi-threaded logging, but can only have one thread calling fmtlog::poll(). By default, fmtlog doesn't create a polling thread internally, it requires the user to poll it periodically. The idea is that this allows users to manage the threads in their own way, and have full control of polling/flushing behavior. However, you can ask fmtlog to create a background polling thread for you by fmtlog::startPollingThread(interval) with a polling interval, but you can't call fmtlog::poll() when the thread is running.

Format

fmtlog is based on fmtlib, almost all fmtlib features are supported(except for color):

#include "fmt/ranges.h"
using namespace fmt::literals;

logi("I'd rather be {1} than {0}.", "right", "happy");
logi("Hello, {name}! The answer is {number}. Goodbye, {name}.", "name"_a = "World", "number"_a = 42);

std::vector<int> v = {1, 2, 3};
logi("ranges: {}", v);

logi("std::move can be used for objects with non-trivial destructors: {}", std::move(v));
assert(v.size() == 0);

std::tuple<int, char> t = {1, 'a'};
logi("tuples: {}", fmt::join(t, ", "));

enum class color {red, green, blue};
template <> struct fmt::formatter<color>: formatter<string_view> {
  // parse is inherited from formatter<string_view>.
  template <typename FormatContext>
  auto format(color c, FormatContext& ctx) {
    string_view name = "unknown";
    switch (c) {
    case color::red:   name = "red"; break;
    case color::green: name = "green"; break;
    case color::blue:  name = "blue"; break;
    }
    return formatter<string_view>::format(name, ctx);
  }
};
logi("user defined type: {:>10}", color::blue);
logi("{:*^30}", "centered");
logi("int: {0:d};  hex: {0:#x};  oct: {0:#o};  bin: {0:#b}", 42);
logi("dynamic precision: {:.{}f}", 3.14, 1);

// A compile-time error because 'd' is an invalid specifier for strings.
logi("{:d}", "foo");

As an asynchronous logging library, fmtlog provides additional support for passing arguments by pointer(which is seldom needed for fmtlib and it only supports void and char pointers). User can pass a pointer of any type as argument to avoid copy overhead if the lifetime of referred object is assured(otherwise the polling thread will refer to a dangling pointer!). For string arg as an example, fmtlog copies string content for type std::string by default, but only a pointer for type std::string*:

  std::string str = "aaa";
  logi("str: {}, pstr: {}", str, &str);
  str = "bbb";
  fmtlog::poll();
  // output: str: aaa, pstr: bbb

In addition to raw pointers, fmtlog supports std::shared_ptr and std::unique_ptr as well, which makes object lifetime management much easier:

  int a = 4;
  auto sptr = std::make_shared<int>(5);
  auto uptr = std::make_unique<int>(6);
  logi("void ptr: {}, ptr: {}, sptr: {}, uptr: {}", (void*)&a, &a, sptr, std::move(uptr));
  a = 7;
  *sptr = 8;
  fmtlog::poll();
  // output: void ptr: 0x7ffd08ac53ac, ptr: 7, sptr: 8, uptr: 6

Log header pattern can also be customized with fmtlog::setHeaderPattern() and the argument is a fmtlib format string with named arguments. The default header pattern is "{HMSf} {s:<16} {l}[{t:<6}] " (example: "15:46:19.149844 log_test.cc:43 INF[448050] "). All supported named arguments in header are as below:

Name Meaning Example
l Log level INF
s File base name and line num log_test.cc:48
g File path and line num /home/raomeng/fmtlog/log_test.cc:48
t Thread id by default, can be reset by fmt::setThreadName() main
a Weekday Mon
b Month name May
Y Year 2021
C Short year 21
m Month 05
d Day 03
H Hour 16
M Minute 08
S Second 09
e Millisecond 796
f Microsecond 796341
F Nanosecond 796341126
Ymd Year-Month-Day 2021-05-03
HMS Hour:Minute:Second 16:08:09
HMSe Hour:Minute:Second.Millisecond 16:08:09.796
HMSf Hour:Minute:Second.Microsecond 16:08:09.796341
HMSF Hour:Minute:Second.Nanosecond 16:08:09.796341126
YmdHMS Year-Month-Day Hour:Minute:Second 2021-05-03 16:08:09
YmdHMSe Year-Month-Day Hour:Minute:Second.Millisecond 2021-05-03 16:08:09.796
YmdHMSf Year-Month-Day Hour:Minute:Second.Microsecond 2021-05-03 16:08:09.796341
YmdHMSF Year-Month-Day Hour:Minute:Second.Nanosecond 2021-05-03 16:08:09.796341126

Note that using concatenated named args is more efficient than seperated ones, e.g. {YmdHMS} is faster than {Y}-{m}-{d} {H}:{M}:{S}.

Output

By default, fmtlog output to stdout. Normally users want to write to a log file instead, this is accomplished by fmtlog::setLogFile(filename,truncate). For performance, fmtlog internally buffer data, and under certain conditions will the buffer be flushed into the underlying file. The flushing conditions are:

  • The underlying FILE* is not managed by fmtlog, then fmtlog will not buffer at all. For example, the default stdout FILE* will not be buffered. User can also pass an existing FILE* and indicate whether fmtlog should manage it by fmtlog::setLogFile(fp, manageFp), e.g. fmtlog::setLogFile(stderr, false), then fmtlog will log into stderr without buffering.
  • The buffer size is larger than 8 KB, this number can be reset by fmtlog::setFlushBufSize(bytes).
  • The oldest data in the buffer has passed a specified duration. The duration is by default 3 seconds, and can be set by fmtlog::setFlushDelay(ns).
  • The new log has at least a specified flush log level. The default flush log level can't be reached by any log, but it can be set by fmtlog::flushOn(logLevel).
  • User can actively ask fmtlog to flush by fmtlog::poll(true).

Optionally, user can ask fmtlog to close the log file by fmtlog::closeLogFile(), and subsequent log msgs will not be output.

In addition to writing to a FILE*, user can register a callback function to handle log msgs by fmtlog::setLogCB(cb, minCBLogLevel). This can be useful in circumstances where warning/error msgs need to be published out in real time for alerting purposes. Log callback will not be buffered as log file, and can be triggered even when the file is closed. The signiture of callback function is:

  // callback signature user can register
  // ns: nanosecond timestamp
  // level: logLevel
  // location: full file path with line num, e.g: /home/raomeng/fmtlog/fmtlog.h:45
  // basePos: file base index in the location
  // threadName: thread id or the name user set with setThreadName
  // msg: full log msg with header
  // bodyPos: log body index in the msg
  typedef void (*LogCBFn)(int64_t ns, LogLevel level, fmt::string_view location, size_t basePos,
                          fmt::string_view threadName, fmt::string_view msg, size_t bodyPos);

Performance

Benchmark is done in terms of both front-end latency and throughput, with comparisons to Nanolog and spdlog basic_logger_st. Test log messages use NanoLog benchmark Log-Messages-Map, and header pattern uses spdlog default pattern(e.g. "[2021-05-04 10:36:38.098] [spdlog] [info] [bench.cc:111] "), check bench.cc for details.

The results on a linux server with "Intel(R) Xeon(R) Gold 6144 CPU @ 3.50GHz" is:

Message fmtlog Nanolog spdlog
staticString 6.4 ns, 7.08 M/s 6.5 ns, 33.10 M/s 156.4 ns, 6.37 M/s
stringConcat 6.4 ns, 6.05 M/s 7.5 ns, 14.20 M/s 209.4 ns, 4.77 M/s
singleInteger 6.3 ns, 6.22 M/s 6.5 ns, 50.29 M/s 202.3 ns, 4.94 M/s
twoIntegers 6.4 ns, 4.87 M/s 6.6 ns, 39.25 M/s 257.2 ns, 3.89 M/s
singleDouble 6.2 ns, 5.37 M/s 6.5 ns, 39.62 M/s 225.0 ns, 4.44 M/s
complexFormat 6.4 ns, 2.95 M/s 6.7 ns, 24.30 M/s 390.9 ns, 2.56 M/s

Note that the throughput of Nanolog is not comparable here because it outputs to binary log file instead of human-readable text format, e.g. it saves an int64 timestamp instead of a long formatted date time string.

How can fmtlog achieve such low and stable latency? Two key optimization techniques are employed inspired by Nanolog:

One is allocating a single producer single consumer queue for each logging thread, and have the background thread polling for all these queues. This avoids threads contention and performance will not deteriorate when thread number increases. The queue is automatically created on the first log msg of a thread, so queue is not created for threads that don't use fmtlog. The thread queue has a fixed size of 1 MB, and it takes a little time to allocate the queue. It's recommended that user actively calls fmt::preallocate() once the thread is created, so even the first log can have low latency.

What happens when the queue is full? By default, fmtlog simply dump addtional log msgs and return. Alternatively, front-end logging can be blocked while the queue is full by defining macro FMTLOG_BLOCK=1, then no log will be missing. Normally, consumer(fmtlog::poll()) should keep up with producers(log statments in multiple threads) and log queues seldom become full, but incautious user could leave log statements that are invoked in an unexpected high frequency, e.g. a tcp client spamming with "Connection refused" errors without a connection retry delay. To handle this problem in an elegant way, fmtlog provides a log macro which limits mininum interval of this log: FMTLOG_LIMIT and 4 shortcuts logdl, logil, logwl and logel respectively, user needs to pass the mininum interval in nanosecond as the first argument, e.g.

logil(1000000000, "this log will be displayed at most once per second").

The other optimization is that static information of a log(such as format string, log level and location) is saved in a table at its first call, and fmtlog simply pushes the index of the static info table entry with dynamic arguments to the queue, minimizing the msg size. In addition, fmtlog defines a decoding function for each log statment, which is invoked in fmtlog::poll() when the log msg is popped from the queue.

However, these decoding functions bloat program size with each function consuming around 50 bytes. In addition, the static infomation entry also consumes 50-ish bytes runtime memory for each log statement. Such memory overhead may not be worthwhile for those infrequent and latency insensitive logs(e.g. program initialization info), thus fmtlog provides user with another log macro which disables this optimization: FMTLOG_ONCE and of couse shortcuts: logdo, logio, logwoand logeo. FMTLOG_ONCE will not create a static info table entry, nor add a decoding function: it pushes static info along with formatted msg body onto the queue. Note that passing argument by pointer is not supported by FMTLOG_ONCE.

For those who prefer to further optimize memory usage by filtering log at compile time, macro FMTLOG_ACTIVE_LEVEL is applied with a default value FMTLOG_LEVEL_INF, meaning debug logs will simply be discarded at compile time. Note that FMTLOG_ACTIVE_LEVEL only applies to log shortcut macros, e.g. logi, but not FMTLOG.

Comments
  • Feature suggestion: add a callback for logging threads (in addition to the async work callback)

    Feature suggestion: add a callback for logging threads (in addition to the async work callback)

    Current callback set with fmtlog::setLogCB is called from fmtlog::poll, which doesn't help in scheduling background asynchronous work done by fmtlog::poll. Consider adding a callback that would be called in the context of the logging thread based on some criteria, which could be used to queue a work request for the thread calling fmtlog::poll.

    Consider a scenario in which fmtlog::poll is being called periodically (e.g. once a second) and keeps the logging buffer reasonably empty for new log messages. If a logging burst occurs that would overflow the buffer, some of the messages would be lost or blocking will occur, based on FMTLOG_BLOCK. If the frequency of fmtlog::poll is increased, it would be called unnecessarily most of the time, since logging bursts would happen rarely.

    If there was a callback invoked in the logging thread context (i.e. from the call stack of log and logOnce), and this callback would have some call condition (e.g. when logger buffer is 50% full), it would be possible to use this callback to queue an asynchronous work request for the logger thread at the application level (i.e. outside of fmtlog), which would trigger fmtlog::poll call between regular polling intervals.

    opened by gh-andre 12
  • fmtlog::setLogLevel(fmtlog::DBG);没有生效

    fmtlog::setLogLevel(fmtlog::DBG);没有生效

    使用了说明里的代码: logi("A info msg"); logd("This msg will not be logged as the default log level is INF"); fmtlog::setLogLevel(fmtlog::DBG); logd("Now debug msg is shown"); fmtlog::poll(); 结果输出中,依然仅仅输出了A info msg,而没有输出Now debug msg is shown

    opened by tilongzs 8
  • Installation of header files

    Installation of header files

    The header files are not set to be installed by the cmake. It is a minor issue... but I feel it would be good to install header files in the system so users do not need to copy these files in their project directory manually.

    opened by randomizedthinking 7
  • [Question] Several logs output in one line in multithreaded application

    [Question] Several logs output in one line in multithreaded application

    I have a multithreaded application. Auto-polling is enabled with 1 second delay. I don't do any fmtlog::poll() calls manually in my code.

    Sometimes I get strange output like that:

    10:05:21.305388 web_socket_cli nmarket_data_server.cpp:439cketClient::WebSocketClien  INFcan[t17ni      ] itMarket data websocket client was created successfully with connection handler _key" : "..........................................................."
    

    What am I doing wrong?

    opened by Elnee 6
  • Format string is hard-coded to be a string literal

    Format string is hard-coded to be a string literal

    If the format string is not a string literal, cryptic compiler messages are reported and at first it's not even clear what the problem is. For example:

    template <typename ... T>
    void mylog(const std::string_view& format, T&& ... arg)
    {
      FMTLOG_ONCE(fmtlog::INF, format, std::forward<T>(arg)...);
    }
    
    int main(void)
    {
      mylog("test log line {} {}"sv, "ABC", 123);
      return 0;
    }
    

    GCC reports use of parameter from containing function against the format parameter and an additional error 'constexpr' call flows off the end of the function in core.h in fmt. VC++ reports just function cannot access 'fmt'.

    This is caused by FMT_STRING used in all logging macros, which only works with string literals. Removing FMT_STRING from logging macros fixes these errors.

    opened by gh-andre 5
  • undefined behaviour found by asan (llvm)

    undefined behaviour found by asan (llvm)

    Hi,

    When running the following small program under the memory sanitizers:

    #include "fmtlog/fmtlog.h"
    
    int main(int argc, char** argv)
    {
      fmtlog::startPollingThread();
      logi("some data: {}", argc);
      fmtlog::stopPollingThread();
      return 0;
    }
    

    I got the following output:

    /home/matthijs/t5/fmtlog/fmtlog-inl.h:72:17: runtime error: store to misaligned address 0x000000f28e77 for type 'uint16_t' (aka 'unsigned short'), which requires 2 byte alignment
    0x000000f28e77: note: pointer points here
     30 32 31 2d 00  00 2d 00 00 20 00 00 3a  00 00 3a 00 00 2e 00 00  00 00 00 00 00 00 00 00  00 00 00
                 ^
    SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /home/matthijs/t5/fmtlog/fmtlog-inl.h:72:17 in
    /home/matthijs/t5/fmtlog/fmtlog-inl.h:72:17: runtime error: store to misaligned address 0x000000f28e83 for type 'uint16_t' (aka 'unsigned short'), which requires 2 byte alignment
    0x000000f28e83: note: pointer points here
     3a  00 00 3a 00 00 2e 30 31  31 35 36 38 30 35 37 00  00 00 00 00 00 00 00 00  00 68 35 5b 3c 7f 00
                  ^
    SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /home/matthijs/t5/fmtlog/fmtlog-inl.h:72:17 in
    /home/matthijs/t5/fmtlog/fmtlog-inl.h:72:17: runtime error: store to misaligned address 0x000000f28e7d for type 'uint16_t' (aka 'unsigned short'), which requires 2 byte alignment
    0x000000f28e7d: note: pointer points here
     2d 30 39 20 00 00 3a  33 33 3a 31 36 2e 30 31  31 35 36 38 30 35 37 00  00 00 00 00 00 00 00 00  00
                 ^
    SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /home/matthijs/t5/fmtlog/fmtlog-inl.h:72:17 in
    22:33:16.011568 main.cpp:6       INF[4743  ] some data: 1
    

    Compile line:

    export ASAN_SYMBOLIZER_PATH="/usr/lib/llvm-12/bin/llvm-symbolizer"
    export ASAN_OPTIONS="check_initialization_order=1:detect_stack_use_after_return=1:strict_string_checks=1:strict_init_order=1"
    export LSAN_OPTIONS="use_unaligned=1"
    /usr/bin/clang++-12 -DFMTLOG_HEADER_ONLY -DFMT_HEADER_ONLY -fno-omit-frame-pointer -O1 -fsanitize=address,undefined,pointer-compare,pointer-subtract,leak -fsanitize-address-use-after-scope -stdlib=libc++ -g -std=gnu++17 -o main main.cpp
    ./main
    

    I am not sure if the sanitizers are to strict here or there is actual 'undefined behaviour' here. I also didn't take a look at the code on whats happening here...

    opened by matthijs 5
  • Problem about align

    Problem about align

    我在阅读时看到这段注释,这里的意思是否是在处理对齐要求大于 16 的对象时,gcc 会嵌入对齐指令,所以尽量使用 memcpy 而非 new? https://github.com/MengRao/fmtlog/blob/d34990c63c0c32a4cfbf5fcab6df3dd1e5121f19/fmtlog.h#L480-L482 但是我在 gcc 9.2 上观察到传递给 new 非对齐的地址,new 返回的值并没有偏移到对齐的位置上。随后我查看 cppreference,发现 placement new 返回的是给定的指针。 所以,这里指的是什么情况,是否是我理解错了场景?

    opened by LazyWolfLin 3
  • 关于多线程时间戳乱序问题

    关于多线程时间戳乱序问题

    采用heap处理多线程时间戳好像有些问题,不知是否是我理解有问题 代码demo如下

    #include <vector>
    #include <iostream>
    struct HeapNode
    {
        HeapNode(int t) : cur_time(t) {}
        int cur_time;
    };
    std::vector<HeapNode> bgThreadBuffers;
    
    void adjustHeap(size_t i)
    {
        while (true)
        {
            size_t min_i = i;
            size_t ch = i * 2 + 1;
            size_t end = std::min(ch + 2, bgThreadBuffers.size());
            for (; ch < end; ch++)
            {
                auto h_ch = bgThreadBuffers[ch].cur_time;
                auto h_min = bgThreadBuffers[min_i].cur_time;
                if (h_ch && (!h_min || h_ch < h_min))
                    min_i = ch;
            }
            if (min_i == i)
                break;
            std::swap(bgThreadBuffers[i], bgThreadBuffers[min_i]);
            i = min_i;
        }
    }
    int main()
    {
        HeapNode node1(0);
        HeapNode node2(3);
        HeapNode node3(1);
        HeapNode node4(2);
        HeapNode node5(4);
        HeapNode node6(7);
        bgThreadBuffers.push_back(node1);
        bgThreadBuffers.push_back(node2);
        bgThreadBuffers.push_back(node3);
        bgThreadBuffers.push_back(node4);
        bgThreadBuffers.push_back(node5);
        bgThreadBuffers.push_back(node6);
    
        for (int i = bgThreadBuffers.size() / 2; i >= 0; i--)
        {
            adjustHeap(i);
        }
        std::cout << "--------------end-----------------" << std::endl;
        for (auto node : bgThreadBuffers)
        {
            std::cout << node.cur_time << " ";
        }
        std::cout << std::endl;
    }
    

    输出结果为1 2 7 3 4 0 请大佬指教

    opened by ahao1995 3
  • Quoted #include pattern picks up wrong (packaged) fmt library

    Quoted #include pattern picks up wrong (packaged) fmt library

    Thank you for the logging library. It fits background logging quite well, leaving threading to the application, which many other logging libraries lack. One thing I wanted to mention is a small build issue in configurations that already have a packaged version of fmt installed.

    A quoted #include pattern always looks in the current directory and only then in the search path. If fmtlog is installed on a system that already has a packaged version of fmt installed (e.g. via dnf), this causes the wrong headers picked up.

    That is, fmtlog.h contains this line:

    #include "fmt/format.h"
    

    All compilers always check current directory, which always fails, and then the search path is checked. On systems where fmt is installed via a package manager, such as CentOS 8, there will be a version in /usr/include/fmt/, which would be picked via the search path.

    Given that libfmt includes fmt as a Git submodule, it should always include the version of fmt in its own source subtree, which can be done by using quoted #include pattern to include the header relative to the directory of the fmtlog.h file:

    #include "fmt/include/fmt/format.h"
    

    In this case this line can be removed from CMakeLists.txt as well:

    include_directories(fmt/include)
    

    These two changes allow both versions of fmt coexist without a conflict - one is in the packaged location and one under /usr/local/include/fmtlog/fmt/, or any other directory suitable for a given project.

    opened by gh-andre 3
  • 疑似丢数据

    疑似丢数据

    打开 startPollingThread()

    使用四个级别各打印 9999 条记录,然后退出程序

    检查日志,发现前面部分完好无误,接近结尾的部分数据丢失严重。

    测试代码:

    
    #include <csignal>
    #include "fmtlog/fmtlog.h"
    
    void exit_f(int signal)
    {
     fmtlog::stopPollingThread();
    }
    
    int run()
    {
      std::signal(SIGTERM, exit_f);
      std::signal(SIGINT, exit_f);
    
      fmtlog::setLogFile("logs/main.log");
      fmtlog::setHeaderPattern("{HMSF} {s:<16} {l}[{t:<6}]");
      fmtlog::setLogLevel(fmtlog::DBG);
    
      fmtlog::startPollingThread();
    
      for(int i = 0 ; i < 10000; ++i)
      {
        logi("aaaaaaaaaaaaaaaaaaaa:{}, {}", i, "dsfsdfsfsdssssssssssssssssssssssssssssssssssssssssssssssssssssssss");
        logd("bbbbbbbbbbbbbbbbb:{}, {}", i, "dsfsdfsfsdssssssssssssssssssssssssssssssssssssssssssssssssssssssss");
        logw("cccccccccccccccccccccc:{}, {}", i, "dsfsdfsfsdssssssssssssssssssssssssssssssssssssssssssssssssssssssss");
        loge("ddddddddddddddddddd:{}, {}", i, "dsfsdfsfsdssssssssssssssssssssssssssssssssssssssssssssssssssssssss");
     }
    
     logi("close:");
    
     return 0;
    
    }
    
    
    int main(int argc, char* argv[])
    {
    
      run();
      return 0;
    }
    
    

    编译选项:

    add_compile_definitions(FMTLOG_ACTIVE_LEVEL=FMTLOG_LEVEL_DBG) add_compile_definitions(FMTLOG_BLOCK=1)

    测试结果的临近末尾部分: test

    opened by WayneY 3
  • Changing log format

    Changing log format

    I cannot find examples in the documentation on how to change the format of the log message. I would like to remove the 'location' part from it. Studying the source code location is saved the first time the log is called from a thread. Still, I don't quite get a simple way to change the format. Please advise!

    opened by maxsharabayko 2
  • Building on Win32 / Visual Studio generates a .dll with no exports

    Building on Win32 / Visual Studio generates a .dll with no exports

    Hi! I succesfuly built fmtlog with CMake on a Windows 10 box, with CMake 3.25.0 and Visual Studio 2019. Targets fmtlog-static.lib and fmtlog-shared.dll are generated. Tests targets enc_dec_test.exe, log_test.exe, multithread_test.exe are generated linking against the static library.

    But:

    fmtlog-shared.dll has no exported symbols, because they are actually not exported i.e. no FMTLOG_API that resolves to __declspec(dllimport) / __declspec(dllexport), with some FMTLOG_EXPORTS compile symbol defined when building the DLL. (So there is no fmtlog-shared.lib because no symbol is exported)

    I know... Stupid Windows linker, GNU ld does not need this...

    opened by olivier-fs 6
  • vs2022 上编译报错c2955

    vs2022 上编译报错c2955

    错误 C2955 “fmt::v8::basic_format_args”: 使用 类 模板 需要 模板 参数列表 错误 C2955 “fmt::v8::basic_format_args”: 使用 类 模板 需要 模板 参数列表 错误 C2661 “fmtlogT<0>::vformat_to”: 没有重载函数接受 2 个参数

    opened by zhangwen-sys 2
  • Failure to build on MacOS

    Failure to build on MacOS

    Pulled "main" today and included in a C++ program which I am compiling on macOS.

    I had 2 issues, the first is as follows:

    fmtlog/fmtlog.h:101:28: error: expected ';' at end of declaration list
      static void preallocate() FMT_NOEXCEPT;
                               ^
                               ;
    

    I rectified this adding #define FMT_NOEXCEPT to my source file prior to #include <fmtlog/fmtlog.h> I now have:

    #define FMTLOG_HEADER_ONLY
    #define FMT_HEADER_ONLY
    #define FMT_NOEXCEPT
    #include <fmtlog/fmtlog.h>
    #include <fmt/format.h>
    

    The second issue is with macOS where the functionsyscall is deprecated:

    fmtlog/fmtlog-inl.h:294:44: warning: 'syscall' is deprecated: first deprecated in macOS 10.12 - syscall(2) is unsupported; please switch to a supported interface. For SYS_kdebug_trace use kdebug_signpost(). [-Wdeprecated-declarations]
       uint32_t tid = static_cast<uint32_t>(::syscall(SYS_gettid));
                                               ^
    /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/unistd.h:746:6: note: 'syscall' has been explicitly marked deprecated here
    int      syscall(int, ...);
             ^
    1 warning generated.
    

    I have patched locally by adding the following:

    diff --git a/fmtlog-inl.h b/fmtlog-inl.h
    index 9d85e51..34e1c11 100644
    --- a/fmtlog-inl.h
    +++ b/fmtlog-inl.h
    @@ -38,6 +38,10 @@ SOFTWARE.
     #include <unistd.h>
     #endif
    
    +#ifdef __APPLE__
    +#include <pthread.h>
    +#endif
    +
     namespace {
     void fmtlogEmptyFun(void*) {
     }
    @@ -290,6 +294,9 @@ public:
         fmtlog::threadBuffer = new fmtlog::ThreadBuffer();
     #ifdef _WIN32
         uint32_t tid = static_cast<uint32_t>(::GetCurrentThreadId());
    +#elif __APPLE__
    +    uint64_t tid = 0;
    +    pthread_threadid_np(nullptr, &tid);
     #else
         uint32_t tid = static_cast<uint32_t>(::syscall(SYS_gettid));
     #endif
    
    opened by usermarqueemark 1
  • Remove Spdlog and Nanolog

    Remove Spdlog and Nanolog

    Hi,

    I am trying to embed library in my C++ project as a submodule. It also brings Nanolog and spdlog as its own submodule. I believe we can avoid that if you create bench as separate repository.

    Thanks

    opened by alphanso 0
  • Fix android ndk build

    Fix android ndk build

    So I believe this PR fixes the issues related to building with Android NDK.

    NDK Version: 23.1.7779620 CMake version: 3.22.1 Build command:

    BUILD_DIR=${BUILD_DIR:-.build}
    
    ANDROID_NDK_CMAKE="-DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_NAME=Android -DCMAKE_ANDROID_API=24 -DANDROID_PLATFORM=24 -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a -DCMAKE_ANDROID_NDK=$ANDROID_NDK_ROOT -DCMAKE_ANDROID_STL_TYPE=c++_static -DCMAKE_CXX_STANDARD=20 -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake -DFMTLOG_IGNORE_SHARED=TRUE -DFMTLOG_SKIP_BENCHMARKS=TRUE -DFMTLOG_SKIP_TEST_BUILD=TRUE" 
    
    mkdir -p "$BUILD_DIR" \
      && cd "$BUILD_DIR" \
      && cmake $ANDROID_NDK_CMAKE -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..\
      && cmake --build . -j
    

    For whatever reason, the tests and benchmarks do not compile on NDK and they seem to be a bigger problem to solve than skip. For the purposes of this PR, I'll just be skipping them as I don't believe they are critical.

    However, I do imagine that building the shared library is important. I'm not sure why CMake cannot properly link functions to fmt while building shared. I do know that it works if you build without NDK though.

    I'm willing to provide logs if required. Cheers

    opened by Fernthedev 0
Releases(v2.2.1)
Owner
Meng Rao
Meng Rao
Asynchronous Low Latency C++ Logging Library

Quill Asynchronous Low Latency C++ Logging Library Introduction Features Performance Supported Platforms And Compilers Basic Usage CMake Integration D

Odysseas Georgoudis 677 Dec 20, 2022
A Fast and Convenient C++ Logging Library for Low-latency or Real-time Environments

xtr What is it? XTR is a C++ logging library aimed at applications with low-latency or real-time requirements. The cost of log statements is minimised

null 10 Jul 17, 2022
Colorful Logging is a simple and efficient library allowing for logging and benchmarking.

Colorful-Logging "Colorful Logging" is a library allowing for simple and efficient logging as well for benchmarking. What can you use it for? -Obvious

Mateusz Antkiewicz 1 Feb 17, 2022
Yet another logging library.

Blackhole - eating your logs with pleasure Blackhole is an attribute-based logger with strong focus on gaining maximum performance as possible for suc

Evgeny Safronov 191 Dec 20, 2022
A lightweight C++ logging library

Loguru: a lightweight and flexible C++ logging library. At a glance Documentation Documentation can be found at https://emilk.github.io/loguru/index.h

Emil Ernerfeldt 1.5k Jan 7, 2023
Portable, simple and extensible C++ logging library

Plog - portable, simple and extensible C++ logging library Pretty powerful logging library in about 1000 lines of code Introduction Hello log! Feature

Sergey Podobry 1.6k Dec 29, 2022
Fast C++ logging library.

spdlog Very fast, header-only/compiled, C++ logging library. Install Header only version Copy the source folder to your build tree and use a C++11 com

Gabi Melman 16.6k Jan 1, 2023
Cute Log is a C++ Library that competes to be a unique logging tool.

Cute Log Cute Log is a C++ Library that competes to be a unique logging tool. Version: 2 Installation Click "Code" on the main repo page (This one.).

null 3 Oct 13, 2022
Minimalistic logging library with threads and manual callstacks

Minimalistic logging library with threads and manual callstacks

Sergey Kosarevsky 20 Dec 5, 2022
logog is a portable C++ library to facilitate logging of real-time events in performance-oriented applications

logog is a portable C++ library to facilitate logging of real-time events in performance-oriented applications, such as games. It is especially appropriate for projects that have constrained memory and constrained CPU requirements.

John Byrd 46 Oct 21, 2020
Boost Logging library

Boost.Log, part of collection of the Boost C++ Libraries, provides tools for adding logging to libraries and applications. Directories build - Boost.L

Boost.org 157 Dec 22, 2022
C++ implementation of the Google logging module

Google Logging Library The Google Logging Library (glog) implements application-level logging. The library provides logging APIs based on C++-style st

Google 5.9k Jan 9, 2023
log4cplus is a simple to use C++ logging API providing thread-safe, flexible, and arbitrarily granular control over log management and configuration. It is modelled after the Java log4j API.

% log4cplus README Short Description log4cplus is a simple to use C++17 logging API providing thread--safe, flexible, and arbitrarily granular control

null 1.4k Jan 4, 2023
Uberlog - Cross platform multi-process C++ logging system

uberlog uberlog is a cross platform C++ logging system that is: Small Fast Robust Runs on Linux, Windows, OSX MIT License Small Two headers, and three

IMQS Software 15 Sep 29, 2022
Reckless logging. Low-latency, high-throughput, asynchronous logging library for C++.

Introduction Reckless is an extremely low-latency, high-throughput logging library. It was created because I needed to perform extensive diagnostic lo

Mattias Flodin 445 Dec 20, 2022
Asynchronous Low Latency C++ Logging Library

Quill Asynchronous Low Latency C++ Logging Library Introduction Features Performance Supported Platforms And Compilers Basic Usage CMake Integration D

Odysseas Georgoudis 677 Dec 20, 2022
A Fast and Convenient C++ Logging Library for Low-latency or Real-time Environments

xtr What is it? XTR is a C++ logging library aimed at applications with low-latency or real-time requirements. The cost of log statements is minimised

null 10 Jul 17, 2022