• 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   main.cpp
9  * \author Andrey Semashev
10  * \date   11.11.2007
11  *
12  * \brief  An example of in-depth library usage. See the library tutorial for expanded
13  *         comments on this code. It may also be worthwhile reading the Wiki requirements page:
14  *         http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost.Logging
15  */
16 
17 // #define BOOST_LOG_USE_CHAR
18 // #define BOOST_ALL_DYN_LINK 1
19 // #define BOOST_LOG_DYN_LINK 1
20 
21 #include <cassert>
22 #include <iostream>
23 #include <fstream>
24 #include <boost/smart_ptr/shared_ptr.hpp>
25 #include <boost/core/null_deleter.hpp>
26 #include <boost/date_time/posix_time/posix_time.hpp>
27 #include <boost/log/common.hpp>
28 #include <boost/log/expressions.hpp>
29 #include <boost/log/attributes.hpp>
30 #include <boost/log/sinks.hpp>
31 #include <boost/log/sources/logger.hpp>
32 #include <boost/log/utility/manipulators/add_value.hpp>
33 #include <boost/log/attributes/scoped_attribute.hpp>
34 #include <boost/log/support/date_time.hpp>
35 
36 namespace logging = boost::log;
37 namespace expr = boost::log::expressions;
38 namespace sinks = boost::log::sinks;
39 namespace attrs = boost::log::attributes;
40 namespace src = boost::log::sources;
41 namespace keywords = boost::log::keywords;
42 
43 using boost::shared_ptr;
44 
45 // Here we define our application severity levels.
46 enum severity_level
47 {
48     normal,
49     notification,
50     warning,
51     error,
52     critical
53 };
54 
55 // The formatting logic for the severity level
56 template< typename CharT, typename TraitsT >
operator <<(std::basic_ostream<CharT,TraitsT> & strm,severity_level lvl)57 inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, severity_level lvl)
58 {
59     static const char* const str[] =
60     {
61         "normal",
62         "notification",
63         "warning",
64         "error",
65         "critical"
66     };
67     if (static_cast< std::size_t >(lvl) < (sizeof(str) / sizeof(*str)))
68         strm << str[lvl];
69     else
70         strm << static_cast< int >(lvl);
71     return strm;
72 }
73 
foo(src::logger & lg)74 int foo(src::logger& lg)
75 {
76     BOOST_LOG_FUNCTION();
77     BOOST_LOG(lg) << "foo is being called";
78     return 10;
79 }
80 
main(int argc,char * argv[])81 int main(int argc, char* argv[])
82 {
83     // This is a in-depth tutorial/example of Boost.Log usage
84 
85     // The first thing we have to do to get using the library is
86     // to set up the logging sinks - i.e. where the logs will be written to.
87     // Each sink is composed from frontend and backend. Frontend deals with
88     // general sink behavior, like filtering (see below) and threading model.
89     // Backend implements formatting and, actually, storing log records.
90     // Not every frontend/backend combinations are compatible (mostly because of
91     // threading models incompatibilities), but if they are not, the code will
92     // simply not compile.
93 
94     // For now we only create a text output sink:
95     typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
96     shared_ptr< text_sink > pSink(new text_sink);
97 
98     // Here synchronous_sink is a sink frontend that performs thread synchronization
99     // before passing log records to the backend (the text_ostream_backend class).
100     // The backend formats each record and outputs it to one or several streams.
101     // This approach makes implementing backends a lot simpler, because you don't
102     // need to worry about multithreading.
103 
104     {
105         // The good thing about sink frontends is that they are provided out-of-box and
106         // take away thread-safety burden from the sink backend implementors. Even if you
107         // have to call a custom backend method, the frontend gives you a convenient way
108         // to do it in a thread safe manner. All you need is to acquire a locking pointer
109         // to the backend.
110         text_sink::locked_backend_ptr pBackend = pSink->locked_backend();
111 
112         // Now, as long as pBackend lives, you may work with the backend without
113         // interference of other threads that might be trying to log.
114 
115         // Next we add streams to which logging records should be output
116         shared_ptr< std::ostream > pStream(&std::clog, boost::null_deleter());
117         pBackend->add_stream(pStream);
118 
119         // We can add more than one stream to the sink backend
120         shared_ptr< std::ofstream > pStream2(new std::ofstream("sample.log"));
121         assert(pStream2->is_open());
122         pBackend->add_stream(pStream2);
123     }
124 
125     // Ok, we're ready to add the sink to the logging library
126     logging::core::get()->add_sink(pSink);
127 
128     // Now our logs will be written both to the console and to the file.
129     // Let's do a quick test and output something. We have to create a logger for this.
130     src::logger lg;
131 
132     // And output...
133     BOOST_LOG(lg) << "Hello, World!";
134 
135     // Nice, huh? That's pretty much equivalent to writing the string to both the file
136     // and the console. Now let's define the different way of formatting log records.
137     // Each logging record may have a number of attributes in addition to the
138     // message body itself. By setting up formatter we define which of them
139     // will be written to log and in what way they will look there.
140     pSink->set_formatter(expr::stream
141         << expr::attr< unsigned int >("RecordID") // First an attribute "RecordID" is written to the log
142         << " [" << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%d.%m.%Y %H:%M:%S.%f")
143         << "] [" << expr::attr< severity_level >("Severity")
144         << "] [" << expr::attr< boost::posix_time::time_duration >("Uptime")
145         << "] [" // then this delimiter separates it from the rest of the line
146         << expr::if_(expr::has_attr("Tag"))
147            [
148                expr::stream << expr::attr< std::string >("Tag") // then goes another attribute named "Tag"
149                                                // Note here we explicitly stated that its type
150                                                // should be std::string. We could omit it just
151                                                // like we did it with the "RecordID", but in this case
152                                                // library would have to detect the actual attribute value
153                                                // type in run time which has the following consequences:
154                                                // - On the one hand, the attribute would have been output
155                                                //   even if it has another type (not std::string).
156                                                // - On the other, this detection does not come for free
157                                                //   and will result in performance decrease.
158                                                //
159                                                // In general it's better you to specify explicitly which
160                                                // type should an attribute have wherever it is possible.
161                                                // You may specify an MPL sequence of types if the attribute
162                                                // may have more than one type. And you will have to specify
163                                                // it anyway if the library is not familiar with it (see
164                                                // boost/log/utility/type_dispatch/standard_types.hpp for the list
165                                                // of the supported out-of-the-box types).
166                 << "] [" // yet another delimiter
167            ]
168         << expr::format_named_scope("Scope", keywords::format = "%n", keywords::iteration = expr::reverse) << "] "
169         << expr::smessage); // here goes the log record text
170 
171 /*
172     // There is an alternative way of specifying formatters
173     pSink->set_formatter(
174         expr::format("%1% @ %2% [%3%] >%4%< Scope: %5%: %6%")
175             % expr::attr< unsigned int >("RecordID")
176             % expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%d.%m.%Y %H:%M:%S.%f")
177             % expr::attr< boost::posix_time::time_duration >("Uptime")
178             % expr::attr< std::string >("Tag")
179             % expr::format_named_scope("Scope", keywords::format = "%n", keywords::iteration = expr::reverse, keywords::depth = 2)
180             % expr::smessage);
181 */
182 
183     // Now the sink will output in the following format:
184     // 1 [Current time] [Tag value] Hello World!
185     // The output will be the same for all streams we add to the sink. If you want something different,
186     // you may create another sink for that purpose.
187 
188     // Now we're going to set up the attributes.
189     // Remember that "RecordID" attribute in the formatter? There is a counter
190     // attribute in the library that increments or decrements the value each time
191     // it is output. Let's create it with a starting value 1.
192     attrs::counter< unsigned int > RecordID(1);
193 
194     // Since we intend to count all logging records ever made by the application,
195     // this attribute should clearly be global.
196     logging::core::get()->add_global_attribute("RecordID", RecordID);
197 
198     // And similarly add a time stamp
199     attrs::local_clock TimeStamp;
200     logging::core::get()->add_global_attribute("TimeStamp", TimeStamp);
201 
202     // And an up time stopwatch
203     BOOST_LOG_SCOPED_THREAD_ATTR("Uptime", attrs::timer());
204 
205     // Attributes may have two other scopes: thread scope and source scope. Attributes of thread
206     // scope are output with each record made by the thread (regardless of the logger object), and
207     // attributes of the source scope are output with each record made by the logger. On output
208     // all attributes of global, thread and source scopes are merged into a one record and passed to
209     // the sinks as one view. There is no difference between attributes of different scopes from the
210     // sinks' perspective.
211 
212     // Let's also track the execution scope from which the records are made
213     attrs::named_scope Scope;
214     logging::core::get()->add_thread_attribute("Scope", Scope);
215 
216     // We can mark the current execution scope now - it's the 'main' function
217     BOOST_LOG_FUNCTION();
218 
219     // Let's try out the counter attribute and formatting
220     BOOST_LOG(lg) << "Some log line with a counter";
221     BOOST_LOG(lg) << "Another log line with the counter";
222 
223     // Ok, remember the "Tag" attribute we added in the formatter? It is absent in these
224     // two lines above, so it is empty in the output. Let's try to tag some log records with it.
225     {
226         BOOST_LOG_NAMED_SCOPE("Tagging scope");
227 
228         // Here we add a temporary attribute to the logger lg.
229         // Every log record being written in the current scope with logger lg
230         // will have a string attribute "Tag" with value "Tagged line" attached.
231         BOOST_LOG_SCOPED_LOGGER_TAG(lg, "Tag", "Tagged line");
232 
233         // The above line is roughly equivalent to the following:
234         // attrs::constant< std::string > TagAttr("Tagged line");
235         // logging::scoped_attribute _ =
236         //     logging::add_scoped_logger_attribute(lg, "Tag", TagAttr);
237 
238         // Now these lines will be highlighted with the tag
239         BOOST_LOG(lg) << "Some tagged log line";
240         BOOST_LOG(lg) << "Another tagged log line";
241     }
242 
243     // And this line is not highlighted anymore
244     BOOST_LOG(lg) << "Now the tag is removed";
245     BOOST_LOG(lg) << logging::add_value("Tag", "Tagged line") << "Some lines can also be selectively tagged";
246 
247     // Now let's try to apply filtering to the output. Filtering is based on
248     // attributes being output with the record. One of the common filtering use cases
249     // is filtering based on the record severity level. We've already defined severity levels.
250     // Now we can set the filter. A filter is essentially a functor that returns
251     // boolean value that tells whether to write the record or not.
252     pSink->set_filter(
253         expr::attr< severity_level >("Severity").or_default(normal) >= warning // Write all records with "warning" severity or higher
254         || expr::begins_with(expr::attr< std::string >("Tag").or_default(std::string()), "IMPORTANT")); // ...or specifically tagged
255 
256     // The "attr" placeholder here acts pretty much like the "attr" placeholder in formatters, except
257     // that it requires the attribute type (or types in MPL-sequence) to be specified.
258     // In case of a single std::string or std::wstring type of attribute the "attr" placeholder
259     // provides a number of extended predicates which include "begins_with", "ends_with", "contains"
260     // and "matches" (the last one performs RegEx matching).
261     // There are other placeholders to be used for filter composition in the "boost/log/filters"
262     // directory. Additionally, you are not restricted to them and may provide your own filtering
263     // functors.
264 
265     // It must be noted that filters may be applied on per-sink basis and/or globally.
266     // Above we set a filter for this particular sink. Had we another sink, the filter would
267     // not influence it. To set a global filter one should call the set_filter method of the
268     // logging system like that:
269     // logging::core::get()->set_filter(...);
270 
271     // Now, to set logging severity we could perfectly use our previously created logger "lg".
272     // But no make it more convenient and efficient there is a special extended logger class.
273     // Its implementation may serve as an example of extending basic library functionality.
274     // You may add your specific capabilities to the logger by deriving your class from it.
275     src::severity_logger< severity_level > slg;
276 
277     // These two lines test filtering based on severity
278     BOOST_LOG_SEV(slg, normal) << "A normal severity message, will not pass to the output";
279     BOOST_LOG_SEV(slg, error) << "An error severity message, will pass to the output";
280 
281     {
282         // Next we try if the second condition of the filter works
283         // We mark following lines with a tag
284         BOOST_LOG_SCOPED_THREAD_TAG("Tag", "IMPORTANT MESSAGES");
285 
286         // We may omit the severity and use the shorter BOOST_LOG macro. The logger "slg"
287         // has the default severity that may be specified on its construction. We didn't
288         // do it, so it is 0 by default. Therefore this record will have "normal" severity.
289         // The only reason this record will be output is the "Tag" attribute we added above.
290         BOOST_LOG(slg) << "Some really urgent line";
291     }
292 
293     pSink->reset_filter();
294 
295     // And moreover, it is possible to nest logging records. For example, this will
296     // be processed in the order of evaluation:
297     BOOST_LOG(lg) << "The result of foo is " << foo(lg);
298 
299     return 0;
300 }
301