• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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