
Hello Christian, I think that I start to understand your concern. We need to see which is the minimal interface a log library must have in order to be be used by a library allowing user customizations. I dont know why a thought that this interface was already provided by the Boost.logging library, and maybe it was in his first version, but it is sure that it isnot the case for log2. From: "Christian Holmquist" <c.holmquist@gmail.com> To: <boost@lists.boost.org> Subject: Re: [boost] thoughts on logging library: orthogonal runtimeconfiguration
On 09/04/2008, vicente.botet <vicente.botet@wanadoo.fr> wrote:
From: "Christian Holmquist" <c.holmquist@gmail.com> Subject: [boost] thoughts on logging library
<snip>
#define BOOST_ASIO_LOG_ENABLE #define BOOST_ASIO_LOG_MIN_LEVEL boost::log::level::debug #include <boost/asio/asio.hpp>
I'm not sure it is a good idea to place these macros in the source code files. First you can define the macros on several header files, needing some test undef and so one. Which will be the result when two source files (.cpp) defines different configurations? IMO the macros you purpose should be defined at the build level of the executable.
Of course. It was only an example of the defines.
I prefer :)
I really think that we need runtime configuration and I suspect that this configuration should be centralized. But I don't think that this is a particular case of the logging library. logging and configuration are orthogonal. A runtime configuration library will be welcome and should be able to configure the asio log level as well as other configurable features as for example the boost::log::level of a global log.
Let me show how I will use the current Boost.Log library interface if I were the maintainer of asio library. I will define un internal macro like that:
#ifdef BOOST_ASIO_LOG_ENABLE #define BOOST_ASIO_LOG(LEVEL, MSG) \ if (boost::asio::log_level <= LEVEL) { \ BOOST_LOG(LEVEL) << "[asio] " << MSG \ } #else #define BOOST_ASIO_LOG(LEVEL, MSG) #endif
I will place the definition of the boost::asio::log_level variable in a specific header file, which should be included by the user in only one comilation unit.
// boost/asio/log_level_def.hpp
#ifndef BOOST_ASIO_LOG_LEVEL_DEF__HPP #define BOOST_ASIO_LOG_LEVEL_DEF__HPP
#ifndef BOOST_ASIO_LOG_LEVEL_DEFAULT #define BOOST_ASIO_LOG_LEVEL_DEFAULT boos::log::level::debug #endif
#include <boost/asio/log_level_fwd.hpp>
namespace boost { namespace asio {
atomic_log_level_type log_level=BOOST_ASIO_LOG_LEVEL_DEFAULT;
} }
#endif
Sure, this could work, but I think asio::log_level should be a function returning the current level instead of a global variable. Then it's up to the user to choose where from this variable is read and how it's updated. There may be more things to consider, again something for a log library to decide best practice.
Why? Do you mean that you prefer if (asio::log_level()) ... and asio::log_level() = debug to if (asio::log_level) ... and asio::log_level = debug ? Why asio::log_level should not be an object?
As the boost::asio::log_level reading and writing could be done by different threads the read and write operations must be atomics. So atomic_log_level_type must be convertible to boost::log::level and must is assignable from boost::log::level atomically.
typedef atomic<boost::log::level> atomic_log_level_type ;
An other history is how can we configure the boost::asio::log_level variable at runtime?
Well this can be done in a lot of ways. But at the end what you realy need is to do a the following
boost::asio::log_level = new_value;
Is for this raison that the atomic_log_level_type assignation operator must be atomic.
We can use also a configurator class which store a mapping between some key and a reference of such variables.
rtc::runtime_configurator<std::string, atomic_log_level_type&> conf;
Note the mapped type is a reference to the real atomic_log_level_type. The advantage is that now the owner of the configured variable do not depends on how this variable is configured. It is up to the user to make the association between the configured variable and the configurator. This runtime_configurator could be based on the property_tree library.
The user can add the configurable variables as follows
conf.register("boost.asio.log_level", boost::asio::log_level);
The runtime configuration library can provide a callable backdoor to configure any registered variable using cli, as for example
$$ push boost $$ push asio $$ log_level = debug $$ log_level $$ pop $$ asio.log_level = debug $$ asio.log_level
The push and pop command are used to change the context. the var = value command assign the value, and the variable command prints its value
This backdoor will look up the corresponding configurable variable and read o write depending on the command.
To allow symbolic values wee need manage with the conversion from std::string to boost::log::level and vice versa. We can use the lexical_cast library for this purpose as soon as the we have the output and input operators defined for boost::log::level.
If the register is done at initialization time before the backdoor thread is lunched, the runtime_configurator instance will be used only for reads, so no thread safety problem at the map or tree_map level.
Then the user can start the backdoor as follows boost::thread configurator_backdoor(rtc::backdoor)
The user can provide its own external configuration means with a graphical interface if he consider convenient.
Do you think that a such runtime configurator variables library has a place in Boost?
I haven't thought about it, but it sounds useful. It's probably an often reinvented wheel.
I thougth that Boost was there exacltly for that. To avoid that every one reinvent the wheel. Am I wrong? Isn't the Boost.Log library a wheel reinvented by every body?
This could be a complement to a larger configuration library starting from program_options, extending to property_tree storage and making the CLI a specific parser, other parsers can be reading from some configuration file formats, the backdoor I'm proposing or other as has been already proposed in this mailing list (CGI parameter parser).
<snip>
Configuring the above with a compiled library like boost serialization is an interesting task as well, that I definitely think should be addressed. could be that the flexibility decreases some in such a scenario, or it could be configued in site-config.jam or whatever.
Once the serialization library provide traces or log, if you want to use the serialization library with logs you should add a define at compile time e.g. -DBOOST_SERIALIZATION_LOG_ENABLE and link with the serialization logged variant e.g. -lboost_lserialization.
Agreed. Should the maintainer of serialization invent his own build/jam/config thingie to make this possible? I think not.
Please, could you be more precise? What do you propose instead?
Is this along the line what boost.log has in mind, or will it be a logging library not to be used by the boost libraries themselves?
I think that this has nothing to be with the Boost.Log library. It is up to the other Boost libraries to use or not the loging library.
Sorry, I disagree, I think that it has everything to do with the log library. It should show how boost libraries should use the library to make them customizable for end users of boost.
As I said I think that I start to understand your concern. We need to see which is the minimal interface a log library must have in order to be be used by a library allowing user customizations. Once this minimal interface will be clear I think that if a library provides logs, it is up to the library autor to describe how to configure it. Isn't it? The role of a Boost.Configuration library will be to provide generic mechanisms to configure applications and of course libraries.
The single flaw is that these libraries either must use a single global variable log or extend their interface with a log parameter. This problem has already been discused during the Boost.Singleton review.
Some boost libraries already use singletons. Religious views on globally accessible data aside, what would be the flaw with a single log instance?
I have no particular problem, I'm not the maintainer of any Boost library :). I'm not so sure that there will be a lot of maintainers that like to use a external global variable, or even add to his library some logs. Are there some Boost maintaniers that plans to add log support for his library once the Boost.Library is accepted? Any way and independently of which library wil have logs, as you have stated from the begining, we need to define the minimal interface a logging library must provide. What are really these minimal features? * It is clear that the instantiation of the log and its configuration is reserved to the application. * Should work for example with the std::cerr stream. * As the minimal logging library do not provide configuration, the output for the default log will have a predefined format. * Logiging client libraries should be able to be configured Boost::logging logs and filters Comments? Best _____________________ Vicente Juan Botet Escriba