1 /*
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)
6 */
7 /*!
8 * \file formatter_parser.cpp
9 * \author Andrey Semashev
10 * \date 07.04.2008
11 *
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.
14 */
15
16 #ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS
17
18 #include <boost/log/detail/setup_config.hpp>
19 #include <map>
20 #include <string>
21 #include <sstream>
22 #include <stdexcept>
23 #include <boost/assert.hpp>
24 #include <boost/move/core.hpp>
25 #include <boost/move/utility_core.hpp>
26 #include <boost/optional/optional.hpp>
27 #include <boost/utility/in_place_factory.hpp>
28 #include <boost/log/expressions/formatter.hpp>
29 #include <boost/log/attributes/attribute_name.hpp>
30 #include <boost/log/exceptions.hpp>
31 #include <boost/log/detail/singleton.hpp>
32 #include <boost/log/detail/code_conversion.hpp>
33 #include <boost/log/detail/default_attribute_names.hpp>
34 #include <boost/log/utility/formatting_ostream.hpp>
35 #include <boost/log/utility/functional/nop.hpp>
36 #include <boost/log/utility/setup/formatter_parser.hpp>
37 #if !defined(BOOST_LOG_NO_THREADS)
38 #include <boost/log/detail/locks.hpp>
39 #include <boost/log/detail/light_rw_mutex.hpp>
40 #endif
41 #include "parser_utils.hpp"
42 #include "spirit_encoding.hpp"
43 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
44 #include "default_formatter_factory.hpp"
45 #endif
46 #include <boost/log/detail/header.hpp>
47
48 namespace boost {
49
50 BOOST_LOG_OPEN_NAMESPACE
51
52 BOOST_LOG_ANONYMOUS_NAMESPACE {
53
54 //! The structure contains formatter factories repository
55 template< typename CharT >
56 struct formatters_repository :
57 public log::aux::lazy_singleton< formatters_repository< CharT > >
58 {
59 //! Base class type
60 typedef log::aux::lazy_singleton< formatters_repository< CharT > > base_type;
61
62 #if !defined(BOOST_LOG_BROKEN_FRIEND_TEMPLATE_SPECIALIZATIONS)
63 friend class log::aux::lazy_singleton< formatters_repository< CharT > >;
64 #else
65 friend class base_type;
66 #endif
67
68 typedef CharT char_type;
69 typedef formatter_factory< char_type > formatter_factory_type;
70
71 //! Attribute name ordering predicate
72 struct attribute_name_order
73 {
74 typedef bool result_type;
75 result_type operator() (attribute_name const& left, attribute_name const& right) const
76 {
77 return left.id() < right.id();
78 }
79 };
80
81 //! Map of formatter factories
82 typedef std::map< attribute_name, shared_ptr< formatter_factory_type >, attribute_name_order > factories_map;
83
84
85 #if !defined(BOOST_LOG_NO_THREADS)
86 //! Synchronization mutex
87 mutable log::aux::light_rw_mutex m_Mutex;
88 #endif
89 //! The map of formatter factories
90 factories_map m_Map;
91 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
92 //! Default factory
93 mutable aux::default_formatter_factory< char_type > m_DefaultFactory;
94 #endif
95
96 //! The method returns the filter factory for the specified attribute name
97 formatter_factory_type& get_factory(attribute_name const& name) const
98 {
99 typename factories_map::const_iterator it = m_Map.find(name);
100 if (it != m_Map.end())
101 {
102 return *it->second;
103 }
104 else
105 {
106 #if !defined(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES)
107 return m_DefaultFactory;
108 #else
109 BOOST_LOG_THROW_DESCR(setup_error, "No formatter factory registered for attribute " + name.string());
110 #endif
111 }
112 }
113
114 private:
115 formatters_repository()
116 {
117 }
118 };
119
120 //! Function object for formatter chaining
121 template< typename CharT, typename SecondT >
122 struct chained_formatter
123 {
124 typedef void result_type;
125 typedef basic_formatter< CharT > formatter_type;
126 typedef typename formatter_type::stream_type stream_type;
127
128 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
129 explicit chained_formatter(formatter_type&& first, SecondT&& second) :
130 #else
131 template< typename T >
132 explicit chained_formatter(BOOST_RV_REF(formatter_type) first, T const& second) :
133 #endif
134 m_first(boost::move(first)), m_second(boost::move(second))
135 {
136 }
137
138 result_type operator() (record_view const& rec, stream_type& strm) const
139 {
140 m_first(rec, strm);
141 m_second(rec, strm);
142 }
143
144 private:
145 formatter_type m_first;
146 SecondT m_second;
147 };
148
149 //! String literal formatter
150 template< typename CharT >
151 struct literal_formatter
152 {
153 typedef void result_type;
154 typedef CharT char_type;
155 typedef std::basic_string< char_type > string_type;
156 typedef basic_formatting_ostream< char_type > stream_type;
157
158 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
159 explicit literal_formatter(string_type&& str) : m_str(boost::move(str))
160 #else
161 explicit literal_formatter(string_type const& str) : m_str(str)
162 #endif
163 {
164 }
165
166 result_type operator() (record_view const& rec, stream_type& strm) const
167 {
168 strm << m_str;
169 }
170
171 private:
172 const string_type m_str;
173 };
174
175 //! Formatter parsing grammar
176 template< typename CharT >
177 class formatter_parser
178 {
179 private:
180 typedef CharT char_type;
181 typedef const char_type* iterator_type;
182 typedef std::basic_string< char_type > string_type;
183 typedef basic_formatter< char_type > formatter_type;
184 typedef boost::log::aux::char_constants< char_type > constants;
185 typedef typename log::aux::encoding< char_type >::type encoding;
186 typedef log::aux::encoding_specific< encoding > encoding_specific;
187 typedef formatter_factory< char_type > formatter_factory_type;
188 typedef typename formatter_factory_type::args_map args_map;
189
190 private:
191 //! The formatter being constructed
192 optional< formatter_type > m_Formatter;
193
194 //! Attribute name
195 attribute_name m_AttrName;
196 //! Formatter factory arguments
197 args_map m_FactoryArgs;
198
199 //! Formatter argument name
200 mutable string_type m_ArgName;
201 //! Argument value
202 mutable string_type m_ArgValue;
203
204 public:
205 //! Constructor
206 formatter_parser()
207 {
208 }
209
210 //! Parses formatter
211 void parse(iterator_type& begin, iterator_type end)
212 {
213 iterator_type p = begin;
214
215 while (p != end)
216 {
217 // Find the end of a string literal
218 iterator_type start = p;
219 for (; p != end; ++p)
220 {
221 char_type c = *p;
222 if (c == constants::char_backslash)
223 {
224 // We found an escaped character
225 ++p;
226 if (p == end)
227 BOOST_LOG_THROW_DESCR(parse_error, "Invalid escape sequence in the formatter string");
228 }
229 else if (c == constants::char_percent)
230 {
231 // We found an attribute
232 break;
233 }
234 }
235
236 if (start != p)
237 push_string(start, p);
238
239 if (p != end)
240 {
241 // We found an attribute placeholder
242 iterator_type start = constants::trim_spaces_left(++p, end);
243 p = constants::scan_attr_placeholder(start, end);
244 if (p == end)
245 BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string");
246
247 on_attribute_name(start, p);
248
249 p = constants::trim_spaces_left(p, end);
250 if (p == end)
251 BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string");
252
253 if (*p == constants::char_paren_bracket_left)
254 {
255 // We found formatter arguments
256 p = parse_args(constants::trim_spaces_left(++p, end), end);
257 p = constants::trim_spaces_left(p, end);
258 if (p == end)
259 BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string");
260 }
261
262 if (*p != constants::char_percent)
263 BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder in the formatter string");
264
265 ++p;
266
267 push_attr();
268 }
269 }
270
271 begin = p;
272 }
273
274 //! Returns the parsed formatter
275 formatter_type get_formatter()
276 {
277 if (!m_Formatter)
278 {
279 // This may happen if parser input is an empty string
280 return formatter_type(nop());
281 }
282
283 return boost::move(m_Formatter.get());
284 }
285
286 private:
287 //! The method parses formatter arguments
288 iterator_type parse_args(iterator_type begin, iterator_type end)
289 {
290 iterator_type p = begin;
291 if (p == end)
292 BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder arguments description in the formatter string");
293 if (*p == constants::char_paren_bracket_right)
294 return ++p;
295
296 while (true)
297 {
298 char_type c = *p;
299
300 // Read argument name
301 iterator_type start = p;
302 if (!encoding::isalpha(*p))
303 BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is invalid");
304 for (++p; p != end; ++p)
305 {
306 c = *p;
307 if (encoding::isspace(c) || c == constants::char_equal)
308 break;
309 if (!encoding::isalnum(c) && c != constants::char_underline)
310 BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is invalid");
311 }
312
313 if (start == p)
314 BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is empty");
315
316 on_arg_name(start, p);
317
318 p = constants::trim_spaces_left(p, end);
319 if (p == end || *p != constants::char_equal)
320 BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument description is not valid");
321
322 // Read argument value
323 start = p = constants::trim_spaces_left(++p, end);
324 p = constants::parse_operand(p, end, m_ArgValue);
325 if (p == start)
326 BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument value is not specified");
327
328 push_arg();
329
330 p = constants::trim_spaces_left(p, end);
331 if (p == end)
332 BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder arguments description in the formatter string");
333
334 c = *p;
335 if (c == constants::char_paren_bracket_right)
336 {
337 break;
338 }
339 else if (c == constants::char_comma)
340 {
341 p = constants::trim_spaces_left(++p, end);
342 if (p == end)
343 BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is invalid");
344 }
345 else
346 {
347 BOOST_LOG_THROW_DESCR(parse_error, "Invalid attribute placeholder arguments description in the formatter string");
348 }
349 }
350
351 return ++p;
352 }
353
354 //! The method is called when an argument name is discovered
355 void on_arg_name(iterator_type begin, iterator_type end)
356 {
357 m_ArgName.assign(begin, end);
358 }
359
360 //! The method is called when an argument is filled
361 void push_arg()
362 {
363 m_FactoryArgs[m_ArgName] = m_ArgValue;
364 m_ArgName.clear();
365 m_ArgValue.clear();
366 }
367
368 //! The method is called when an attribute name is discovered
369 void on_attribute_name(iterator_type begin, iterator_type end)
370 {
371 if (begin == end)
372 BOOST_LOG_THROW_DESCR(parse_error, "Empty attribute name encountered");
373
374 // For compatibility with Boost.Log v1 we recognize %_% as the message attribute name
375 const std::size_t len = end - begin;
376 if (std::char_traits< char_type >::length(constants::message_text_keyword()) == len &&
377 std::char_traits< char_type >::compare(constants::message_text_keyword(), begin, len) == 0)
378 {
379 m_AttrName = log::aux::default_attribute_names::message();
380 }
381 else
382 {
383 m_AttrName = attribute_name(log::aux::to_narrow(string_type(begin, end)));
384 }
385 }
386 //! The method is called when an attribute is filled
387 void push_attr()
388 {
389 BOOST_ASSERT_MSG(!!m_AttrName, "Attribute name is not set");
390
391 if (m_AttrName == log::aux::default_attribute_names::message())
392 {
393 // We make a special treatment for the message text formatter
394 append_formatter(expressions::aux::message_formatter());
395 }
396 else
397 {
398 // Use the factory to create the formatter
399 formatters_repository< char_type > const& repo = formatters_repository< char_type >::get();
400 formatter_factory_type& factory = repo.get_factory(m_AttrName);
401 append_formatter(factory.create_formatter(m_AttrName, m_FactoryArgs));
402 }
403
404 // Eventually, clear all the auxiliary data
405 m_AttrName = attribute_name();
406 m_FactoryArgs.clear();
407 }
408
409 //! The method is called when a string literal is discovered
410 void push_string(iterator_type begin, iterator_type end)
411 {
412 string_type s(begin, end);
413 constants::translate_escape_sequences(s);
414 append_formatter(literal_formatter< char_type >(boost::move(s)));
415 }
416
417 //! The method appends a formatter part to the final formatter
418 template< typename FormatterT >
419 void append_formatter(FormatterT fmt)
420 {
421 if (!!m_Formatter)
422 m_Formatter = boost::in_place(chained_formatter< char_type, FormatterT >(boost::move(m_Formatter.get()), boost::move(fmt)));
423 else
424 m_Formatter = boost::in_place(boost::move(fmt));
425 }
426
427 // Assignment and copying are prohibited
428 BOOST_DELETED_FUNCTION(formatter_parser(formatter_parser const&))
429 BOOST_DELETED_FUNCTION(formatter_parser& operator= (formatter_parser const&))
430 };
431
432 } // namespace
433
434 //! The function registers a user-defined formatter factory
435 template< typename CharT >
436 BOOST_LOG_SETUP_API void register_formatter_factory(attribute_name const& name, shared_ptr< formatter_factory< CharT > > const& factory)
437 {
438 BOOST_ASSERT(!!name);
439 BOOST_ASSERT(!!factory);
440
441 formatters_repository< CharT >& repo = formatters_repository< CharT >::get();
442 BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);)
443 repo.m_Map[name] = factory;
444 }
445
446 //! The function parses a formatter from the string
447 template< typename CharT >
parse_formatter(const CharT * begin,const CharT * end)448 BOOST_LOG_SETUP_API basic_formatter< CharT > parse_formatter(const CharT* begin, const CharT* end)
449 {
450 typedef CharT char_type;
451
452 formatter_parser< char_type > parser;
453 const char_type* p = begin;
454
455 BOOST_LOG_EXPR_IF_MT(formatters_repository< CharT >& repo = formatters_repository< CharT >::get();)
456 BOOST_LOG_EXPR_IF_MT(log::aux::shared_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);)
457
458 parser.parse(p, end);
459
460 return parser.get_formatter();
461 }
462
463 #ifdef BOOST_LOG_USE_CHAR
464 template BOOST_LOG_SETUP_API
465 void register_formatter_factory< char >(
466 attribute_name const& attr_name, shared_ptr< formatter_factory< char > > const& factory);
467 template BOOST_LOG_SETUP_API
468 basic_formatter< char > parse_formatter< char >(const char* begin, const char* end);
469 #endif // BOOST_LOG_USE_CHAR
470
471 #ifdef BOOST_LOG_USE_WCHAR_T
472 template BOOST_LOG_SETUP_API
473 void register_formatter_factory< wchar_t >(
474 attribute_name const& attr_name, shared_ptr< formatter_factory< wchar_t > > const& factory);
475 template BOOST_LOG_SETUP_API
476 basic_formatter< wchar_t > parse_formatter< wchar_t >(const wchar_t* begin, const wchar_t* end);
477 #endif // BOOST_LOG_USE_WCHAR_T
478
479 BOOST_LOG_CLOSE_NAMESPACE // namespace log
480
481 } // namespace boost
482
483 #include <boost/log/detail/footer.hpp>
484
485 #endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS
486