2 * Copyright Andrey Semashev 2007 - 2015.
3 * Distributed under the Boost Software License, Version 1.0.
4 * (See accompanying file LICENSE_1_0.txt or copy at
5 * http://www.boost.org/LICENSE_1_0.txt)
8 * \file formatter_parser.cpp
9 * \author Andrey Semashev
12 * \brief This header is the Boost.Log library implementation, see the library documentation
13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
16 #ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS
18 #include <boost/log/detail/setup_config.hpp>
23 #include <boost/assert.hpp>
24 #include <boost/bind.hpp>
25 #include <boost/move/core.hpp>
26 #include <boost/move/utility_core.hpp>
27 #include <boost/optional/optional.hpp>
28 #include <boost/utility/in_place_factory.hpp>
29 #include <boost/log/expressions/formatter.hpp>
30 #include <boost/log/attributes/attribute_name.hpp>
31 #include <boost/log/exceptions.hpp>
32 #include <boost/log/detail/singleton.hpp>
33 #include <boost/log/detail/code_conversion.hpp>
34 #include <boost/log/detail/default_attribute_names.hpp>
35 #include <boost/log/utility/formatting_ostream.hpp>
36 #include <boost/log/utility/functional/nop.hpp>
37 #include <boost/log/utility/setup/formatter_parser.hpp>
38 #if !defined(BOOST_LOG_NO_THREADS)
39 #include <boost/log/detail/locks.hpp>
40 #include <boost/log/detail/light_rw_mutex.hpp>
42 #include "parser_utils.hpp"
43 #include "spirit_encoding.hpp"
44 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
45 #include "default_formatter_factory.hpp"
47 #include <boost/log/detail/header.hpp>
51 BOOST_LOG_OPEN_NAMESPACE
53 BOOST_LOG_ANONYMOUS_NAMESPACE
{
55 //! The structure contains formatter factories repository
56 template< typename CharT
>
57 struct formatters_repository
:
58 public log::aux::lazy_singleton
< formatters_repository
< CharT
> >
61 typedef log::aux::lazy_singleton
< formatters_repository
< CharT
> > base_type
;
63 #if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS)
64 friend class log::aux::lazy_singleton
< formatters_repository
< CharT
> >;
66 friend class base_type
;
69 typedef CharT char_type
;
70 typedef formatter_factory
< char_type
> formatter_factory_type
;
72 //! Attribute name ordering predicate
73 struct attribute_name_order
75 typedef bool result_type
;
76 result_type
operator() (attribute_name
const& left
, attribute_name
const& right
) const
78 return left
.id() < right
.id();
82 //! Map of formatter factories
83 typedef std::map
< attribute_name
, shared_ptr
< formatter_factory_type
>, attribute_name_order
> factories_map
;
86 #if !defined(BOOST_LOG_NO_THREADS)
87 //! Synchronization mutex
88 mutable log::aux::light_rw_mutex m_Mutex
;
90 //! The map of formatter factories
92 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
94 mutable aux::default_formatter_factory
< char_type
> m_DefaultFactory
;
97 //! The method returns the filter factory for the specified attribute name
98 formatter_factory_type
& get_factory(attribute_name
const& name
) const
100 typename
factories_map::const_iterator it
= m_Map
.find(name
);
101 if (it
!= m_Map
.end())
107 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
108 return m_DefaultFactory
;
110 BOOST_LOG_THROW_DESCR(setup_error
, "No formatter factory registered for attribute " + name
.string());
116 formatters_repository()
121 //! Function object for formatter chaining
122 template< typename CharT
, typename SecondT
>
123 struct chained_formatter
125 typedef void result_type
;
126 typedef basic_formatter
< CharT
> formatter_type
;
127 typedef typename
formatter_type::stream_type stream_type
;
129 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
130 explicit chained_formatter(formatter_type
&& first
, SecondT
&& second
) :
132 template< typename T
>
133 explicit chained_formatter(BOOST_RV_REF(formatter_type
) first
, T
const& second
) :
135 m_first(boost::move(first
)), m_second(boost::move(second
))
139 result_type
operator() (record_view
const& rec
, stream_type
& strm
) const
146 formatter_type m_first
;
150 //! String literal formatter
151 template< typename CharT
>
152 struct literal_formatter
154 typedef void result_type
;
155 typedef CharT char_type
;
156 typedef std::basic_string
< char_type
> string_type
;
157 typedef basic_formatting_ostream
< char_type
> stream_type
;
159 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
160 explicit literal_formatter(string_type
&& str
) : m_str(boost::move(str
))
162 explicit literal_formatter(string_type
const& str
) : m_str(str
)
167 result_type
operator() (record_view
const& rec
, stream_type
& strm
) const
173 const string_type m_str
;
176 //! Formatter parsing grammar
177 template< typename CharT
>
178 class formatter_parser
181 typedef CharT char_type
;
182 typedef const char_type
* iterator_type
;
183 typedef std::basic_string
< char_type
> string_type
;
184 typedef basic_formatter
< char_type
> formatter_type
;
185 typedef boost::log::aux::char_constants
< char_type
> constants
;
186 typedef typename
log::aux::encoding
< char_type
>::type encoding
;
187 typedef log::aux::encoding_specific
< encoding
> encoding_specific
;
188 typedef formatter_factory
< char_type
> formatter_factory_type
;
189 typedef typename
formatter_factory_type::args_map args_map
;
192 //! The formatter being constructed
193 optional
< formatter_type
> m_Formatter
;
196 attribute_name m_AttrName
;
197 //! Formatter factory arguments
198 args_map m_FactoryArgs
;
200 //! Formatter argument name
201 mutable string_type m_ArgName
;
203 mutable string_type m_ArgValue
;
212 void parse(iterator_type
& begin
, iterator_type end
)
214 iterator_type p
= begin
;
218 // Find the end of a string literal
219 iterator_type start
= p
;
220 for (; p
!= end
; ++p
)
223 if (c
== constants::char_backslash
)
225 // We found an escaped character
228 BOOST_LOG_THROW_DESCR(parse_error
, "Invalid escape sequence in the formatter string");
230 else if (c
== constants::char_percent
)
232 // We found an attribute
238 push_string(start
, p
);
242 // We found an attribute placeholder
243 iterator_type start
= constants::trim_spaces_left(++p
, end
);
244 p
= constants::scan_attr_placeholder(start
, end
);
246 BOOST_LOG_THROW_DESCR(parse_error
, "Invalid attribute placeholder in the formatter string");
248 on_attribute_name(start
, p
);
250 p
= constants::trim_spaces_left(p
, end
);
252 BOOST_LOG_THROW_DESCR(parse_error
, "Invalid attribute placeholder in the formatter string");
254 if (*p
== constants::char_paren_bracket_left
)
256 // We found formatter arguments
257 p
= parse_args(constants::trim_spaces_left(++p
, end
), end
);
258 p
= constants::trim_spaces_left(p
, end
);
260 BOOST_LOG_THROW_DESCR(parse_error
, "Invalid attribute placeholder in the formatter string");
263 if (*p
!= constants::char_percent
)
264 BOOST_LOG_THROW_DESCR(parse_error
, "Invalid attribute placeholder in the formatter string");
275 //! Returns the parsed formatter
276 formatter_type
get_formatter()
280 // This may happen if parser input is an empty string
281 return formatter_type(nop());
284 return boost::move(m_Formatter
.get());
288 //! The method parses formatter arguments
289 iterator_type
parse_args(iterator_type begin
, iterator_type end
)
291 iterator_type p
= begin
;
293 BOOST_LOG_THROW_DESCR(parse_error
, "Invalid attribute placeholder arguments description in the formatter string");
294 if (*p
== constants::char_paren_bracket_right
)
301 // Read argument name
302 iterator_type start
= p
;
303 if (!encoding::isalpha(*p
))
304 BOOST_LOG_THROW_DESCR(parse_error
, "Placeholder argument name is invalid");
305 for (++p
; p
!= end
; ++p
)
308 if (encoding::isspace(c
) || c
== constants::char_equal
)
310 if (!encoding::isalnum(c
) && c
!= constants::char_underline
)
311 BOOST_LOG_THROW_DESCR(parse_error
, "Placeholder argument name is invalid");
315 BOOST_LOG_THROW_DESCR(parse_error
, "Placeholder argument name is empty");
317 on_arg_name(start
, p
);
319 p
= constants::trim_spaces_left(p
, end
);
320 if (p
== end
|| *p
!= constants::char_equal
)
321 BOOST_LOG_THROW_DESCR(parse_error
, "Placeholder argument description is not valid");
323 // Read argument value
324 start
= p
= constants::trim_spaces_left(++p
, end
);
325 p
= constants::parse_operand(p
, end
, m_ArgValue
);
327 BOOST_LOG_THROW_DESCR(parse_error
, "Placeholder argument value is not specified");
331 p
= constants::trim_spaces_left(p
, end
);
333 BOOST_LOG_THROW_DESCR(parse_error
, "Invalid attribute placeholder arguments description in the formatter string");
336 if (c
== constants::char_paren_bracket_right
)
340 else if (c
== constants::char_comma
)
342 p
= constants::trim_spaces_left(++p
, end
);
344 BOOST_LOG_THROW_DESCR(parse_error
, "Placeholder argument name is invalid");
348 BOOST_LOG_THROW_DESCR(parse_error
, "Invalid attribute placeholder arguments description in the formatter string");
355 //! The method is called when an argument name is discovered
356 void on_arg_name(iterator_type begin
, iterator_type end
)
358 m_ArgName
.assign(begin
, end
);
361 //! The method is called when an argument is filled
364 m_FactoryArgs
[m_ArgName
] = m_ArgValue
;
369 //! The method is called when an attribute name is discovered
370 void on_attribute_name(iterator_type begin
, iterator_type end
)
373 BOOST_LOG_THROW_DESCR(parse_error
, "Empty attribute name encountered");
375 // For compatibility with Boost.Log v1 we recognize %_% as the message attribute name
376 const std::size_t len
= end
- begin
;
377 if (std::char_traits
< char_type
>::length(constants::message_text_keyword()) == len
&&
378 std::char_traits
< char_type
>::compare(constants::message_text_keyword(), begin
, len
) == 0)
380 m_AttrName
= log::aux::default_attribute_names::message();
384 m_AttrName
= attribute_name(log::aux::to_narrow(string_type(begin
, end
)));
387 //! The method is called when an attribute is filled
390 BOOST_ASSERT_MSG(!!m_AttrName
, "Attribute name is not set");
392 if (m_AttrName
== log::aux::default_attribute_names::message())
394 // We make a special treatment for the message text formatter
395 append_formatter(expressions::aux::message_formatter());
399 // Use the factory to create the formatter
400 formatters_repository
< char_type
> const& repo
= formatters_repository
< char_type
>::get();
401 formatter_factory_type
& factory
= repo
.get_factory(m_AttrName
);
402 append_formatter(factory
.create_formatter(m_AttrName
, m_FactoryArgs
));
405 // Eventually, clear all the auxiliary data
406 m_AttrName
= attribute_name();
407 m_FactoryArgs
.clear();
410 //! The method is called when a string literal is discovered
411 void push_string(iterator_type begin
, iterator_type end
)
413 string_type
s(begin
, end
);
414 constants::translate_escape_sequences(s
);
415 append_formatter(literal_formatter
< char_type
>(boost::move(s
)));
418 //! The method appends a formatter part to the final formatter
419 template< typename FormatterT
>
420 void append_formatter(FormatterT fmt
)
423 m_Formatter
= boost::in_place(chained_formatter
< char_type
, FormatterT
>(boost::move(m_Formatter
.get()), boost::move(fmt
)));
425 m_Formatter
= boost::in_place(boost::move(fmt
));
428 // Assignment and copying are prohibited
429 BOOST_DELETED_FUNCTION(formatter_parser(formatter_parser
const&))
430 BOOST_DELETED_FUNCTION(formatter_parser
& operator= (formatter_parser
const&))
435 //! The function registers a user-defined formatter factory
436 template< typename CharT
>
437 BOOST_LOG_SETUP_API
void register_formatter_factory(attribute_name
const& name
, shared_ptr
< formatter_factory
< CharT
> > const& factory
)
439 BOOST_ASSERT(!!name
);
440 BOOST_ASSERT(!!factory
);
442 formatters_repository
< CharT
>& repo
= formatters_repository
< CharT
>::get();
443 BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard
< log::aux::light_rw_mutex
> lock(repo
.m_Mutex
);)
444 repo
.m_Map
[name
] = factory
;
447 //! The function parses a formatter from the string
448 template< typename CharT
>
449 BOOST_LOG_SETUP_API basic_formatter
< CharT
> parse_formatter(const CharT
* begin
, const CharT
* end
)
451 typedef CharT char_type
;
453 formatter_parser
< char_type
> parser
;
454 const char_type
* p
= begin
;
456 BOOST_LOG_EXPR_IF_MT(formatters_repository
< CharT
>& repo
= formatters_repository
< CharT
>::get();)
457 BOOST_LOG_EXPR_IF_MT(log::aux::shared_lock_guard
< log::aux::light_rw_mutex
> lock(repo
.m_Mutex
);)
459 parser
.parse(p
, end
);
461 return parser
.get_formatter();
464 #ifdef BOOST_LOG_USE_CHAR
465 template BOOST_LOG_SETUP_API
466 void register_formatter_factory
< char >(
467 attribute_name
const& attr_name
, shared_ptr
< formatter_factory
< char > > const& factory
);
468 template BOOST_LOG_SETUP_API
469 basic_formatter
< char > parse_formatter
< char >(const char* begin
, const char* end
);
470 #endif // BOOST_LOG_USE_CHAR
472 #ifdef BOOST_LOG_USE_WCHAR_T
473 template BOOST_LOG_SETUP_API
474 void register_formatter_factory
< wchar_t >(
475 attribute_name
const& attr_name
, shared_ptr
< formatter_factory
< wchar_t > > const& factory
);
476 template BOOST_LOG_SETUP_API
477 basic_formatter
< wchar_t > parse_formatter
< wchar_t >(const wchar_t* begin
, const wchar_t* end
);
478 #endif // BOOST_LOG_USE_WCHAR_T
480 BOOST_LOG_CLOSE_NAMESPACE
// namespace log
484 #include <boost/log/detail/footer.hpp>
486 #endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS