JSON Logging - Quill v11.0.2 (original) (raw)

The library supports outputting structured JSON logs to either the console or a file. To utilize this feature, you must use named arguments within the format string placeholders (e.g., {name} instead of {}) when invoking the LOG_ macros.

For convenience, the LOGJ_ macros offer an alternative method for logging l-values, automatically including the argument names in the placeholders. These macros support up to 20 arguments.

It is also possible to combine JSON output with standard log pattern display by passing multiple Sink objects to a Logger. This allows you to see the same log message in different formats simultaneously.

Logging Json to Console

1#include "quill/Backend.h" 2#include "quill/Frontend.h" 3#include "quill/LogMacros.h" 4#include "quill/Logger.h" 5#include "quill/sinks/JsonSink.h" 6 7#include 8 9int main() 10{ 11 // Start the backend thread 12 quill::BackendOptions backend_options; 13 quill::Backend::start(backend_options); 14 15 // Frontend 16 17 // Create a json sink 18 auto json_sink = quill::Frontend::create_or_get_sinkquill::JsonConsoleSink("json_sink_1"); 19 20 // PatternFormatter is only used for non-structured logs formatting 21 // When logging only json, it is ideal to set the logging pattern to empty to avoid unnecessary message formatting. 22 quill::Logger* logger = quill::Frontend::create_or_get_logger( 23 "json_logger", std::move(json_sink), 24 quill::PatternFormatterOptions{"", "%H:%M:%S.%Qns", quill::Timezone::GmtTime}); 25 26 int var_a = 123; 27 std::string var_b = "test"; 28 29 // Log via the convenient LOGJ_ macros 30 LOGJ_INFO(logger, "A json message", var_a, var_b); 31 32 // Or manually specify the desired names of each variable 33 LOG_INFO(logger, "A json message with {var_1} and {var_2}", var_a, var_b); 34 35 for (uint32_t i = 0; i < 40; ++i) 36 { 37 // Will only log the message once per second 38 LOG_INFO_LIMIT(std::chrono::seconds{1}, logger, "A json message with {var_1} and {var_2}", var_a, var_b); 39 LOGJ_INFO_LIMIT(std::chrono::seconds{1}, logger, "A json message", var_a, var_b); 40 41 if (i % 10 == 0) 42 { 43 std::this_thread::sleep_for(std::chrono::milliseconds{(i / 10) * 500}); 44 } 45 } 46 47 for (uint32_t i = 0; i < 20; ++i) 48 { 49 // Will only log the message once per N occurrences second 50 LOG_INFO_LIMIT_EVERY_N(10, logger, "A json message with {var_1} and {occurrence}", var_a, i); 51 LOGJ_INFO_LIMIT_EVERY_N(10, logger, "A json message", var_a, i); 52 } 53}

Logging Json to File

1#include "quill/Backend.h" 2#include "quill/Frontend.h" 3#include "quill/LogMacros.h" 4#include "quill/Logger.h" 5#include "quill/sinks/JsonSink.h" 6#include 7 8int main() 9{ 10 // Start the backend thread 11 quill::BackendOptions backend_options; 12 quill::Backend::start(backend_options); 13 14 // Create a json sink 15 auto json_sink = quill::Frontend::create_or_get_sinkquill::JsonFileSink("example_json.log", 16 17 { 18 quill::FileSinkConfig config; 19 return config; 20 }()); 21 22 // PatternFormatter is only used for non-structured logs formatting 23 // When logging only json, it is ideal to set the logging pattern to empty to avoid unnecessary message formatting. 24 quill::Logger* logger = quill::Frontend::create_or_get_logger( 25 "json_logger", std::move(json_sink), 26 quill::PatternFormatterOptions{"", "%H:%M:%S.%Qns", quill::Timezone::GmtTime}); 27 28 int var_a = 123; 29 std::string var_b = "test"; 30 31 // Log via the convenient LOGJ_ macros 32 LOGJ_INFO(logger, "A json message", var_a, var_b); 33 34 // Or manually specify the desired names of each variable 35 LOG_INFO(logger, "A json message with {var_1} and {var_2}", var_a, var_b); 36}

Customising Json Format

To customize the JSON format, define a custom sink that derives from one of the following classes:

