-
Notifications
You must be signed in to change notification settings - Fork 4.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Enhancement] Option to log thread name #686
Comments
Thanks. Looks useful. Few suggestions:
|
I think I understand what you mean. I have made those changes (as I understood them) in my codebase. It is tested in Linux again and pushed to my copy of your repo https://github.com/nuertey/spdlog. If my understanding of your suggestions are accurate, consequences of these changes are :
Let me know if I misunderstood or missed anything. |
I wouldn't change the %t flag meaning. We should maintain backward compatible. I would add a %q flag. Also, maybe bench to see if storing once the thread name in thread local storage improves performance instead of querying the thread name for each call. |
Definitely, it makes sense that the thread local storage of the thread name would be faster than making the call on each log. I haven't had time to benchmark both options but because it makes obvious sense what you suggest, I have made that change. |
I notice that suggested change uses GetThreadDescription on Windows. That only works on Windows 10, but I think there are a lot of people still using older versions of Windows. My suggestion would be to create a spdlog-specific call to set the thread name, which just stores the passed-in value in a thread-local string. This would have a few benefits:
|
Arthur-tacca, I think the bigger issue as you probably sensed yourself is this begins to lead to issue #617 and whether we really should generalize these potential extra diagnostic info. What issue #617 seems to be proposing is rather like log4cxx's Mapped Diagnostic Context. It is a per-thread scheme used to distinguish interleaved log output from different sources. See their documentation here : https://svn.apache.org/repos/asf/logging/site/trunk/docs/log4cxx/apidocs/classlog4cxx_1_1_m_d_c.html and accompanying format specifiers (aka conversion characters) here : https://svn.apache.org/repos/asf/logging/site/trunk/docs/log4cxx/apidocs/classlog4cxx_1_1_pattern_layout.html I think we have to be very careful to not introduce a correct but unwieldy solution that might affect the performance bottom-line of spdlog. It would be interesting to assess how many folks out there do really need such a generic solution and if not, what performance costs they are willing to incur for having that general solution. Having said that, certainly what you do suggest is doable. And in fact before finding spdlog and its awesomeness, when I used to use log4cxx, I would use their MDC feature to achieve your exact aims in such a way :
I never delved into the MDC's implementation of log4cxx to assess its true costs though so take my comment with a caveat. |
+1 on this one -- being able to name the threads (via |
FYI, spdlog now have an option to customize pattern flags. This makes it possible to customize the %t flag to display thread name. |
Good to know. Excellent work. |
@nuertey I am on Windows OS. When I replaced the original .h files with the three changed .h files, there was an error saying sys/prctl.h not found in my Qt Creator IDE. I know that file is for Unix-like OSes, but there were more errors after I commented out that include. Please advise how to solve this problem. Thank you. |
@swimmingmachine :) gosh it has been 3 years since that my thread naming enhancement to spdlog. I am not even sure I have access to my original workspace still. And note that @gabime may have modified the APIs and enhanced things further since then. So reach out to @gabime and let him suggest how best to accomplish your aims with his latest code. Due to being severely entrenched in other projects, I am out of touch with spdlog. Thanks for reaching out though. So let us wait till @gabime comments. He is the original author of excellent spdlog... so he will have better info for you than me. |
@swimmingmachine Please see the Wiki for how to create a custom formatter. If you need the thread name, get it in your custom formatter. spdlog/include/spdlog/details/log_msg-inl.h Lines 15 to 25 in 0ca574a
spdlog/include/spdlog/details/os-inl.h Lines 365 to 374 in 0ca574a
The easiest way is to save the thread name in the Another way.
|
Hi @swimmingmachine, From that excerpt, you can see that you can use many different APIs to set and retrieve thread names; this goes to @tt4g tip about C++11 even. You can set and retrieve thread names via C++11, or even via any POSIX-supporting OS calls such as pthread_setname_np() or via prctl() or via GetThreadDescription() Windows-like API calls as pointed out by @tt4g. Here is the reference to C++11 'std::thread' :
Note that in my attached excerpt of those thread set and get implementations, I have omitted other implementations that are extraneous to your actual question; Implementations such as NonInterspersedLog<>, which I simply implemented like this below, in case you are curious: // Log colored output to the console but in a truly thread-safe and
// non-interleaved character way:
// Due to a more involved syntax when invoking template lambdas,
// rather prefer template function to a C++20 template lambda.
template <typename T, typename... Args>
inline void NonInterspersedLog(const std::string_view& logMessage,
const Args&... args)
{
static_assert((std::is_same_v<T, DebugLog_t>
|| std::is_same_v<T, TraceLog_t>
|| std::is_same_v<T, InfoLog_t>
|| std::is_same_v<T, ErrorLog_t>
|| std::is_same_v<T, WarnLog_t>
|| std::is_same_v<T, CriticalLog_t>),
"Hey! Logging category MUST be one of the following:\n\tDebugLog_t \
\n\tTraceLog_t \n\tInfoLog_t \n\tErrorLog_t \
\n\tWarnLog_t \n\tFatalLog_t");
char myThreadName[RECOMMENDED_BUFFER_SIZE];
GetThreadName(myThreadName, sizeof(myThreadName));
if constexpr (std::is_same_v<T, DebugLog_t>)
{
std::ostringstream oss;
oss << Color::FG_LIGHT_BLUE << "[" << myThreadName << "] "
<< Color::FG_LIGHT_CYAN << "{" << TypeName<T>() << "}: "
<< Color::FG_DEFAULT << "\"" << logMessage << "\"";
((oss << ' ' << args), ...);
// Ensure that even the newlines are properly output without
// being incorrectly interspersed.
std::string logString = oss.str() + "\n";
std::cout << logString;
}
else if constexpr (std::is_same_v<T, TraceLog_t>)
{
std::ostringstream oss;
oss << Color::FG_LIGHT_BLUE << "[" << myThreadName << "] "
<< Color::FG_MAGENTA << "{" << TypeName<T>() << "}: "
<< Color::FG_DEFAULT << "\"" << logMessage << "\"";
((oss << ' ' << args), ...);
// Ensure that even the newlines are properly output without
// being incorrectly interspersed.
std::string logString = oss.str() + "\n";
std::cout << logString;
}
else if constexpr (std::is_same_v<T, InfoLog_t>)
{
std::ostringstream oss;
oss << Color::FG_LIGHT_BLUE << "[" << myThreadName << "] "
<< Color::FG_GREEN << "{" << TypeName<T>() << "}: "
<< Color::FG_DEFAULT << "\"" << logMessage << "\"";
((oss << ' ' << args), ...);
// Ensure that even the newlines are properly output without
// being incorrectly interspersed.
std::string logString = oss.str() + "\n";
std::cout << logString;
}
else if constexpr (std::is_same_v<T, ErrorLog_t>)
{
std::ostringstream oss;
oss << Color::FG_LIGHT_BLUE << "[" << myThreadName << "] "
<< Color::FG_LIGHT_RED << "{" << TypeName<T>() << "}: "
<< Color::FG_DEFAULT << "\"" << logMessage << "\"";
((oss << ' ' << args), ...);
// Ensure that even the newlines are properly output without
// being incorrectly interspersed.
std::string logString = oss.str() + "\n";
std::cerr << logString;
}
else if constexpr (std::is_same_v<T, WarnLog_t>)
{
std::ostringstream oss;
oss << Color::FG_LIGHT_BLUE << "[" << myThreadName << "] "
<< Color::FG_YELLOW << Color::FT_BOLD << "{" << TypeName<T>() << "}: "
<< Color::FG_DEFAULT << "\"" << logMessage << "\"";
((oss << ' ' << args), ...);
// Ensure that even the newlines are properly output without
// being incorrectly interspersed.
std::string logString = oss.str() + "\n";
std::cerr << logString;
}
else if constexpr (std::is_same_v<T, CriticalLog_t>)
{
std::ostringstream oss;
oss << Color::FG_LIGHT_BLUE << "[" << myThreadName << "] "
<< Color::FG_RED << Color::FT_BOLD << "{" << TypeName<T>() << "}: "
<< Color::FG_DEFAULT << "\"" << logMessage << "\"";
((oss << ' ' << args), ...);
// Ensure that even the newlines are properly output without
// being incorrectly interspersed.
std::string logString = oss.str() + "\n";
std::cerr << logString;
}
} Hope all these informations help. Enjoy. Here is the link to my several implementations of thread name sets and gets: https://github.com/nuertey/RandomArtifacts/blob/master/thread_name_answer.cpp |
And @swimmingmachine, inline constexpr size_t THREAD_NAME_LENGTH = 16; // Length is restricted by the OS. Includes null-termination.
inline constexpr size_t RECOMMENDED_BUFFER_SIZE = 20; // For retrievals; must be at least 16; OS will null-terminate. Regards |
Thank you both! I will give it a try. |
Awesome logging library. Thanks. I added a little code to it in order to be able to log thread names for a personal project. I am sharing so if others find it useful it can make it into the library. I tested the Linux portion and it works. I don't have access to a Windows machine so could not test that part.
Code located at : https://github.com/nuertey/spdlog
The text was updated successfully, but these errors were encountered: