File: main.cpp

package info (click to toggle)
boost1.90 1.90.0-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 593,120 kB
  • sloc: cpp: 4,190,908; xml: 196,648; python: 34,618; ansic: 23,145; asm: 5,468; sh: 3,774; makefile: 1,161; perl: 1,020; sql: 728; ruby: 676; yacc: 478; java: 77; lisp: 24; csh: 6
file content (300 lines) | stat: -rw-r--r-- 14,734 bytes parent folder | download | duplicates (18)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*
 *          Copyright Andrey Semashev 2007 - 2015.
 * Distributed under the Boost Software License, Version 1.0.
 *    (See accompanying file LICENSE_1_0.txt or copy at
 *          http://www.boost.org/LICENSE_1_0.txt)
 */
/*!
 * \file   main.cpp
 * \author Andrey Semashev
 * \date   11.11.2007
 *
 * \brief  An example of in-depth library usage. See the library tutorial for expanded
 *         comments on this code. It may also be worthwhile reading the Wiki requirements page:
 *         http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost.Logging
 */

// #define BOOST_LOG_USE_CHAR
// #define BOOST_ALL_DYN_LINK 1
// #define BOOST_LOG_DYN_LINK 1

#include <cassert>
#include <iostream>
#include <fstream>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/core/null_deleter.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
#include <boost/log/support/date_time.hpp>

namespace logging = boost::log;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;
namespace keywords = boost::log::keywords;

using boost::shared_ptr;

// Here we define our application severity levels.
enum severity_level
{
    normal,
    notification,
    warning,
    error,
    critical
};

// The formatting logic for the severity level
template< typename CharT, typename TraitsT >
inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, severity_level lvl)
{
    static const char* const str[] =
    {
        "normal",
        "notification",
        "warning",
        "error",
        "critical"
    };
    if (static_cast< std::size_t >(lvl) < (sizeof(str) / sizeof(*str)))
        strm << str[lvl];
    else
        strm << static_cast< int >(lvl);
    return strm;
}

int foo(src::logger& lg)
{
    BOOST_LOG_FUNCTION();
    BOOST_LOG(lg) << "foo is being called";
    return 10;
}