1#include "quill/Backend.h" 2#include "quill/Frontend.h" 3#include "quill/LogMacros.h" 4#include "quill/Logger.h" 5#include "quill/sinks/JsonSink.h" 6 7#include 8 9/** 10 * Overrides generate_json_message to use a custom json format 11 / 12class MyJsonConsoleSink : public quill::JsonConsoleSink 13{ 14 void generate_json_message(quill::MacroMetadata const /** log_metadata /, uint64_t log_timestamp, 15 std::string_view / thread_id /, std::string_view / thread_name /, 16 std::string const& / process_id /, std::string_view / logger_name /, 17 quill::LogLevel / log_level /, std::string_view log_level_description, 18 std::string_view / log_level_short_code /, 19 std::vector<std::pair<std::string, std::string>> const named_args, 20 std::string_view /* log_message /, 21 std::string_view / log_statement **/, char const* message_format) override 22 { 23 // format json as desired 24 _json_message.append(fmtquill::format(R"({{"timestamp":"{}","log_level":"{}","message":"{}")", 25 std::to_string(log_timestamp), log_level_description, message_format)); 26 27 // add log statement arguments as key-values to the json 28 if (named_args) 29 { 30 for (auto const& [key, value] : named_args) 31 { 32 _json_message.append(std::string_view{",""}); 33 _json_message.append(key); 34 _json_message.append(std::string_view{"":""}); 35 _json_message.append(value); 36 _json_message.append(std::string_view{"""}); 37 } 38 } 39 } 40}; 41 42int main() 43{ 44 // Start the backend thread 45 quill::BackendOptions backend_options; 46 quill::Backend::start(backend_options); 47 48 // Frontend 49 50 // Create a json sink 51 auto json_sink = quill::Frontend::create_or_get_sink("json_sink_1"); 52 53 // PatternFormatter is only used for non-structured logs formatting 54 // When logging only json, it is ideal to set the logging pattern to empty to avoid unnecessary message formatting. 55 quill::Logger logger = quill::Frontend::create_or_get_logger( 56 "json_logger", std::move(json_sink), 57 quill::PatternFormatterOptions{"", "%H:%M:%S.%Qns", quill::Timezone::GmtTime}); 58 59 int var_a = 123; 60 std::string var_b = "test"; 61 62 // Log via the convenient LOGJ_ macros 63 LOGJ_INFO(logger, "A json message", var_a, var_b); 64 65 // Or manually specify the desired names of each variable 66 LOG_INFO(logger, "A json message with {var_1} and {var_2}", var_a, var_b); 67}

Combining JSON and Standard Log Patterns

1#include "quill/Backend.h" 2#include "quill/Frontend.h" 3#include "quill/LogMacros.h" 4#include "quill/Logger.h" 5#include "quill/sinks/ConsoleSink.h" 6#include "quill/sinks/JsonSink.h" 7 8#include 9 10/** 11 * This example showcases the usage of the JsonFileSink to generate JSON-formatted logs. 12 * Additionally, it demonstrates how to simultaneously log in both the standard logger output 13 * format, e.g., to console and the corresponding JSON format to a JSON output sink. 14 * 15 * For successful JSON logging, it's essential to use named placeholders within the provided 16 * format string, such as "{method}" and "{endpoint}". 17 / 18 19int main() 20{ 21 // Start the backend thread 22 quill::BackendOptions backend_options; 23 quill::Backend::start(backend_options); 24 25 // Frontend 26 27 // Create a json file for output 28 auto json_sink = quill::Frontend::create_or_get_sinkquill::JsonFileSink( 29 "example_json.log", 30 31 { 32 quill::FileSinkConfig cfg; 33 cfg.set_open_mode('w'); 34 cfg.set_filename_append_option(quill::FilenameAppendOption::None); 35 return cfg; 36 }(), 37 quill::FileEventNotifier{}); 38 39 // When using the JsonFileSink, it is ideal to set the logging pattern to empty to avoid unnecessary message formatting. 40 quill::Logger json_logger = quill::Frontend::create_or_get_logger( 41 "json_logger", std::move(json_sink), 42 quill::PatternFormatterOptions{"", "%H:%M:%S.%Qns", quill::Timezone::GmtTime}); 43 44 for (int i = 0; i < 2; ++i) 45 { 46 LOG_INFO(json_logger, "{method} to {endpoint} took {elapsed} ms", "POST", "http://", 10 * i); 47 } 48 49 // It is also possible to create a logger than logs to both the json file and stdout 50 // with the appropriate format 51 auto json_sink_2 = quill::Frontend::get_sink("example_json.log"); 52 auto console_sink = quill::Frontend::create_or_get_sinkquill::ConsoleSink("console_sink_id_1"); 53 54 // Define a custom format pattern for console logging, which includes named arguments in the output. 55 // If you prefer to omit named arguments from the log messages, you can remove the "[%(named_args)]" part. 56 quill::PatternFormatterOptions console_log_pattern = quill::PatternFormatterOptions{ 57 "%(time) [%(thread_id)] %(short_source_location:<28) LOG_%(log_level:<9) %(logger:<20) " 58 "%(message) [%(named_args)]"}; 59 60 // Create a logger named "hybrid_logger" that writes to both a JSON sink and a console sink. 61 // Note: The JSON sink uses its own internal format, so the custom format defined here 62 // will only apply to the console output (via console_sink). 63 quill::Logger* hybrid_logger = quill::Frontend::create_or_get_logger( 64 "hybrid_logger", {std::move(json_sink_2), std::move(console_sink)}, console_log_pattern); 65 66 for (int i = 2; i < 4; ++i) 67 { 68 LOG_INFO(hybrid_logger, "{method} to {endpoint} took {elapsed} ms", "POST", "http://", 10 * i); 69 } 70 71 LOG_INFO(hybrid_logger, "Operation {name} completed with code {code}", "Update", 123, 72 "Data synced successfully"); 73}