Log Tagging
Use this page to add static or dynamic tags to log messages for categorization, filtering, and
monitoring.
The _TAGS macros store their tags in the call site’s static metadata. This avoids copying tag
text with every record and is the preferred path when the tags are known at compile time.
To include these tags in the output, add the %(tags) placeholder to
PatternFormatterOptions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 | #include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogMacros.h"
#include "quill/Logger.h"
#include "quill/Utility.h"
#include "quill/sinks/ConsoleSink.h"
#include <cstdint>
#include <string>
#include <string_view>
#include <utility>
#define TAG_1 "foo"
#define TAG_2 "bar"
#define TAG_3 "baz"
/**
* This example shows how to attach tags to log statements and include them in
* the logger pattern with the `%(tags)` placeholder.
*/
int main()
{
// Start the backend thread
quill::BackendOptions backend_options;
quill::Backend::start(backend_options);
auto console_sink = quill::Frontend::create_or_get_sink<quill::ConsoleSink>("sink_id_1");
// Ensure the logging pattern includes the %(tags) placeholder to display custom tags.
// It is crucial to place %(tags) immediately before the next attribute without any intervening spaces.
// This ensures correct formatting when no tags are present, preventing unwanted gaps in the output.
quill::Logger* logger = quill::Frontend::create_or_get_logger(
"root", std::move(console_sink),
quill::PatternFormatterOptions{
"%(time) [%(thread_id)] %(short_source_location:<28) %(log_level:<9) "
"%(tags)%(message)",
"%Y-%m-%d %H:%M:%S.%Qms", quill::Timezone::GmtTime});
LOG_INFO_TAGS(logger, TAGS("random"), "Debug with tags");
LOG_INFO_TAGS(logger, TAGS(TAG_2), "Info with tags");
LOG_WARNING_TAGS(logger, TAGS(TAG_1, TAG_2), "Warning with tags");
LOG_ERROR_TAGS(logger, TAGS(TAG_1, TAG_2, TAG_3), "Info with tags");
LOG_INFO(logger, "Without tags");
}
|
Output:
2024-08-11 01:23:44.463 [46228] tags_logging.cpp:40 INFO #random Debug with tags
2024-08-11 01:23:44.463 [46228] tags_logging.cpp:41 INFO #bar Info with tags
2024-08-11 01:23:44.463 [46228] tags_logging.cpp:42 WARNING #foo #bar Warning with tags
2024-08-11 01:23:44.463 [46228] tags_logging.cpp:43 ERROR #foo #bar #baz Info with tags
2024-08-11 01:23:44.463 [46228] tags_logging.cpp:45 INFO Without tags
For tags selected at runtime, the macro-free API accepts a Tags object built
from char const*, std::string, or std::string_view values. The values are copied into
the Tags object, so a string view does not need to be null-terminated and its source only needs
to remain valid while the Tags constructor runs.
std::string execution_tag = current_execution_tag();
quill::info(logger, quill::Tags{execution_tag}, "Task resumed");
Macro-free logging uses the hybrid runtime-metadata path, which copies the combined tags into the
frontend queue before the call returns. The
QUILL_LOG_RUNTIME_METADATA_DEEP and QUILL_LOG_RUNTIME_METADATA_HYBRID macros can likewise
carry dynamic null-terminated tag strings. QUILL_LOG_RUNTIME_METADATA_SHALLOW only stores the
tag pointer, so that string must remain valid and immutable until the backend consumes the record.
The regular LOG_<LEVEL>_TAGS macros remain the lowest-latency choice for static tags; runtime
std::string and std::string_view values cannot be stored in their compile-time metadata.
Backend Tag Processing
Set PatternFormatterOptions::process_tags to transform the combined tag text
on the backend before it is inserted into %(tags). The callback receives a valid
null-terminated string and returns the text to display. See Formatters for an
example.
Tag Processing in Sinks
Tags can be accessed at the Sink level and used for additional log processing or filtering. This enables more log handling based on tag content. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 | #include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogMacros.h"
#include "quill/Logger.h"
#include "quill/Utility.h"
#include "quill/sinks/ConsoleSink.h"
#include <cstdint>
#include <iostream>
#include <string>
#include <string_view>
#include <utility>
#define TAG_1 "foo"
#define TAG_2 "bar"
#define TAG_3 "baz"
class CustomSink final : public quill::Sink
{
public:
CustomSink() = default;
/***/
void write_log(quill::MacroMetadata const* log_metadata, uint64_t /** log_timestamp **/,
std::string_view /** thread_id **/, std::string_view /** thread_name **/,
std::string const& /** process_id **/, std::string_view /** logger_name **/,
quill::LogLevel /** log_level **/, std::string_view /** log_level_description **/,
std::string_view /** log_level_short_code **/,
std::vector<std::pair<std::string, std::string>> const* /** named_args - only populated when named args in the format placeholder are used **/,
std::string_view /** log_message **/, std::string_view log_statement) override
{
if (log_metadata->tags())
{
std::cout << "Message tags: " << log_metadata->tags() << std::endl;
}
std::cout << log_statement << std::endl;
}
/***/
void flush_sink() noexcept override {}
/***/
void run_periodic_tasks() noexcept override {}
};
int main()
{
// Start the backend thread
quill::BackendOptions backend_options;
quill::Backend::start(backend_options);
auto sink = quill::Frontend::create_or_get_sink<CustomSink>("sink_id_1");
quill::Logger* logger = quill::Frontend::create_or_get_logger(
"root", std::move(sink),
quill::PatternFormatterOptions{
"%(time) [%(thread_id)] %(short_source_location:<28) %(log_level:<9) "
"%(message)",
"%Y-%m-%d %H:%M:%S.%Qms", quill::Timezone::GmtTime});
LOG_INFO_TAGS(logger, TAGS("random"), "Debug with tags");
LOG_INFO_TAGS(logger, TAGS(TAG_2), "Info with tags");
LOG_WARNING_TAGS(logger, TAGS(TAG_1, TAG_2), "Warning with tags");
LOG_ERROR_TAGS(logger, TAGS(TAG_1, TAG_2, TAG_3), "Info with tags");
LOG_INFO(logger, "Without tags");
}
|