-
I'd like to have a custom thread name for every log generated from a different thread. From the docs it looks like I need to set thread_name. To accomplish I did
I'm using a user-defined JsonConsoleSink as discussed in this discussion - #601 (comment) The first 2 calls to LOG_TEST() do show the thread name, but subsequent calls from other threads only show [#1] or [#2] which I believe is an indication that it crashed or terminated? What am I doing wrong? |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 5 replies
-
Hey, make sure to set your thread name once when the thread starts and before issuing any log statements from that thread. The thread name is retrieved and cached on the first log statement for that thread. The function Once the thread name is set, it will be accessible in the sink, where you can include it in your log entries. quill/include/quill/sinks/JsonFileSink.h Line 63 in 62450f9 if you still have issues you can submit a minimal example and i can help |
Beta Was this translation helpful? Give feedback.
-
note: thread name on linux is max 15 chars #include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogMacros.h"
#include "quill/Logger.h"
#include "quill/sinks/JsonConsoleSink.h"
#include <string>
class CustomJsonConsoleSink : public quill::StreamSink
{
public:
CustomJsonConsoleSink() : StreamSink("stdout", nullptr) {}
~CustomJsonConsoleSink() override = 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,
std::string_view, std::string_view) override
{
_json_message.clear();
char const* message_format;
if (strchr(log_metadata->message_format(), '\n') != nullptr)
{
_format = log_metadata->message_format();
for (size_t pos = 0; (pos = _format.find('\n', pos)) != std::string::npos; pos++)
{
_format.replace(pos, 1, " ");
}
message_format = _format.data();
}
else
{
message_format = log_metadata->message_format();
}
// !! - Add thread_name to json
_json_message.append(fmtquill::format(
R"({{"timestamp":"{}","file_name":"{}","line":"{}","thread_id":"{}",thread_name:"{}","logger":"{}","log_level":"{}","message":"{}")",
std::to_string(log_timestamp), log_metadata->file_name(), log_metadata->line(), thread_id,
thread_name, logger_name, log_level_description, message_format));
if (named_args)
{
for (auto const& [key, value] : *named_args)
{
_json_message.append(std::string_view{",\""});
_json_message.append(key);
_json_message.append(std::string_view{"\":\""});
_json_message.append(value);
_json_message.append(std::string_view{"\""});
}
}
_json_message.append(std::string_view{"}\n"});
StreamSink::write_log(log_metadata, log_timestamp, thread_id, thread_name, process_id, logger_name, log_level,
log_level_description, log_level_short_code, named_args, std::string_view{},
std::string_view{_json_message.data(), _json_message.size()});
}
private:
fmtquill::memory_buffer _json_message;
std::string _format;
};
int main()
{
// Start the backend thread
quill::BackendOptions backend_options;
quill::Backend::start(backend_options);
// Frontend
// Create a json sink
auto json_sink = quill::Frontend::create_or_get_sink<CustomJsonConsoleSink>("json_sink_1");
// Create a logger
// When logging json, it is ideal to set the logging pattern to empty to avoid unnecessary message formatting.
quill::Frontend::create_or_get_logger(
"json_logger", std::move(json_sink),
quill::PatternFormatterOptions{"", "%H:%M:%S.%Qns", quill::Timezone::GmtTime});
auto t1 = std::thread([]() {
quill::Logger* logger = quill::Frontend::get_logger("json_logger");
// ideally use your own implementation to set the thread nae
quill::detail::set_thread_name("t1_thread");
LOG_INFO(logger, "A json message {var_1}", "test");
LOG_INFO(logger, "A json message {var_1}", "test2");
LOG_INFO(logger, "A json message {var_1}", "test3");
});
auto t2 = std::thread([]() {
quill::Logger* logger = quill::Frontend::get_logger("json_logger");
// ideally use your own implementation to set the thread nae
quill::detail::set_thread_name("t2_thread");
LOG_INFO(logger, "A json message {var_1}", "bar");
LOG_INFO(logger, "A json message {var_1}", "bar2");
LOG_INFO(logger, "A json message {var_1}", "bar3");
});
t1.join();
t2.join();
} |
Beta Was this translation helpful? Give feedback.
-
It is not possible to run it without the logging thread, the whole architecture of the library is designed around async logging Not sure I understand what you would like to achieve with the thread locals… you could just pass them to each log statement, that is the same as MDC but with additional typing eg LOG_INFO(logger, “{t}”, threadLocal) the best way to know what each thread calls is to create a Logger with a unique name per thread and pass the Logger* to the functions you are calling from that thread Logger instance is a cheap object even if you create thousay is fine |
Beta Was this translation helpful? Give feedback.
-
This is an example of how you can track it with Logger instances #include "quill/Backend.h"
#include "quill/Frontend.h"
#include "quill/LogMacros.h"
#include "quill/Logger.h"
#include "quill/sinks/JsonConsoleSink.h"
#include <string>
class CustomJsonConsoleSink : public quill::StreamSink
{
public:
CustomJsonConsoleSink() : StreamSink("stdout", nullptr) {}
~CustomJsonConsoleSink() override = 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,
std::string_view, std::string_view) override
{
_json_message.clear();
char const* message_format;
if (strchr(log_metadata->message_format(), '\n') != nullptr)
{
_format = log_metadata->message_format();
for (size_t pos = 0; (pos = _format.find('\n', pos)) != std::string::npos; pos++)
{
_format.replace(pos, 1, " ");
}
message_format = _format.data();
}
else
{
message_format = log_metadata->message_format();
}
// !! - Add thread_name to json
_json_message.append(fmtquill::format(
R"({{"logger":"{}","message":"{}")",logger_name, message_format));
if (named_args)
{
for (auto const& [key, value] : *named_args)
{
_json_message.append(std::string_view{",\""});
_json_message.append(key);
_json_message.append(std::string_view{"\":\""});
_json_message.append(value);
_json_message.append(std::string_view{"\""});
}
}
_json_message.append(std::string_view{"}\n"});
StreamSink::write_log(log_metadata, log_timestamp, thread_id, thread_name, process_id, logger_name, log_level,
log_level_description, log_level_short_code, named_args, std::string_view{},
std::string_view{_json_message.data(), _json_message.size()});
}
private:
fmtquill::memory_buffer _json_message;
std::string _format;
};
void mul(quill::Logger* logger, size_t a, size_t b)
{
LOG_INFO(logger, "{a} * {b} = {c}", a, b, a * b);
}
void add(quill::Logger* logger, size_t a, size_t b)
{
LOG_INFO(logger, "{a} + {b} = {c}", a, b, a + b);
mul(logger, a, b);
}
int main()
{
// Start the backend thread
quill::BackendOptions backend_options;
quill::Backend::start(backend_options);
// Create a json sink
auto json_sink = quill::Frontend::create_or_get_sink<CustomJsonConsoleSink>("json_sink_1");
std::vector<std::thread> threads;
for (size_t i = 0; i < 10; ++i)
{
threads.emplace_back([i]()
{
// setup once when the thread starts ..
// get existing sink
auto json_sink = quill::Frontend::get_sink("json_sink_1");
// create logger
quill::Logger* logger = quill::Frontend::create_or_get_logger(
"thread_" + std::to_string(i) , std::move(json_sink),
quill::PatternFormatterOptions{"", "%H:%M:%S.%Qns", quill::Timezone::GmtTime});
// thread main loop ..
for (size_t j = 0; j < 10; ++j)
{
add(logger, j, j);
}
// when thread exits ..
quill::Frontend::remove_logger(logger);
});
}
// simulate program run..
std::this_thread::sleep_for(std::chrono::seconds{10});
for (auto& thread : threads)
{
thread.join();
}
} |
Beta Was this translation helpful? Give feedback.
This is an example of how you can track it with Logger instances