main.cpp 14 KB

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