int main(int argc, char* argv[])
{
    // This is a in-depth tutorial/example of Boost.Log usage

    // The first thing we have to do to get using the library is
    // to set up the logging sinks - i.e. where the logs will be written to.
    // Each sink is composed from frontend and backend. Frontend deals with
    // general sink behavior, like filtering (see below) and threading model.
    // Backend implements formatting and, actually, storing log records.
    // Not every frontend/backend combinations are compatible (mostly because of
    // threading models incompatibilities), but if they are not, the code will
    // simply not compile.

    // For now we only create a text output sink:
    typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
    shared_ptr< text_sink > pSink(new text_sink);

    // Here synchronous_sink is a sink frontend that performs thread synchronization
    // before passing log records to the backend (the text_ostream_backend class).
    // The backend formats each record and outputs it to one or several streams.
    // This approach makes implementing backends a lot simpler, because you don't
    // need to worry about multithreading.

    {
        // The good thing about sink frontends is that they are provided out-of-box and
        // take away thread-safety burden from the sink backend implementors. Even if you
        // have to call a custom backend method, the frontend gives you a convenient way
        // to do it in a thread safe manner. All you need is to acquire a locking pointer
        // to the backend.
        text_sink::locked_backend_ptr pBackend = pSink->locked_backend();

        // Now, as long as pBackend lives, you may work with the backend without
        // interference of other threads that might be trying to log.

        // Next we add streams to which logging records should be output
        shared_ptr< std::ostream > pStream(&std::clog, boost::null_deleter());
        pBackend->add_stream(pStream);

        // We can add more than one stream to the sink backend
        shared_ptr< std::ofstream > pStream2(new std::ofstream("sample.log"));
        assert(pStream2->is_open());
        pBackend->add_stream(pStream2);
    }

    // Ok, we're ready to add the sink to the logging library
    logging::core::get()->add_sink(pSink);

    // Now our logs will be written both to the console and to the file.
    // Let's do a quick test and output something. We have to create a logger for this.
    src::logger lg;

    // And output...
    BOOST_LOG(lg) << "Hello, World!";

    // Nice, huh? That's pretty much equivalent to writing the string to both the file
    // and the console. Now let's define the different way of formatting log records.
    // Each logging record may have a number of attributes in addition to the
    // message body itself. By setting up formatter we define which of them
    // will be written to log and in what way they will look there.
    pSink->set_formatter(expr::stream
        << expr::attr< unsigned int >("RecordID") // First an attribute "RecordID" is written to the log
        << " [" << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%d.%m.%Y %H:%M:%S.%f")
        << "] [" << expr::attr< severity_level >("Severity")
        << "] [" << expr::attr< boost::posix_time::time_duration >("Uptime")
        << "] [" // then this delimiter separates it from the rest of the line
        << expr::if_(expr::has_attr("Tag"))
           [
               expr::stream << expr::attr< std::string >("Tag") // then goes another attribute named "Tag"
                                               // Note here we explicitly stated that its type
                                               // should be std::string. We could omit it just
                                               // like we did it with the "RecordID", but in this case
                                               // library would have to detect the actual attribute value
                                               // type in run time which has the following consequences:
                                               // - On the one hand, the attribute would have been output
                                               //   even if it has another type (not std::string).
                                               // - On the other, this detection does not come for free
                                               //   and will result in performance decrease.
                                               //
                                               // In general it's better you to specify explicitly which
                                               // type should an attribute have wherever it is possible.
                                               // You may specify an MPL sequence of types if the attribute
                                               // may have more than one type. And you will have to specify
                                               // it anyway if the library is not familiar with it (see
                                               // boost/log/utility/type_dispatch/standard_types.hpp for the list
                                               // of the supported out-of-the-box types).
                << "] [" // yet another delimiter
           ]
        << expr::format_named_scope("Scope", keywords::format = "%n", keywords::iteration = expr::reverse) << "] "
        << expr::smessage); // here goes the log record text

/*
    // There is an alternative way of specifying formatters
    pSink->set_formatter(
        expr::format("%1% @ %2% [%3%] >%4%< Scope: %5%: %6%")
            % expr::attr< unsigned int >("RecordID")
            % expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%d.%m.%Y %H:%M:%S.%f")
            % expr::attr< boost::posix_time::time_duration >("Uptime")
            % expr::attr< std::string >("Tag")
            % expr::format_named_scope("Scope", keywords::format = "%n", keywords::iteration = expr::reverse, keywords::depth = 2)
            % expr::smessage);
*/

    // Now the sink will output in the following format:
    // 1 [Current time] [Tag value] Hello World!
    // The output will be the same for all streams we add to the sink. If you want something different,
    // you may create another sink for that purpose.

    // Now we're going to set up the attributes.
    // Remember that "RecordID" attribute in the formatter? There is a counter
    // attribute in the library that increments or decrements the value each time
    // it is output. Let's create it with a starting value 1.
    attrs::counter< unsigned int > RecordID(1);

    // Since we intend to count all logging records ever made by the application,
    // this attribute should clearly be global.
    logging::core::get()->add_global_attribute("RecordID", RecordID);

    // And similarly add a time stamp
    attrs::local_clock TimeStamp;
    logging::core::get()->add_global_attribute("TimeStamp", TimeStamp);

    // And an up time stopwatch
    BOOST_LOG_SCOPED_THREAD_ATTR("Uptime", attrs::timer());

    // Attributes may have two other scopes: thread scope and source scope. Attributes of thread
    // scope are output with each record made by the thread (regardless of the logger object), and
    // attributes of the source scope are output with each record made by the logger. On output
    // all attributes of global, thread and source scopes are merged into a one record and passed to
    // the sinks as one view. There is no difference between attributes of different scopes from the
    // sinks' perspective.

    // Let's also track the execution scope from which the records are made
    attrs::named_scope Scope;
    logging::core::get()->add_thread_attribute("Scope", Scope);

    // We can mark the current execution scope now - it's the 'main' function
    BOOST_LOG_FUNCTION();

    // Let's try out the counter attribute and formatting
    BOOST_LOG(lg) << "Some log line with a counter";
    BOOST_LOG(lg) << "Another log line with the counter";

    // Ok, remember the "Tag" attribute we added in the formatter? It is absent in these
    // two lines above, so it is empty in the output. Let's try to tag some log records with it.
    {
        BOOST_LOG_NAMED_SCOPE("Tagging scope");

        // Here we add a temporary attribute to the logger lg.
        // Every log record being written in the current scope with logger lg
        // will have a string attribute "Tag" with value "Tagged line" attached.
        BOOST_LOG_SCOPED_LOGGER_TAG(lg, "Tag", "Tagged line");

        // The above line is roughly equivalent to the following:
        // attrs::constant< std::string > TagAttr("Tagged line");
        // logging::scoped_attribute _ =
        //     logging::add_scoped_logger_attribute(lg, "Tag", TagAttr);

        // Now these lines will be highlighted with the tag
        BOOST_LOG(lg) << "Some tagged log line";
        BOOST_LOG(lg) << "Another tagged log line";
    }

    // And this line is not highlighted anymore
    BOOST_LOG(lg) << "Now the tag is removed";
    BOOST_LOG(lg) << logging::add_value("Tag", "Tagged line") << "Some lines can also be selectively tagged";

    // Now let's try to apply filtering to the output. Filtering is based on
    // attributes being output with the record. One of the common filtering use cases
    // is filtering based on the record severity level. We've already defined severity levels.
    // Now we can set the filter. A filter is essentially a functor that returns
    // boolean value that tells whether to write the record or not.
    pSink->set_filter(
        expr::attr< severity_level >("Severity").or_default(normal) >= warning // Write all records with "warning" severity or higher
        || expr::begins_with(expr::attr< std::string >("Tag").or_default(std::string()), "IMPORTANT")); // ...or specifically tagged

    // The "attr" placeholder here acts pretty much like the "attr" placeholder in formatters, except
    // that it requires the attribute type (or types in MPL-sequence) to be specified.
    // In case of a single std::string or std::wstring type of attribute the "attr" placeholder
    // provides a number of extended predicates which include "begins_with", "ends_with", "contains"
    // and "matches" (the last one performs RegEx matching).
    // There are other placeholders to be used for filter composition in the "boost/log/filters"
    // directory. Additionally, you are not restricted to them and may provide your own filtering
    // functors.

    // It must be noted that filters may be applied on per-sink basis and/or globally.
    // Above we set a filter for this particular sink. Had we another sink, the filter would
    // not influence it. To set a global filter one should call the set_filter method of the
    // logging system like that:
    // logging::core::get()->set_filter(...);

    // Now, to set logging severity we could perfectly use our previously created logger "lg".
    // But no make it more convenient and efficient there is a special extended logger class.
    // Its implementation may serve as an example of extending basic library functionality.
    // You may add your specific capabilities to the logger by deriving your class from it.
    src::severity_logger< severity_level > slg;

    // These two lines test filtering based on severity
    BOOST_LOG_SEV(slg, normal) << "A normal severity message, will not pass to the output";
    BOOST_LOG_SEV(slg, error) << "An error severity message, will pass to the output";

    {
        // Next we try if the second condition of the filter works
        // We mark following lines with a tag
        BOOST_LOG_SCOPED_THREAD_TAG("Tag", "IMPORTANT MESSAGES");

        // We may omit the severity and use the shorter BOOST_LOG macro. The logger "slg"
        // has the default severity that may be specified on its construction. We didn't
        // do it, so it is 0 by default. Therefore this record will have "normal" severity.
        // The only reason this record will be output is the "Tag" attribute we added above.
        BOOST_LOG(slg) << "Some really urgent line";
    }

    pSink->reset_filter();

    // And moreover, it is possible to nest logging records. For example, this will
    // be processed in the order of evaluation:
    BOOST_LOG(lg) << "The result of foo is " << foo(lg);

    return 0;
}