Using post #11 of http://sourceforge.net/projects/boost-log/forums/forum/710022/topic/3604044 as my guide, I thought I would go about adding __LINE__ and __FILE__ to my logs (A useful feature indeed). I had already been using named scopes with the severity logger to give us a better view at critical log entries. As such I added a new attribute (FileAndLine) identical to my previous Scope Attribute.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Anonymous
-
2012-07-18
As an additional note, I thought perhaps I ought to replace
fmt::named_scope("FileAndLine")
with
logging::FileAndLineFormatter("FileAndLine")
But that leaves me with the following compile-time error:
boost/log/formatters/wrappers.hpp:74: error: no match for 'operator<<' in 'strm << ((const boost::log_mt_posix::formatters::fmt_wrapper<char, logging::FileAndLineFormatter>*)this)->boost::log_mt_posix::formatters::fmt_wrapper<char, logging::FileAndLineFormatter>::m_T'
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
In the first code snippet, you didn't use FileAndLineFormatter in the formatting expression. The register_formatter_factory function call registers the factory to be used when formatter is parsed from a string (like when loading from a settings file) and this is not the case. That's why FileAndLineFormatter is not used.
When you use FileAndLineFormatter in the formatter expression the compilation fails because it doesn't derive from basic_formatter class template. The library interprets it as an object to be output when formatting a record and it doesn't have operator<<.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Anonymous
-
2012-07-19
Ah, of course. That makes sense. When I first set the logger up, I was using configuration files and only recently switched. Thank you for pointing that out.
For those who read this thread in the future and want an explicit answer, the updated (and now working) code is included below:
I have a format factory for this object. Using the string formatter syntax '%MySuperCustomObject% works.
But I would prefer to use the formatter expression syntax.
Using Boost version 1.79.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
First, you don't derive from basic_formatter. It is not a base class but a wrapper that erases the type of the actual formatter, a-la std::function with a fixed signature.
Next, there are multiple ways to integrate with formatting expressions, but ultimately what you need to do is to turn your formatter function into a Boost.Phoenix terminal. One easy way to do this is to use wrap_formatter:
Hi, is using the expr::wrap_formatter in multi-threaded log (boost::log::sources::severity_logger_mt) thread safe?
Yes, meaning that wrap_formatter itself does not introduce data races as it doesn't modify any shared data. The wrapped function though is not protected from concurrent calls.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Andrey, can I get your opinion on a post I saw. Is this an OK idea or a bad idea. It's along the lines of this conversation of how to get file information into the log and it needs to be available for filtering records to boost::log::add_value will not work.
I don't think this is a good idea because you are copying thread-specific attributes and searching for the attribute - twice - per every log record being made. Copying an attribute set involves one or more memory allocations, depending on the number of attributes.
Furthermore, passing the result of set_get_attrib to BOOST_LOG_STREAM_WITH_PARAMS is not intended to work. The list is supposed to be a list of named parameters that is composed onto named parameters pack that is then consumed by open_record method of the logger. Since set_get_attrib returns just an attribute value, and there isn't an operator, overload that takes the value and a named parameter, the built-in operator, discards the attribute value. This will break if such an operator appears in Boost.Parameter in the future (and such an extension would be reasonable since, as I said, you are not supposed to mix named parameters and other random values in the comma-separated parameter list).
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Do you have a suggestion on how to get FILE for every place the logger is called, so it can participate in filtering?
For every "BOOST_LOG_SEV(logger, severity) << msg;" statement in the code we need FILE so we can filter on filename and severity. This needs to be the exact location of the log message so scoping variables will not work.
This is an ongoing question for the logger that no one seems to have a good solution for. Any help is greatly appreciated.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I answered it here. Since you need filtering, option 3 fits your case.
One other option is to define a new logger feature that will manage the file and line attributes. This would have better performance since the attributes wouldn't be added/removed on every log record. But this would still require defining a new logging macro since this is the only way to use __FILE__ and __LINE__ macros. You can read about creating logger features here.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This is off-topic but BOOST_LOG_SCOPED_LOGGER_TAG does not introduce a data race if used with a multi-threaded logger. The backtrace makes no sense to me as Boost.Log doesn't use Boost.Function. Looks like TSan is triggering a race on the virtual function table pointer. This can happen if your code is destroying the attribute while a method is being called on it.
Last edit: Andrey Semashev 2022-09-10
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The test is multi-threaded but it does not use any of the BOOST macros in any of the threads except the main thread. AFAIK it is the BOOST macros that are not thread safe. Only calls to BOOST_LOG_SCOPED_LOGGER_TAG and BOOST_LOG_SEV(logger, severity) << msg are made in the threads. It's the call to expr::stream << expr::attr< std::string >("Tag") that causes the data race. I don't see how the code could be destroying the Tag attribute while some place else is updating it. The Tag attribute for this test is only set in 2 places.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Compile with clang 14 and the thread sanitizer enabled on Ubuntu 22.04.
Run the program a few times and you should encounter the data race.
SUMMARY: ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call) /usr/local/include/boost/log/attributes/attribute_value_impl.hpp:49:7 in boost::log::v2_mt_posix::attributes::attribute_value_impl<std::__cxx11::basic_string\<char, std::char_traits\<char="">, std::allocator\<char> > >::~attribute_value_impl()</char></std::__cxx11::basic_string\<char,>
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Sorry about the off topic post, but it did relate to an earlier post about using the wrap_formatter and getting a race condition. I upgraded to 1.80 and still can reproduce the data race using log/example/advanced_usage/main.cpp with some minor modifications described above.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Did you build Boost with TSan? What compiler flags did you use? What exact source code, with everything irrelevant removed, reproduces it? Please post in another thread.
Last edit: Andrey Semashev 2022-09-14
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Using post #11 of http://sourceforge.net/projects/boost-log/forums/forum/710022/topic/3604044 as my guide, I thought I would go about adding __LINE__ and __FILE__ to my logs (A useful feature indeed). I had already been using named scopes with the severity logger to give us a better view at critical log entries. As such I added a new attribute (FileAndLine) identical to my previous Scope Attribute.
While it compiles and runs, it seems to skip the custom formatter completely, and instead I end up with two copies of the scope attribute. I compared my code to http://boost-log.sourceforge.net/libs/log/doc/html/log/extension/settings.html#log.extension.settings.adding_support_for_user_defined_types_to_the_formatter_parser but I cannot seem to find the missing piece. Given the amount of time I have spent going nowhere, I am willing to bet that I am merely overlooking something simple and need an outside voice. Thank you for your help in advance.
The code is in a header-only file to make it easier for me to use in multiple projects. I have included what I believe to be the relevant parts here:
As an additional note, I thought perhaps I ought to replace
with
But that leaves me with the following compile-time error:
In the first code snippet, you didn't use FileAndLineFormatter in the formatting expression. The register_formatter_factory function call registers the factory to be used when formatter is parsed from a string (like when loading from a settings file) and this is not the case. That's why FileAndLineFormatter is not used.
When you use FileAndLineFormatter in the formatter expression the compilation fails because it doesn't derive from basic_formatter class template. The library interprets it as an object to be output when formatting a record and it doesn't have operator<<.
Ah, of course. That makes sense. When I first set the logger up, I was using configuration files and only recently switched. Thank you for pointing that out.
For those who read this thread in the future and want an explicit answer, the updated (and now working) code is included below:
That should have been basic_formatter<char, FileAndLineFormatter>.
Is it possible to get a working solution posted for this question?
I have a similar issue and cannot get the formatter to compile when used as an expression.
I would like to use this syntax :
but cannot figure out what replaces '????'.
I have a format factory for this object. Using the string formatter syntax '%MySuperCustomObject% works.
But I would prefer to use the formatter expression syntax.
Using Boost version 1.79.
First, you don't derive from
basic_formatter
. It is not a base class but a wrapper that erases the type of the actual formatter, a-lastd::function
with a fixed signature.Next, there are multiple ways to integrate with formatting expressions, but ultimately what you need to do is to turn your formatter function into a Boost.Phoenix terminal. One easy way to do this is to use
wrap_formatter
:Thank you. I was able to get the expr::wrap_formatter to work.
Hi, is using the expr::wrap_formatter in multi-threaded log (boost::log::sources::severity_logger_mt) thread safe?
I am running the clang thread sanitizer (https://clang.llvm.org/docs/ThreadSanitizer.html) and getting many data race conditions reported.
Thanks
Yes, meaning that
wrap_formatter
itself does not introduce data races as it doesn't modify any shared data. The wrapped function though is not protected from concurrent calls.Andrey, can I get your opinion on a post I saw. Is this an OK idea or a bad idea. It's along the lines of this conversation of how to get file information into the log and it needs to be available for filtering records to boost::log::add_value will not work.
From post:
https://stackoverflow.com/questions/24750218/boost-log-to-print-source-code-file-name-and-line-number
Thanks in advance for your input.
I don't think this is a good idea because you are copying thread-specific attributes and searching for the attribute - twice - per every log record being made. Copying an attribute set involves one or more memory allocations, depending on the number of attributes.
Furthermore, passing the result of
set_get_attrib
toBOOST_LOG_STREAM_WITH_PARAMS
is not intended to work. The list is supposed to be a list of named parameters that is composed onto named parameters pack that is then consumed byopen_record
method of the logger. Sinceset_get_attrib
returns just an attribute value, and there isn't anoperator,
overload that takes the value and a named parameter, the built-inoperator,
discards the attribute value. This will break if such an operator appears in Boost.Parameter in the future (and such an extension would be reasonable since, as I said, you are not supposed to mix named parameters and other random values in the comma-separated parameter list).Ok, I thought it was going to be a bad idea.
Do you have a suggestion on how to get FILE for every place the logger is called, so it can participate in filtering?
For every "BOOST_LOG_SEV(logger, severity) << msg;" statement in the code we need FILE so we can filter on filename and severity. This needs to be the exact location of the log message so scoping variables will not work.
This is an ongoing question for the logger that no one seems to have a good solution for. Any help is greatly appreciated.
I answered it here. Since you need filtering, option 3 fits your case.
One other option is to define a new logger feature that will manage the file and line attributes. This would have better performance since the attributes wouldn't be added/removed on every log record. But this would still require defining a new logging macro since this is the only way to use
__FILE__
and__LINE__
macros. You can read about creating logger features here.Thank you. I missed that post and option 3.
I do have a concern about using BOOST_LOG_SCOPED_LOGGER_ATTR.
I currently use this for setting a TAG field but get get thread sanitizer data race errors.
I'm using "boost::log::sources::severity_logger_mt<>".
I do the following in a few different places.
BOOST_LOG_SCOPED_LOGGER_TAG(lg, "Tag", "some-tag-message");
I then run a multi-threaded test program using the clang thread sanitizer and get a data race on the Tag output.
Is this a known issue? Is there a way to work around it?
Thanks
This is off-topic but
BOOST_LOG_SCOPED_LOGGER_TAG
does not introduce a data race if used with a multi-threaded logger. The backtrace makes no sense to me as Boost.Log doesn't use Boost.Function. Looks like TSan is triggering a race on the virtual function table pointer. This can happen if your code is destroying the attribute while a method is being called on it.Last edit: Andrey Semashev 2022-09-10
I just noticed that the backtrace mentions Boost.Test. AFAIK, Boost.Test is not thread-safe, it should not be used from multiple threads.
The test is multi-threaded but it does not use any of the BOOST macros in any of the threads except the main thread. AFAIK it is the BOOST macros that are not thread safe. Only calls to BOOST_LOG_SCOPED_LOGGER_TAG and BOOST_LOG_SEV(logger, severity) << msg are made in the threads. It's the call to expr::stream << expr::attr< std::string >("Tag") that causes the data race. I don't see how the code could be destroying the Tag attribute while some place else is updating it. The Tag attribute for this test is only set in 2 places.
I'm wondering if we do not have the logger setup correctly to support a multithreaded environment.
Usage:
BOOST_LOG_SCOPED_LOGGER_TAG(boostLogger::get(), "Tag", "sometagmessage");
BOOST_LOG_SCOPED_LOGGER_TAG is called on the main thread and one other thread.
Usage:
BOOST_LOG_SEV(boostLogger::get(), sev) << msg;
This is called in many different threads.
After multiple executions of the code, we always encounter a data race on:
expr::stream << " [TAG: " << expr::attr< std::string >("Tag") << "]"
Do not initialize anything but the logger itself in the global logger initializer. It is not guaranteed that the initializer is only run once.
Hi, to show the data race I modified the following example program:
boost_1_77_0/libs/log/example/advanced_usage/main.cpp
I added the following functions:
At the end of main.cpp, before the call to pSink->reset_filter(), I added the following code.
Compile with clang 14 and the thread sanitizer enabled on Ubuntu 22.04.
Run the program a few times and you should encounter the data race.
SUMMARY: ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call) /usr/local/include/boost/log/attributes/attribute_value_impl.hpp:49:7 in boost::log::v2_mt_posix::attributes::attribute_value_impl<std::__cxx11::basic_string\<char, std::char_traits\<char="">, std::allocator\<char> > >::~attribute_value_impl()</char></std::__cxx11::basic_string\<char,>
I also had to change
slg
toseverity_logger_mt
. I cannot reproduce this on the current develop branch on my Ubuntu 22.04 and clang 14.Also, please don't post off-topic in this thread. This discussion is about
__FILE__
and__LINE__
formatting.Last edit: Andrey Semashev 2022-09-13
Sorry about the off topic post, but it did relate to an earlier post about using the wrap_formatter and getting a race condition. I upgraded to 1.80 and still can reproduce the data race using log/example/advanced_usage/main.cpp with some minor modifications described above.
Did you build Boost with TSan? What compiler flags did you use? What exact source code, with everything irrelevant removed, reproduces it? Please post in another thread.
Last edit: Andrey Semashev 2022-09-14