Boost.Log merged to trunk.
[SVN r83860]
This commit is contained in:
commit
a7c4e0e319
96
.gitattributes
vendored
Normal file
96
.gitattributes
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
* text=auto !eol svneol=native#text/plain
|
||||
*.gitattributes text svneol=native#text/plain
|
||||
|
||||
# Scriptish formats
|
||||
*.bat text svneol=native#text/plain
|
||||
*.bsh text svneol=native#text/x-beanshell
|
||||
*.cgi text svneol=native#text/plain
|
||||
*.cmd text svneol=native#text/plain
|
||||
*.js text svneol=native#text/javascript
|
||||
*.php text svneol=native#text/x-php
|
||||
*.pl text svneol=native#text/x-perl
|
||||
*.pm text svneol=native#text/x-perl
|
||||
*.py text svneol=native#text/x-python
|
||||
*.sh eol=lf svneol=LF#text/x-sh
|
||||
configure eol=lf svneol=LF#text/x-sh
|
||||
|
||||
# Image formats
|
||||
*.bmp binary svneol=unset#image/bmp
|
||||
*.gif binary svneol=unset#image/gif
|
||||
*.ico binary svneol=unset#image/ico
|
||||
*.jpeg binary svneol=unset#image/jpeg
|
||||
*.jpg binary svneol=unset#image/jpeg
|
||||
*.png binary svneol=unset#image/png
|
||||
*.tif binary svneol=unset#image/tiff
|
||||
*.tiff binary svneol=unset#image/tiff
|
||||
*.svg text svneol=native#image/svg%2Bxml
|
||||
|
||||
# Data formats
|
||||
*.pdf binary svneol=unset#application/pdf
|
||||
*.avi binary svneol=unset#video/avi
|
||||
*.doc binary svneol=unset#application/msword
|
||||
*.dsp text svneol=crlf#text/plain
|
||||
*.dsw text svneol=crlf#text/plain
|
||||
*.eps binary svneol=unset#application/postscript
|
||||
*.gz binary svneol=unset#application/gzip
|
||||
*.mov binary svneol=unset#video/quicktime
|
||||
*.mp3 binary svneol=unset#audio/mpeg
|
||||
*.ppt binary svneol=unset#application/vnd.ms-powerpoint
|
||||
*.ps binary svneol=unset#application/postscript
|
||||
*.psd binary svneol=unset#application/photoshop
|
||||
*.rdf binary svneol=unset#text/rdf
|
||||
*.rss text svneol=unset#text/xml
|
||||
*.rtf binary svneol=unset#text/rtf
|
||||
*.sln text svneol=native#text/plain
|
||||
*.swf binary svneol=unset#application/x-shockwave-flash
|
||||
*.tgz binary svneol=unset#application/gzip
|
||||
*.vcproj text svneol=native#text/xml
|
||||
*.vcxproj text svneol=native#text/xml
|
||||
*.vsprops text svneol=native#text/xml
|
||||
*.wav binary svneol=unset#audio/wav
|
||||
*.xls binary svneol=unset#application/vnd.ms-excel
|
||||
*.zip binary svneol=unset#application/zip
|
||||
|
||||
# Text formats
|
||||
.htaccess text svneol=native#text/plain
|
||||
*.bbk text svneol=native#text/xml
|
||||
*.cmake text svneol=native#text/plain
|
||||
*.css text svneol=native#text/css
|
||||
*.dtd text svneol=native#text/xml
|
||||
*.htm text svneol=native#text/html
|
||||
*.html text svneol=native#text/html
|
||||
*.ini text svneol=native#text/plain
|
||||
*.log text svneol=native#text/plain
|
||||
*.mak text svneol=native#text/plain
|
||||
*.qbk text svneol=native#text/plain
|
||||
*.rst text svneol=native#text/plain
|
||||
*.sql text svneol=native#text/x-sql
|
||||
*.txt text svneol=native#text/plain
|
||||
*.xhtml text svneol=native#text/xhtml%2Bxml
|
||||
*.xml text svneol=native#text/xml
|
||||
*.xsd text svneol=native#text/xml
|
||||
*.xsl text svneol=native#text/xml
|
||||
*.xslt text svneol=native#text/xml
|
||||
*.xul text svneol=native#text/xul
|
||||
*.yml text svneol=native#text/plain
|
||||
boost-no-inspect text svneol=native#text/plain
|
||||
CHANGES text svneol=native#text/plain
|
||||
COPYING text svneol=native#text/plain
|
||||
INSTALL text svneol=native#text/plain
|
||||
Jamfile text svneol=native#text/plain
|
||||
Jamroot text svneol=native#text/plain
|
||||
Jamfile.v2 text svneol=native#text/plain
|
||||
Jamrules text svneol=native#text/plain
|
||||
Makefile* text svneol=native#text/plain
|
||||
README text svneol=native#text/plain
|
||||
TODO text svneol=native#text/plain
|
||||
|
||||
# Code formats
|
||||
*.c text svneol=native#text/plain
|
||||
*.cpp text svneol=native#text/plain
|
||||
*.h text svneol=native#text/plain
|
||||
*.hpp text svneol=native#text/plain
|
||||
*.ipp text svneol=native#text/plain
|
||||
*.tpp text svneol=native#text/plain
|
||||
*.jam text svneol=native#text/plain
|
||||
*.java text svneol=native#text/plain
|
156
build/Jamfile.v2
Normal file
156
build/Jamfile.v2
Normal file
@ -0,0 +1,156 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
import modules ;
|
||||
import os ;
|
||||
import feature ;
|
||||
|
||||
lib psapi ;
|
||||
lib ws2_32 ;
|
||||
|
||||
local rule default_logapi ( )
|
||||
{
|
||||
local api = unix ;
|
||||
if [ os.name ] = "NT" { api = winnt ; }
|
||||
return $(api) ;
|
||||
}
|
||||
|
||||
feature.feature logapi : unix winnt : propagated ;
|
||||
feature.set-default logapi : [ default_logapi ] ;
|
||||
|
||||
project boost/log
|
||||
: source-location ../src
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_LOG_DLL
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<cxxflags>/bigobj
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc-mingw:<linkflags>-Wl,--enable-auto-import
|
||||
<toolset>gcc-cygwin:<linkflags>-Wl,--enable-auto-import
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<threading>single:<define>BOOST_LOG_NO_THREADS
|
||||
<threading>multi:<library>/boost/thread//boost_thread
|
||||
;
|
||||
|
||||
local no_event_log = [ MATCH (define=BOOST_LOG_WITHOUT_EVENT_LOG) : [ modules.peek : ARGV ] ] ;
|
||||
local BOOST_LOG_MC_SRC ;
|
||||
|
||||
if ! $(no_event_log)
|
||||
{
|
||||
DEPENDS event_log_backend.cpp : simple_event_log.mc ;
|
||||
BOOST_LOG_MC_SRC = simple_event_log.mc ;
|
||||
}
|
||||
|
||||
local BOOST_LOG_COMMON_SRC =
|
||||
attribute_name.cpp
|
||||
attribute_set.cpp
|
||||
attribute_value_set.cpp
|
||||
code_conversion.cpp
|
||||
core.cpp
|
||||
record_ostream.cpp
|
||||
severity_level.cpp
|
||||
global_logger_storage.cpp
|
||||
named_scope.cpp
|
||||
process_name.cpp
|
||||
process_id.cpp
|
||||
thread_id.cpp
|
||||
timer.cpp
|
||||
exceptions.cpp
|
||||
default_attribute_names.cpp
|
||||
default_sink.cpp
|
||||
text_ostream_backend.cpp
|
||||
text_file_backend.cpp
|
||||
syslog_backend.cpp
|
||||
thread_specific.cpp
|
||||
once_block.cpp
|
||||
timestamp.cpp
|
||||
threadsafe_queue.cpp
|
||||
event.cpp
|
||||
trivial.cpp
|
||||
spirit_encoding.cpp
|
||||
format_parser.cpp
|
||||
date_time_format_parser.cpp
|
||||
named_scope_format_parser.cpp
|
||||
unhandled_exception_count.cpp
|
||||
;
|
||||
|
||||
lib boost_log
|
||||
: ## sources ##
|
||||
$(BOOST_LOG_COMMON_SRC)
|
||||
## winnt sources ##
|
||||
$(BOOST_LOG_MC_SRC)
|
||||
event_log_backend.cpp
|
||||
debug_output_backend.cpp
|
||||
light_rw_mutex.cpp
|
||||
psapi
|
||||
ws2_32
|
||||
: ## requirements ##
|
||||
<define>BOOST_LOG_BUILDING_THE_LIB=1
|
||||
<define>BOOST_SPIRIT_USE_PHOENIX_V3=1
|
||||
<define>BOOST_THREAD_DONT_USE_CHRONO=1 # Don't introduce false dependency on Boost.Chrono
|
||||
<logapi>winnt
|
||||
;
|
||||
|
||||
lib boost_log
|
||||
: ## sources ##
|
||||
$(BOOST_LOG_COMMON_SRC)
|
||||
## unix sources ##
|
||||
: ## requirements ##
|
||||
<define>BOOST_LOG_BUILDING_THE_LIB=1
|
||||
<define>BOOST_SPIRIT_USE_PHOENIX_V3=1
|
||||
<define>BOOST_THREAD_DONT_USE_CHRONO=1 # Don't introduce false dependency on Boost.Chrono
|
||||
<logapi>unix
|
||||
;
|
||||
|
||||
|
||||
local BOOST_LOG_SETUP_COMMON_SRC =
|
||||
parser_utils.cpp
|
||||
init_from_stream.cpp
|
||||
init_from_settings.cpp
|
||||
settings_parser.cpp
|
||||
filter_parser.cpp
|
||||
formatter_parser.cpp
|
||||
default_filter_factory.cpp
|
||||
;
|
||||
|
||||
lib boost_log_setup
|
||||
: ## sources ##
|
||||
$(BOOST_LOG_SETUP_COMMON_SRC)
|
||||
## winnt sources ##
|
||||
ws2_32
|
||||
: ## requirements ##
|
||||
<link>shared:<define>BOOST_LOG_SETUP_DLL
|
||||
<define>BOOST_LOG_SETUP_BUILDING_THE_LIB=1
|
||||
<define>BOOST_SPIRIT_USE_PHOENIX_V3=1
|
||||
<define>BOOST_THREAD_DONT_USE_CHRONO=1 # Don't introduce false dependency on Boost.Chrono
|
||||
<library>boost_log
|
||||
<logapi>winnt
|
||||
;
|
||||
|
||||
lib boost_log_setup
|
||||
: ## sources ##
|
||||
$(BOOST_LOG_SETUP_COMMON_SRC)
|
||||
## unix sources ##
|
||||
: ## requirements ##
|
||||
<link>shared:<define>BOOST_LOG_SETUP_DLL
|
||||
<define>BOOST_LOG_SETUP_BUILDING_THE_LIB=1
|
||||
<define>BOOST_SPIRIT_USE_PHOENIX_V3=1
|
||||
<define>BOOST_THREAD_DONT_USE_CHRONO=1 # Don't introduce false dependency on Boost.Chrono
|
||||
<library>boost_log
|
||||
<logapi>unix
|
||||
;
|
BIN
doc/Design.dia
Normal file
BIN
doc/Design.dia
Normal file
Binary file not shown.
BIN
doc/Design.png
Normal file
BIN
doc/Design.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
251
doc/Jamfile.v2
Normal file
251
doc/Jamfile.v2
Normal file
@ -0,0 +1,251 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
using quickbook ;
|
||||
using boostbook ;
|
||||
using doxygen ;
|
||||
using xsltproc ;
|
||||
|
||||
import set ;
|
||||
import doxygen ;
|
||||
import xsltproc ;
|
||||
import notfile ;
|
||||
import path ;
|
||||
|
||||
project boost/libs/log/doc ;
|
||||
|
||||
path-constant images_location : html ;
|
||||
|
||||
local doxygen_params =
|
||||
<doxygen:param>RECURSIVE=YES
|
||||
<doxygen:param>ALPHABETICAL_INDEX=YES
|
||||
<doxygen:param>REPEAT_BRIEF=YES
|
||||
<doxygen:param>ALWAYS_DETAILED_SEC=YES
|
||||
<doxygen:param>BRIEF_MEMBER_DESC=NO
|
||||
<doxygen:param>ABBREVIATE_BRIEF=YES
|
||||
<doxygen:param>INHERIT_DOCS=YES
|
||||
<doxygen:param>HIDE_UNDOC_MEMBERS=YES
|
||||
<doxygen:param>HIDE_UNDOC_CLASSES=YES
|
||||
<doxygen:param>HIDE_SCOPE_NAMES=YES
|
||||
<doxygen:param>EXTRACT_ALL=NO
|
||||
<doxygen:param>EXTRACT_PRIVATE=NO
|
||||
<doxygen:param>BUILTIN_STL_SUPPORT=YES
|
||||
<doxygen:param>ENABLE_PREPROCESSING=YES
|
||||
<doxygen:param>MACRO_EXPANSION=YES
|
||||
<doxygen:param>TAB_SIZE=4
|
||||
<doxygen:param>SOURCE_BROWSER=YES
|
||||
<doxygen:param>VERBATIM_HEADERS=NO
|
||||
# <doxygen:param>SEARCH_INCLUDES=YES
|
||||
# <doxygen:param>"INCLUDE_PATH=../../.."
|
||||
# <doxygen:param>EXCLUDE_SYMBOLS="aux aux::*"
|
||||
<doxygen:param>"PREDEFINED=BOOST_LOG_DOXYGEN_PASS \\
|
||||
BOOST_LOG_NO_VTABLE= \\
|
||||
BOOST_LOG_VISIBLE= \\
|
||||
BOOST_LOG_FORCEINLINE=inline \\
|
||||
BOOST_STATIC_ASSERT(x)= \\
|
||||
BOOST_STATIC_ASSERT_MSG(x,y)= \\
|
||||
BOOST_RV_REF(x)=\"x&&\" \\
|
||||
BOOST_COPY_ASSIGN_REF(x)=\"x const&\" \\
|
||||
BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(x)= \\
|
||||
BOOST_LOG_UNIQUE_IDENTIFIER_NAME(x)=anonymous \\
|
||||
BOOST_LOG_USE_NATIVE_SYSLOG=1 \\
|
||||
BOOST_PARAMETER_KEYWORD(x,y)=\"keyword y;\" \\
|
||||
BOOST_LOG_AUX_VOID_DEFAULT=\"= void\" \\
|
||||
BOOST_LOG_NAMESPACE=log \\
|
||||
BOOST_LOG_OPEN_NAMESPACE=\"namespace log {\" \\
|
||||
BOOST_LOG_CLOSE_NAMESPACE=\"}\" \\
|
||||
BOOST_LOG_USE_CHAR \\
|
||||
BOOST_LOG_USE_WCHAR_T \\
|
||||
BOOST_LOG_API= \\
|
||||
BOOST_LOG_SETUP_API="
|
||||
<xsl:param>boost.doxygen.detailns=aux
|
||||
# <xsl:param>boost.doxygen.detail=implementation_
|
||||
;
|
||||
|
||||
|
||||
local top_level_includes =
|
||||
[ glob
|
||||
../../../boost/log/*.hpp
|
||||
] ;
|
||||
|
||||
local core_includes =
|
||||
[ glob
|
||||
../../../boost/log/core/*.hpp
|
||||
] ;
|
||||
|
||||
local attributes_includes =
|
||||
[ glob
|
||||
../../../boost/log/attributes/*.hpp
|
||||
] ;
|
||||
|
||||
local expressions_includes =
|
||||
[ glob
|
||||
../../../boost/log/expressions/*.hpp
|
||||
../../../boost/log/expressions/predicates/*.hpp
|
||||
../../../boost/log/expressions/formatters/*.hpp
|
||||
] ;
|
||||
|
||||
local sources_includes =
|
||||
[ glob
|
||||
../../../boost/log/sources/*.hpp
|
||||
] ;
|
||||
|
||||
local sinks_includes =
|
||||
[ set.difference
|
||||
# Document all these files...
|
||||
[ glob
|
||||
../../../boost/log/sinks/*.hpp
|
||||
]
|
||||
:
|
||||
# ...except these
|
||||
[ glob
|
||||
../../../boost/log/sinks/nt6_event_log*.hpp
|
||||
]
|
||||
] ;
|
||||
|
||||
local utility_includes =
|
||||
[ glob
|
||||
../../../boost/log/utility/*.hpp
|
||||
../../../boost/log/utility/setup/*.hpp
|
||||
../../../boost/log/utility/type_dispatch/*.hpp
|
||||
../../../boost/log/utility/functional/*.hpp
|
||||
../../../boost/log/utility/manipulators/*.hpp
|
||||
] ;
|
||||
|
||||
local support_includes =
|
||||
[ glob
|
||||
../../../boost/log/support/*.hpp
|
||||
] ;
|
||||
|
||||
|
||||
# This rule generates *.qbk files with macros with references to files, classes, etc. from the doxygen resulting *.xml files.
|
||||
rule gen-references ( target : source : properties * )
|
||||
{
|
||||
DEPENDS target : source ;
|
||||
local source-path = [ path.make [ on $(source) return $(LOCATE) ] ] ;
|
||||
STYLESHEET on $(target) = [ path.native [ path.join $(source-path) gen_references.xsl ] ] ;
|
||||
local target-name = $(source:B) ;
|
||||
TARGET on $(target) = [ path.native [ path.join $(source-path) $(target-name:S=.qbk) ] ] ;
|
||||
}
|
||||
actions gen-references
|
||||
{
|
||||
# echo "*** Executing " $(NAME:E=xsltproc) -o "$(TARGET)" "$(STYLESHEET)" "$(>)"
|
||||
$(NAME:E=xsltproc) -o "$(TARGET)" "$(STYLESHEET)" "$(>)"
|
||||
}
|
||||
|
||||
|
||||
doxygen top_level_reference
|
||||
:
|
||||
$(top_level_includes)
|
||||
:
|
||||
$(doxygen_params)
|
||||
<xsl:param>"boost.doxygen.reftitle=Top level headers"
|
||||
;
|
||||
|
||||
notfile top_level_refs : @gen-references : top_level_reference.xml ;
|
||||
|
||||
doxygen core_reference
|
||||
:
|
||||
$(core_includes)
|
||||
:
|
||||
$(doxygen_params)
|
||||
<xsl:param>"boost.doxygen.reftitle=Core components"
|
||||
;
|
||||
|
||||
notfile core_refs : @gen-references : core_reference.xml ;
|
||||
|
||||
doxygen attributes_reference
|
||||
:
|
||||
$(attributes_includes)
|
||||
:
|
||||
$(doxygen_params)
|
||||
<xsl:param>"boost.doxygen.reftitle=Attributes"
|
||||
;
|
||||
|
||||
notfile attributes_refs : @gen-references : attributes_reference.xml ;
|
||||
|
||||
doxygen expressions_reference
|
||||
:
|
||||
$(expressions_includes)
|
||||
:
|
||||
$(doxygen_params)
|
||||
<xsl:param>"boost.doxygen.reftitle=Expressions"
|
||||
;
|
||||
|
||||
notfile expressions_refs : @gen-references : expressions_reference.xml ;
|
||||
|
||||
doxygen sources_reference
|
||||
:
|
||||
$(sources_includes)
|
||||
:
|
||||
$(doxygen_params)
|
||||
<xsl:param>"boost.doxygen.reftitle=Logging sources"
|
||||
;
|
||||
|
||||
notfile sources_refs : @gen-references : sources_reference.xml ;
|
||||
|
||||
doxygen sinks_reference
|
||||
:
|
||||
$(sinks_includes)
|
||||
:
|
||||
$(doxygen_params)
|
||||
<xsl:param>"boost.doxygen.reftitle=Sinks"
|
||||
;
|
||||
|
||||
notfile sinks_refs : @gen-references : sinks_reference.xml ;
|
||||
|
||||
doxygen utility_reference
|
||||
:
|
||||
$(utility_includes)
|
||||
:
|
||||
$(doxygen_params)
|
||||
<xsl:param>"boost.doxygen.reftitle=Utilities"
|
||||
;
|
||||
|
||||
notfile utility_refs : @gen-references : utility_reference.xml ;
|
||||
|
||||
doxygen support_reference
|
||||
:
|
||||
$(support_includes)
|
||||
:
|
||||
$(doxygen_params)
|
||||
<xsl:param>"boost.doxygen.reftitle=Other libraries support layer"
|
||||
;
|
||||
|
||||
notfile support_refs : @gen-references : support_reference.xml ;
|
||||
|
||||
|
||||
xml log_doc
|
||||
:
|
||||
log.qbk
|
||||
:
|
||||
<dependency>top_level_refs
|
||||
<dependency>core_refs
|
||||
<dependency>attributes_refs
|
||||
<dependency>expressions_refs
|
||||
<dependency>sources_refs
|
||||
<dependency>sinks_refs
|
||||
<dependency>utility_refs
|
||||
<dependency>support_refs
|
||||
;
|
||||
|
||||
boostbook log
|
||||
:
|
||||
log_doc
|
||||
:
|
||||
<xsl:param>boost.root=../../../..
|
||||
<xsl:param>boost.libraries=../../../libs/libraries.htm
|
||||
<xsl:param>nav.layout=none
|
||||
<xsl:param>boost.image=Boost
|
||||
<xsl:param>navig.graphics=1
|
||||
<xsl:param>chunk.section.depth=2
|
||||
<xsl:param>boost.compact.function=0
|
||||
<format>pdf:<xsl:param>boost.url.prefix=http://www.boost.org/doc/libs/release/libs/log/doc/html
|
||||
<format>pdf:<xsl:param>img.src.path=$(images_location)/
|
||||
;
|
||||
|
||||
# install html : ../../../doc/src/boostbook.css ;
|
589
doc/attributes.qbk
Normal file
589
doc/attributes.qbk
Normal file
@ -0,0 +1,589 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:attributes Attributes]
|
||||
|
||||
#include <``[boost_log_attributes_attribute_hpp]``>
|
||||
#include <``[boost_log_attributes_attribute_cast_hpp]``>
|
||||
#include <``[boost_log_attributes_attribute_value_hpp]``>
|
||||
|
||||
All attributes in the library are implemented using the [@http://c2.com/cgi/wiki?PimplIdiom pimpl idiom], or more specifically - shared pimpl idiom. Every attribute provides an interface class which derives from the [class_log_attribute] class and an implementation class that derives from [class_log_attribute_impl]. The interface class only holds a reference counted pointer to the actual implementation of the attribute; this pointer is a member of the [class_log_attribute] class, so derived interface classes don't have any data members. When the interface class is default constructed, it creates the corresponding implementation object and initializes the [class_log_attribute] base class with a pointer to the implementation. Therefore the pimpl nature of attributes is transparent for users in a typical workflow.
|
||||
|
||||
The shared pimpl design comes significant in a few cases though. One such case is copying the attribute. The copy operation is shallow, so multiple interface objects may refer to a single implementation object. There is no way to deep copy an attribute. Another case is default construction of [class_log_attribute] which creates an empty object that does not refer to an implementation. Attributes in such empty state should not be passed to the library but can be useful in some cases, e.g. when a delayed variable initialization is needed.
|
||||
|
||||
It is possible to upcast the attribute interface from [class_log_attribute] to the actual interface class. To do this one has to apply [funcref boost::log::attribute_cast `attribute_cast`]:
|
||||
|
||||
logging::attribute attr = ...;
|
||||
attrs::constant< int > const_attr = logging::attribute_cast< attrs::constant< int > >(attr);
|
||||
|
||||
In this example, the cast will succeed (i.e. the `const_attr` will be non-empty) if the attribute `attr` was originally created as `attrs::constant< int >`. Since all data is stored in the implementation object, no data is lost in the casting process.
|
||||
|
||||
The main purpose of attributes is to generate attribute values. Values are semantically distinct from the attributes. Such separation allows implementing attributes that can return different values at different time points (like clock-related attributes, for example) and, on the other hand, allows using different values of the same attribute independently. The [class_log_attribute] interface has a method named `get_value` that returns the actual attribute value. Attribute values are also implemented using the shared pimpl approach, the interface class is [class_log_attribute_value] and implementation classes derive from [class_log_attribute_value_impl].
|
||||
|
||||
The attribute value object is mostly intended to store the actual attribute value and implement type dispatching in order to be able to extract the stored value. One should not confuse the attribute value object type and the stored value type. The former is in most cases not needed by users and provides type erasure, but the latter is needed to be able to extract the value. For brevity we call the stored attribute value type simply the attribute value type in this documentation.
|
||||
|
||||
[section:constant Constants]
|
||||
|
||||
#include <``[boost_log_attributes_constant_hpp]``>
|
||||
|
||||
The most simple and frequently used attribute type is a constant value of some type. This kind of attribute is implemented with the [class_attributes_constant] class template. The template is parametrized with the attribute value type. The constant value should be passed to the attribute constructor. Here is an example:
|
||||
|
||||
void foo()
|
||||
{
|
||||
src::logger lg;
|
||||
|
||||
// Register a constant attribute that always yields value -5
|
||||
lg.add_attribute("MyInteger", attrs::constant< int >(-5));
|
||||
|
||||
// Register another constant attribute. Make it a string this time.
|
||||
lg.add_attribute("MyString", attrs::constant< std::string >("Hello world!"));
|
||||
|
||||
// There is also a convenience generator function. "MyInteger2" is constant< int > here.
|
||||
lg.add_attribute("MyInteger2", attrs::make_constant(10));
|
||||
}
|
||||
|
||||
That's it, there's nothing much you can do with a constant attribute. Constants are very useful when one wants to highlight some log records or just pass some data to a sink backend (e.g. pass statistical parameters to the collector).
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:mutable_constant Mutable constants]
|
||||
|
||||
#include <``[boost_log_attributes_mutable_constant_hpp]``>
|
||||
|
||||
This kind of attribute is an extension for the [link log.detailed.attributes.constant constant attribute]. In addition to being able to store some value, the [class_attributes_mutable_constant] class template has two distinctions:
|
||||
|
||||
* it allows modification of the stored value without re-registering the attribute
|
||||
* it allows synchronization of the stores and reads of the stored value
|
||||
|
||||
In order to change the stored value of the attribute, one must call the `set` method:
|
||||
|
||||
void foo()
|
||||
{
|
||||
src::logger lg;
|
||||
|
||||
// Register a mutable constant attribute that always yields value -5
|
||||
attrs::mutable_constant< int > attr(-5);
|
||||
lg.add_attribute("MyInteger", attr);
|
||||
BOOST_LOG(lg) << "This record has MyInteger == -5";
|
||||
|
||||
// Change the attribute value
|
||||
attr.set(100);
|
||||
BOOST_LOG(lg) << "This record has MyInteger == 100";
|
||||
}
|
||||
|
||||
In multithreaded applications the `set` method calls must be serialized with the `get_value` calls (which, generally speaking, happen on every log record being made). By default [class_attributes_mutable_constant] does not serialize calls in any way, assuming that the user will do so externally. However, the [class_attributes_mutable_constant] template provides three additional template arguments: synchronization primitive type, scoped exclusive lock type and scoped shareable lock type. If a synchronization primitive type is specified, the scoped exclusive lock type is a mandatory parameter. If the scoped shareable lock type is not specified, the attribute will fall back to the exclusive lock instead of shared locks. For example:
|
||||
|
||||
// This mutable constant will always lock exclusively
|
||||
// either for reading or storing the value
|
||||
typedef attrs::mutable_constant<
|
||||
int, // attribute value type
|
||||
boost::mutex, // synchronization primitive
|
||||
boost::lock_guard< boost::mutex > // exclusive lock type
|
||||
> exclusive_mc;
|
||||
exclusive_mc my_int1(10);
|
||||
|
||||
// This mutable constant will use shared clocking for reading the value
|
||||
// and exclusive locking for storing
|
||||
typedef attrs::mutable_constant<
|
||||
int, // attribute value type
|
||||
boost::shared_mutex, // synchronization primitive
|
||||
boost::unique_lock< boost::shared_mutex >, // exclusive lock type
|
||||
boost::shared_lock< boost::shared_mutex > // shared lock type
|
||||
> shared_mc;
|
||||
shared_mc my_int2(20);
|
||||
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(my_logger, src::logger_mt)
|
||||
{
|
||||
src::logger_mt lg;
|
||||
lg.add_attribute("MyInteger1", my_int1);
|
||||
lg.add_attribute("MyInteger2", my_int2);
|
||||
|
||||
return lg;
|
||||
}
|
||||
|
||||
void foo()
|
||||
{
|
||||
src::logger_mt& lg = get_my_logger();
|
||||
|
||||
// This is safe, even if executed in multiple threads
|
||||
my_int1.set(200);
|
||||
BOOST_LOG(lg) << "This record has MyInteger1 == 200";
|
||||
|
||||
my_int2.set(300);
|
||||
BOOST_LOG(lg) << "This record has MyInteger2 == 300";
|
||||
}
|
||||
|
||||
Mutable constants are often used as auxiliary attributes inside loggers to store attributes that may change on some events. As opposed to regular constants, which would require re-registering in case of value modification, mutable constants allow modifying the value in-place.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:counter Counters]
|
||||
|
||||
#include <``[boost_log_attributes_counter_hpp]``>
|
||||
|
||||
Counters are one of the simplest attributes that generate a new value each time requested. Counters are often used to identify log records or to count some events, e.g. accepted network connections. The class template [class_attributes_counter] provides such functionality. This template is parametrized with the counter value type, which should support arithmetic operations, such as `operator +` and `operator -`. The counter attribute allows specification of the initial value and step (which can be negative) on construction.
|
||||
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(my_logger, src::logger_mt)
|
||||
{
|
||||
src::logger_mt lg;
|
||||
|
||||
// This counter will count lines, starting from 0
|
||||
lg.add_attribute("LineCounter", attrs::counter< unsigned int >());
|
||||
|
||||
// This counter will count backwards, starting from 100 with step -5
|
||||
lg.add_attribute("CountDown", attrs::counter< int >(100, -5));
|
||||
|
||||
return lg;
|
||||
}
|
||||
|
||||
void foo()
|
||||
{
|
||||
src::logger_mt& lg = get_my_logger();
|
||||
BOOST_LOG(lg) << "This record has LineCounter == 0, CountDown == 100";
|
||||
BOOST_LOG(lg) << "This record has LineCounter == 1, CountDown == 95";
|
||||
BOOST_LOG(lg) << "This record has LineCounter == 2, CountDown == 90";
|
||||
}
|
||||
|
||||
[note Don't expect that the log records with the [class_attributes_counter] attribute will always have ascending or descending counter values in the resulting log. In multithreaded applications counter values acquired by different threads may come to a sink in any order. See [link log.rationale.why_weak_record_ordering Rationale] for a more detailed explanation on why it can happen. For this reason it is more accurate to say that the [class_attributes_counter] attribute generates an identifier in an ascending or descending order rather than that it counts log records in either order.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:clock Wall clock]
|
||||
|
||||
#include <``[boost_log_attributes_clock_hpp]``>
|
||||
|
||||
One of the "must-have" features of any logging library is support for attaching a time stamp to every log record. The library provides two attributes for this purpose: `utc_clock` and `local_clock`. The former returns the current UTC time and the latter returns the current local time. In either case the returned time stamp is acquired with the maximum precision for the target platform. The attribute value is `boost::posix_time::ptime` (see __boost_date_time__). The usage is quite straightforward:
|
||||
|
||||
BOOST_LOG_DECLARE_GLOBAL_LOGGER(my_logger, src::logger_mt)
|
||||
|
||||
void foo()
|
||||
{
|
||||
logging::core::get()->add_global_attribute(
|
||||
"TimeStamp",
|
||||
attrs::local_clock());
|
||||
|
||||
// Now every log record ever made will have a time stamp attached
|
||||
src::logger_mt& lg = get_my_logger();
|
||||
BOOST_LOG(lg) << "This record has a time stamp";
|
||||
}
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:timer Stop watch (timer)]
|
||||
|
||||
#include <``[boost_log_attributes_timer_hpp]``>
|
||||
|
||||
The [class_attributes_timer] attribute is very useful when there is a need to estimate the duration of some prolonged process. The attribute returns the time elapsed since the attribute construction. The attribute value type is `boost::posix_time::ptime::time_duration_type` (see __boost_date_time__).
|
||||
|
||||
// The class represents a single peer-to-peer connection
|
||||
class network_connection
|
||||
{
|
||||
src::logger m_logger;
|
||||
|
||||
public:
|
||||
network_connection()
|
||||
{
|
||||
m_logger.add_attribute("Duration", attrs::timer());
|
||||
BOOST_LOG(m_logger) << "Connection established";
|
||||
}
|
||||
~network_connection()
|
||||
{
|
||||
// This log record will show the whole life time duration of the connection
|
||||
BOOST_LOG(m_logger) << "Connection closed";
|
||||
}
|
||||
};
|
||||
|
||||
The attribute provides high resolution of the time estimation and can even be used as a simple in-place performance profiling tool.
|
||||
|
||||
[tip The [class_attributes_timer] attribute can even be used to profile the code in different modules without recompiling them. The trick is to wrap an expensive call to a foreign module with the thread-specific [class_attributes_timer] [link log.detailed.attributes.related_components.scoped_attributes scoped attribute], which will markup all log records made from within the module with time readings.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:named_scope Named scopes]
|
||||
|
||||
#include <``[boost_log_attributes_named_scope_hpp]``>
|
||||
|
||||
// Supporting headers
|
||||
#include <``[boost_log_support_exception_hpp]``>
|
||||
|
||||
The logging library supports maintaining scope stack tracking during the application's execution. This stack may either be written to log or be used for other needs (for example, to save the exact call sequence that led to an exception when throwing one). Each stack element contains the following information (see the [class_attributes_named_scope_entry] structure template definition):
|
||||
|
||||
* Scope name. It can be defined by the user or generated by the compiler, but in any case it [_must be a constant string literal] (see [link log.rationale.why_str_lit Rationale]).
|
||||
* Source file name, where the scope begins. It is usually a result of the standard `__FILE__` macro expansion. Like the scope name, the file name [_must be a constant string literal].
|
||||
* Line number in the source file. Usually it is a result of the standard `__LINE__` macro expansion.
|
||||
|
||||
The scope stack is implemented as a thread-specific global storage internally. There is the [class_attributes_named_scope] attribute that allows hooking this stack into the logging pipeline. This attribute generates value of the nested type `named_scope::scope_stack` which is the instance of the scope stack. The attribute can be registered in the following way:
|
||||
|
||||
logging::core::get()->add_global_attribute("Scope", attrs::named_scope());
|
||||
|
||||
Note that it is perfectly valid to register the attribute globally because the scope stack is thread-local anyway. This will also implicitly add scope tracking to all threads of the application, which is often exactly what is needed.
|
||||
|
||||
Now we can mark execution scopes with the macros `BOOST_LOG_FUNCTION` and `BOOST_LOG_NAMED_SCOPE` (the latter accepts the scope name as its argument). These macros automatically add source position information to each scope entry. An example follows:
|
||||
|
||||
void foo(int n)
|
||||
{
|
||||
// Mark the scope of the function foo
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
switch (n)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Mark the current scope
|
||||
BOOST_LOG_NAMED_SCOPE("case 0");
|
||||
BOOST_LOG(lg) << "Some log record";
|
||||
bar(); // call some function
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
{
|
||||
// Mark the current scope
|
||||
BOOST_LOG_NAMED_SCOPE("case 1");
|
||||
BOOST_LOG(lg) << "Some log record";
|
||||
bar(); // call some function
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
// Mark the current scope
|
||||
BOOST_LOG_NAMED_SCOPE("default");
|
||||
BOOST_LOG(lg) << "Some log record";
|
||||
bar(); // call some function
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
After executing `foo` we will be able to see in the log that the `bar` function was called from `foo` and, more precisely, from the case statement that corresponds to the value of `n`. This may be very useful when tracking down subtle bugs that show up only when `bar` is called from a specific location (e.g. if `bar` is being passed invalid arguments in that particular location).
|
||||
|
||||
Another good use case is attaching the scope stack information to an exception. With the help of __boost_exception__, this is possible:
|
||||
|
||||
void bar(int x)
|
||||
{
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
if (x < 0)
|
||||
{
|
||||
// Attach a copy of the current scope stack to the exception
|
||||
throw boost::enable_error_info(std::range_error("x must not be negative"))
|
||||
<< logging::current_scope();
|
||||
}
|
||||
}
|
||||
|
||||
void foo()
|
||||
{
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
try
|
||||
{
|
||||
bar(-1);
|
||||
}
|
||||
catch (std::range_error& e)
|
||||
{
|
||||
// Acquire the scope stack from the exception object
|
||||
BOOST_LOG(lg) << "bar call failed: " << e.what() << ", scopes stack:\n"
|
||||
<< *boost::get_error_info< logging::current_scope_info >(e);
|
||||
}
|
||||
}
|
||||
|
||||
[note In order this code to compile, the __boost_exception__ support header has to be included.]
|
||||
|
||||
[note We do not inject the [class_attributes_named_scope] attribute into the exception. Since scope stacks are maintained globally, throwing an exception will cause stack unwinding and, as a result, will truncate the global stack. Instead we create a copy of the scope stack bu calling [funcref boost::log::current_scope `current_scope`] at the throw site. This copy will be kept intact even if the global stack instance changes during the stack unwinding.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:process_id Current process identifier]
|
||||
|
||||
#include <``[boost_log_attributes_current_process_id_hpp]``>
|
||||
|
||||
It is often useful to know the process identifier that produces the log, especially if the log can eventually combine the output of different processes. The [class_attributes_current_process_id] attribute is a constant that formats into the current process identifier. The value type of the attribute can be determined by the `current_process_id::value_type` typedef.
|
||||
|
||||
void foo()
|
||||
{
|
||||
logging::core::get()->add_global_attribute(
|
||||
"ProcessID",
|
||||
attrs::current_process_id());
|
||||
}
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:process_name Current process name]
|
||||
|
||||
#include <``[boost_log_attributes_current_process_name_hpp]``>
|
||||
|
||||
The [class_attributes_current_process_name] produces `std::string` values with the executable name of the current process.
|
||||
|
||||
[note This attribute is not universally portable, although Windows, Linux and OS X are supported. The attribute may work on other POSIX systems as well, but it was not tested. If the process name cannot be obtained the attribute will generate a string with the process id.]
|
||||
|
||||
void foo()
|
||||
{
|
||||
logging::core::get()->add_global_attribute(
|
||||
"Process",
|
||||
attrs::current_process_name());
|
||||
}
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:thread_id Current thread identifier]
|
||||
|
||||
#include <``[boost_log_attributes_current_thread_id_hpp]``>
|
||||
|
||||
Multithreaded builds of the library also support the [class_attributes_current_thread_id] attribute with value type `current_thread_id::value_type`. The attribute will generate values specific to the calling thread. The usage is similar to the process id.
|
||||
|
||||
void foo()
|
||||
{
|
||||
logging::core::get()->add_global_attribute(
|
||||
"ThreadID",
|
||||
attrs::current_thread_id());
|
||||
}
|
||||
|
||||
[tip You may have noticed that the attribute is registered globally. This will not result in all threads having the same ThreadID in log records as the attribute will always return a thread-specific value. The additional benefit is that you don't have to do a thing in the thread initialization routines to have the thread-specific attribute value in log records.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:function Function objects as attributes]
|
||||
|
||||
#include <``[boost_log_attributes_function_hpp]``>
|
||||
|
||||
This attribute is a simple wrapper around a user-defined function object. Each attempt to acquire the attribute value results in the function object call. The result of the call is returned as the attribute value (this implies that the function must not return `void`). The function object attribute can be constructed with the `make_function` helper function, like this:
|
||||
|
||||
void foo()
|
||||
{
|
||||
logging::core::get()->add_global_attribute("MyRandomAttr", attrs::make_function(&std::rand));
|
||||
}
|
||||
|
||||
Auto-generated function objects, like the ones defined in __boost_bind__ or STL, are also supported.
|
||||
|
||||
[note Some deficient compilers may not support `result_of` construct properly. This metafunction is used in the `make_function` function to automatically detect the return type of the function object. If `result_of` breaks or detects incorrect type, one can try to explicitly specify the return type of the function object as a template argument to the `make_function` function.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:related_components Other attribute-related components]
|
||||
|
||||
[section:attribute_name Attribute names]
|
||||
|
||||
#include <``[boost_log_attributes_attribute_name_hpp]``>
|
||||
|
||||
Attribute names are represented with [class_log_attribute_name] objects which are used as keys in associative containers of attributes used by the library. The [class_log_attribute_name] object can be created from a string, so most of the time its use is transparent.
|
||||
|
||||
The name is not stored as a string within the [class_log_attribute_name] object. Instead, a process-wide unique identifier is generated and associated with the particular name. This association is preserved until the process termination, so every time the [class_log_attribute_name] object is created for the same name it obtains the same identifier. The association is not stable across the different runs of the application though.
|
||||
|
||||
[warning Since the association between string names and identifiers involves some state allocation, it is not advised to use externally provided or known to be changing strings for attribute names. Even if the name is not used in any log records, the association is preserved anyway. Continuously constructing [class_log_attribute_name] objects with unique string names may manifest itself as a memory leak.]
|
||||
|
||||
Working with identifiers is much more efficient than with strings. For example, copying does not involve dynamic memory allocation and comparison operators are very lightweight. On the other hand, it is easy to get a human-readable attribute name for presentation, if needed.
|
||||
|
||||
The [class_log_attribute_name] class supports an empty (uninitialized) state when default constructed. In this state the name object is not equal to any other initialized name object. Unitnitialized attribute names should not be passed to the library but can be useful in some contexts (e.g. when a delayed initialization is desired).
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:attribute_set Attribute set]
|
||||
|
||||
#include <``[boost_log_attributes_attribute_set_hpp]``>
|
||||
|
||||
Attribute set is an unordered associative container that maps [link log.detailed.attributes.related_components.attribute_name attribute names] to [link log.detailed.attributes attributes]. It is used in [link log.detailed.sources loggers] and the [link log.detailed.core.core logging core] to store source-specific, thread-specific and global attributes. The interface is very similar to STL associative containers and is described in the [class_log_attribute_set] class reference.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:attribute_value_set Attribute value set]
|
||||
|
||||
#include <``[boost_log_attributes_attribute_value_set_hpp]``>
|
||||
|
||||
Attribute value set is an unordered assocoative container that maps [link log.detailed.attributes.related_components.attribute_name attribute names] to [link log.detailed.attributes attribute values]. This container is used in log [link log.detailed.core.record records] to represent attribute values. Unlike conventional containers, [class_log_attribute_value_set] does not support removing or modifying elements after being inserted. This warrants that the attribute values that participated filtering will not disappear from the log record in the middle of the processing.
|
||||
|
||||
Additionally, the set can be constructed from three [link log.detailed.attributes.related_components.attribute_set attribute sets], which are interpreted as the sets of source-specific, thread-specific and global attributes. The constructor adopts attribute values from the three attribute sets into a single set of attribute values. After construction, [class_log_attribute_value_set] is considered to be in an unfrozen state. This means that the container may keep references to the elements of the attribute sets used as the source for the value set construction. While in this state, neither the attribute sets nor the value set must not be modified in any way as this may make the value set corrupted. The value set can be used for reading in this state, its lookup operations will perform as usual. The value set can be frozen by calling the `freeze` method; the set will no longer be attached to the original attribute sets and will be available for further insertions after this call. The library will ensure that the value set is always frosen when a log record is returned from the logging core; the set is [_not] frozen during filtering though.
|
||||
|
||||
[tip In the unfrozen state the value set may not have all attribute values acquired from the attributes. It will only acquire the values as requested by filters. After freezing the container has all attribute values. This transition allows to optimize the library so that attribute values are only acquired when needed.]
|
||||
|
||||
For futher details on the container interface please consult the [class_log_attribute_value_set] reference.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:value_processing Attribute value extraction and visitation]
|
||||
|
||||
Since attribute values do not expose the stored value in the interface, an API is needed to acquire the stored value. The library provides two APIs for this purpose: value visitation and extraction.
|
||||
|
||||
[section:visitation Value visitation]
|
||||
|
||||
#include <``[boost_log_attributes_value_visitation_fwd_hpp]``>
|
||||
#include <``[boost_log_attributes_value_visitation_hpp]``>
|
||||
|
||||
Attribute value visitation implements the visitor design pattern, hence the naming. The user has to provide an unary function object (a visitor) which will be invoked on the stored attribute value. The caller also has to provide the expected type or set of possible types of the stored value. Obviously, the visitor must be capable of receiving an argument of the expected type. Visitation will only succeed if the stored type matches the expectation.
|
||||
|
||||
In order to apply the visitor, one should call the [funcref boost::log::visit `visit`] function on the attribute value. Let's see an example:
|
||||
|
||||
[example_attr_value_visitation]
|
||||
|
||||
[@boost:/libs/log/example/doc/attr_value_visitation.cpp See the complete code].
|
||||
|
||||
In this example we print the stored attribute value in our `print_visitor`. We expect the attribute value to have either `int` or `std::string` stored type; only in this case the visitor will be invoked and the visitation result will be positive. In case of failure the [class_log_visitation_result] class provides additional information on the failure reason. The class has the method named `code` which returns visitation error code. The following error codes are possible:
|
||||
|
||||
* `ok` - visitation succeeded, the visitor has been invoked; visitation result is positive when this code is used
|
||||
* `value_not_found` - visitation failed because the requested value was not found; this code is used when visitation is applied to a log record or a set of attrinute values rather than a single value
|
||||
* `value_has_invalid_type` - visitation failed because the value has type differing from any of the expected types
|
||||
|
||||
By default the visitor function result is ignored but it is possible to obtain it. To do this one should use a special [funcref boost::log::save_result `save_result`] wrapper for the visitor; the wrapper will save the visitor resulting value into an external variable captured by reference. The visitor result is initialized when the returned [class_log_visitation_result] is positive. See the following example where we compute the hash value on the stored value.
|
||||
|
||||
[example_attr_value_visitation_with_retval]
|
||||
|
||||
[@boost:/libs/log/example/doc/attr_value_visitation.cpp See the complete code].
|
||||
|
||||
[tip When there is no default state for the visitor result it is convenient to use __boost_optional__ to wrap the returned value. The `optional` will be initialized with the visitor result if visitation succeeded. In case if visitor is polymorphic (i.e. it has different result types depending on its argument type) __boost_variant__ can be used to receive the resulting value. It is also worthwhile to use an empty type, such as `boost::blank`, to indicate the uninitialized state of the `variant`.]
|
||||
|
||||
As it has been mentioned, visitation can also be applied to log records and attribute value sets. The syntax is the same, except that the attribute name also has to be specified. The [funcref boost::log::visit `visit`] algorithm will try to find the attribute value by name and then apply the visitor to the found element.
|
||||
|
||||
[example_attr_value_visitation_with_retval_rec]
|
||||
|
||||
Also, for convenience [class_log_attribute_value] has the method named `visit` with the same meaning as the free function applied to the attribute value.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:extraction Value extraction]
|
||||
|
||||
#include <``[boost_log_attributes_value_extraction_fwd_hpp]``>
|
||||
#include <``[boost_log_attributes_value_extraction_hpp]``>
|
||||
|
||||
Attribute value extraction API allows to acquire a reference to the stored value. It does not require a visitor function object, but the user still has to provide the expected type or a set of types the stored value may have.
|
||||
|
||||
[example_attr_value_extraction]
|
||||
|
||||
[@boost:/libs/log/example/doc/attr_value_extraction.cpp See the complete code].
|
||||
|
||||
In this example we expect the attribute value to have the stored type `int`. The [funcref boost::log::extract `extract`] function attempts to extract a reference to the stored value and returns the filled [link log.detailed.utilities.value_ref `value_ref`] object if succeeded.
|
||||
|
||||
Value extraction can also be used with a set of expected stored types. The follwoing code snippet demonstrates this:
|
||||
|
||||
[example_attr_value_extraction_multiple_types]
|
||||
|
||||
Notice that we used `which` method of the returned reference to dispatch between possible types. The method returns the index of the type in the `types` sequence. Also note that the `get` method now accepts an explicit template parameter to select the reference type to acquire; naturally, this type must correspond to the actual referred type, which is warranted by the switch/case statement in our case.
|
||||
|
||||
Value visitation is also supported by the [link log.detailed.utilities.value_ref `value_ref`] object. Here is how we compute a hash value from the extracted value:
|
||||
|
||||
[example_attr_value_extraction_visitor]
|
||||
|
||||
Lastly, like with value visitation, value extraction can also be applied to log records and attribute value sets.
|
||||
|
||||
[example_attr_value_extraction_visitor_rec]
|
||||
|
||||
In addition the library provides two special variants of the [funcref boost::log::extract `extract`] function: [funcref boost::log::extract_or_throw `extract_or_throw`] and [funcref boost::log::extract_or_default `extract_or_default`]. As the naming implies, the functions provide different behavior in case if the attribute value cannot be extracted. The former one throws an exception if the value cannot be extracted and the latter one returns the default value.
|
||||
|
||||
[warning Care must be taken with the [funcref boost::log::extract_or_default `extract_or_default`] function. The function accepts the default value is accepted by constant reference, and this reference can eventually be returned from [funcref boost::log::extract_or_default `extract_or_default`]. If a temporary object as used for the default value, user must ensure that the result of [funcref boost::log::extract_or_default `extract_or_default`] is saved by value and not by reference. Otherwise the saved reference may become dangling when the temporary is destroyed.]
|
||||
|
||||
Similarly to `visit`, the [class_log_attribute_value] class has methods named `extract`, `extract_or_throw` and `extract_or_default` with the same meaning as the corresponding free functions applied to the attribute value.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:scoped_attributes Scoped attributes]
|
||||
|
||||
#include <``[boost_log_attributes_scoped_attribute_hpp]``>
|
||||
|
||||
Scoped attributes are a powerful mechanism of tagging log records that can be used for different purposes. As the naming implies, scoped attributes are registered in the beginning of a scope and unregistered on the end of the scope. The mechanism includes the following macros:
|
||||
|
||||
``[macroref BOOST_LOG_SCOPED_LOGGER_ATTR]``(logger, attr_name, attr);
|
||||
``[macroref BOOST_LOG_SCOPED_THREAD_ATTR]``(attr_name, attr);
|
||||
|
||||
The first macro registers a source-specific attribute in the `logger` logger object. The attribute name and the attribute itself are given in the `attr_name` and `attr` arguments. The second macro does exactly the same but the attribute is registered for the current thread in the logging core (which does not require a logger).
|
||||
|
||||
[note If an attribute with the same name is already registered in the logger/logging core, the macros won't override the existing attribute and will eventually have no effect. See [link log.rationale.why_weak_scoped_attributes Rationale] for a more detailed explanation of the reasons for such behavior.]
|
||||
|
||||
Usage example follows:
|
||||
|
||||
BOOST_LOG_DECLARE_GLOBAL_LOGGER(my_logger, src::logger_mt)
|
||||
|
||||
void foo()
|
||||
{
|
||||
// This log record will also be marked with the "Tag" attribute,
|
||||
// whenever it is called from the A::bar function.
|
||||
// It will not be marked when called from other places.
|
||||
BOOST_LOG(get_my_logger()) << "A log message from foo";
|
||||
}
|
||||
|
||||
struct A
|
||||
{
|
||||
src::logger m_Logger;
|
||||
|
||||
void bar()
|
||||
{
|
||||
// Set a thread-wide markup tag.
|
||||
// Note the additional parentheses to form a Boost.PP sequence.
|
||||
BOOST_LOG_SCOPED_THREAD_ATTR("Tag",
|
||||
attrs::constant< std::string >("Called from A::bar"));
|
||||
|
||||
// This log record will be marked
|
||||
BOOST_LOG(m_Logger) << "A log message from A::bar";
|
||||
|
||||
foo();
|
||||
}
|
||||
};
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
src::logger lg;
|
||||
|
||||
// Let's measure our application run time
|
||||
BOOST_LOG_SCOPED_LOGGER_ATTR(lg, "RunTime", attrs::timer());
|
||||
|
||||
// Mark application start.
|
||||
// The "RunTime" attribute should be nearly 0 at this point.
|
||||
BOOST_LOG(lg) << "Application started";
|
||||
|
||||
// Note that no other log records are affected by the "RunTime" attribute.
|
||||
foo();
|
||||
|
||||
A a;
|
||||
a.bar();
|
||||
|
||||
// Mark application ending.
|
||||
// The "RunTime" attribute will show the execution time elapsed.
|
||||
BOOST_LOG(lg) << "Application ended";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
It is quite often convenient to mark a group of log records with a constant value in order to be able to filter the records later. The library provides two convenience macros just for this purpose:
|
||||
|
||||
``[macroref BOOST_LOG_SCOPED_LOGGER_TAG]``(logger, tag_name, tag_value);
|
||||
``[macroref BOOST_LOG_SCOPED_THREAD_TAG]``(tag_name, tag_value);
|
||||
|
||||
The macros are effectively wrappers around [macroref BOOST_LOG_SCOPED_LOGGER_ATTR] and [macroref BOOST_LOG_SCOPED_THREAD_ATTR], respectively. For example, the "Tag" scoped attribute from the example above can be registered like this:
|
||||
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("Tag", "Called from A::bar");
|
||||
|
||||
[warning When using scoped attributes, make sure that the scoped attribute is not altered in the attribute set in which it was registered. For example, one should not clear or reinstall the attribute set of the logger if there are logger-specific scoped attributes registered in it. Otherwise the program will likely crash. This issue is especially critical in multithreaded application, when one thread may not know whether there are scoped attributes in the logger or there are not. Future releases may solve this limitation but currently the scoped attribute must remain intact until unregistered on leaving the scope.]
|
||||
|
||||
Although the described macros are intended to be the primary interface for the functionality, there is also a C++ interface available. It may be useful if the user decides to develop his own macros that cannot be based on the existing ones.
|
||||
|
||||
Any scoped attribute is attached to a generic sentry object of type `scoped_attribute`. As long as the sentry exists, the attribute is registered. There are several functions that create sentries for source or thread-specific attributes:
|
||||
|
||||
// Source-specific scoped attribute registration
|
||||
template< typename LoggerT >
|
||||
[unspecified] add_scoped_logger_attribute(
|
||||
LoggerT& l,
|
||||
attribute_name const& name,
|
||||
attribute const& attr);
|
||||
|
||||
// Thread-specific scoped attribute registration
|
||||
template< typename CharT >
|
||||
[unspecified] add_scoped_thread_attribute(
|
||||
attribute_name const& name,
|
||||
attribute const& attr);
|
||||
|
||||
An object of the `scoped_attribute` type is able to attach results of each of these functions on its construction. For example, `BOOST_LOG_SCOPED_LOGGER_ATTR(lg, "RunTime", attrs::timer())` can roughly be expanded to this:
|
||||
|
||||
attrs::scoped_attribute sentry =
|
||||
attrs::add_scoped_logger_attribute(lg, "RunTime", attrs::timer());
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
144
doc/changelog.qbk
Normal file
144
doc/changelog.qbk
Normal file
@ -0,0 +1,144 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:changelog Changelog]
|
||||
|
||||
[heading 2.0]
|
||||
|
||||
[*General changes:]
|
||||
|
||||
* The library is now compatible with Boost 1.53 or newer. __boost_filesystem__ v2 no longer supported.
|
||||
* The library now does not introduce separate logging cores for different character types. A lot of other library components also became character type agnostic. The application can now use loggers of different character types with the common logging core. The library performs character code conversion as needed. __boost_locale__ can be used to construct locale objects for proper encoding conversion.
|
||||
* The `BOOST_LOG_NO_COMPILER_TLS` configuration macro has been replaced with `BOOST_LOG_USE_COMPILER_TLS` with the opposite meaning. The support for compiler intrinsics for TLS is now disabled by default.
|
||||
* Added configuration macros `BOOST_LOG_WITHOUT_DEBUG_OUTPUT`, `BOOST_LOG_WITHOUT_EVENT_LOG` and `BOOST_LOG_WITHOUT_SYSLOG`. `BOOST_LOG_NO_SETTINGS_PARSERS_SUPPORT` macro renamed to `BOOST_LOG_WITHOUT_SETTINGS_PARSERS`. The new macros allow to selectively disable support for the corresponding sink backends.
|
||||
* The library now uses __boost_xpressive__ instead of __boost_regex__ internally which makes it unnecessary to build the latter in order to use the library. __boost_regex__ is still supported on the user's side.
|
||||
* Made some internal code to detect Windows NT6 API availability at run time, if not explicitly enabled by the `BOOST_LOG_USE_WINNT6_API` macro. The code compiled without the macro defined will still be able run on NT5, but when run on NT6 it will be more efficient. With the macro defined the resulting code will not run on NT5, but will be a little more efficient on NT6 than without the macro.
|
||||
* Added a concept of a default sink. The default sink is used when there are no sinks configured in the logging core. The sink is synchronous and thread-safe, it requires no configuration and is overridden by any sinks configured in the core by user. The default sink will write log messages to the console, prepending with a timestamp, thread id and severity level.
|
||||
* Trivial logging no longer implicitly initializes the library. Instead, the default sink is used to display log messages, unless the library is configured otherwise. It is now possible to use both trivial and advanced logging.
|
||||
* Attribute values can now be added to log records after filtering. Such values do not participate in filtering but can be used by formatters and sinks. Log record message is now one of such attribute values, it is no longer directly accessible from the log record interface.
|
||||
* Formatters and sinks no longer operate on log records but rather on [class_log_record_view]s. Records are now moved from when pushed to the core for further processing. This is done in order to eliminate the possibility of unsafe record modification after pushing to the core. As a consequence, log records can no longer be copied, only moving is allowed. Record views can be copied and moved; copying is a shallow operation.
|
||||
* The implementation now provides several stream manipulators. Notably, the [link log.detailed.utilities.manipulators.to_log `to_log`] manipulator allows to customize formatting for particular types and attributes without changing the regular streaming operator. Also, the [link log.detailed.utilities.manipulators.add_value `add_value`] manipulator can be used in logging expressions to attach attribute values to the record.
|
||||
* Made a lot of improvements to speedup code compilation.
|
||||
|
||||
[*Attributes:]
|
||||
|
||||
* Changed the interface and the way of handling attribute values. The value is now a pimpl wrapper around the value holder. The [class_log_attribute_value] class in various components of the library is no longer pointed to with `shared_ptr`s but instead is handled by value. This allowed to simplify attribute value handling in simple cases.
|
||||
* Similarly to attribute values, the interface of attributes has been reworked in the pimpl fashion. All attributes now derive from the [class_log_attribute] base class, which holds the reference to the implementation. All attributes now have to be created by value rather than wrapped into `shared_ptr` by user, which makes the code more concise.
|
||||
* Added support for casting attributes from the base class [class_log_attribute] to the actual attribute type. This can be useful when the concrete attribute factory provides additional interfaces.
|
||||
* The attribute value no longer has the `get` method. Use the `extract` function as a replacement.
|
||||
* The key type of attribute sets and attribute values set has been changed. The new key type is called [class_log_attribute_name]. It is constructible from strings, so in most cases users won't need to change the code. See [link log.detailed.attributes.related_components.attribute_name here] for more information.
|
||||
* Attribute values view have been renamed to attribute value set. The container now supports adding more attribute values after being constructed.
|
||||
* Attribute sets and attribute value sets no longer maintain order of elements. Although it wasn't stated explicitly, the containers used to be ordered associative containers. Now the order of elements is unspecified. The implementation has been reworked to speed up insertion/removal of attributes, as well as attribute lookup and values set construction. The drawback is that memory footprint may get increased in some cases.
|
||||
* Attribute sets now use small memory pools to speed up element insertion/removal.
|
||||
* The header `scoped_attribute.hpp` moved from `utility` to the `attributes` directory. The header `attribute_value_extractor.hpp` in `utility` has been replaced with headers [boost_log_attributes_value_extraction_hpp] and [boost_log_attributes_value_visitation_hpp] in the `attributes` directory. The two new headers define the revised API of attribute value extraction and visitation, respectively. See [link log.detailed.attributes.related_components.value_processing here] for more details.
|
||||
* [link log.detailed.attributes.related_components.scoped_attributes Scoped attibute] macros simplified. The attribute constructor arguments are specified next to the attribute type and tag type is no longer required.
|
||||
* The [link log.detailed.attributes.thread_id `current_thread_id`] attribute no longer uses `boost::thread::id` type for thread identification. An internal type is used instead, the type is accessible as `current_thread_id::value_type`. The new thread ids are taken from the underlying OS API and thus more closely correlate to what may be displayed by debuggers and system diagnostic tools.
|
||||
* Added [link log.detailed.attributes.process_name `current_process_name`] attribute. The attribute generates a string with the executable name of the current process.
|
||||
* The `functor` attribute has been renamed to [class_attributes_function]. The generator function has been renamed from `make_functor_attr` to `make_function`. The header has been renamed from `functor.hpp` to `function.hpp`.
|
||||
|
||||
[*Logging sources:]
|
||||
|
||||
* Fixed compilation problems with exception handling logger feature.
|
||||
* Global logger storage made more friendly to the setups in which hidden visibility is set by default.
|
||||
* Added the macros for separated global logger declaration and definition. Old macros have been renamed to better reflect their effect (`BOOST_LOG_DECLARE_GLOBAL_LOGGER_INIT` to `BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT`, `BOOST_LOG_DECLARE_GLOBAL_LOGGER` to `BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT`, `BOOST_LOG_DECLARE_GLOBAL_LOGGER_CTOR_ARGS` to `BOOST_LOG_INLINE_GLOBAL_LOGGER_CTOR_ARGS`). Also, the macros no longer define the `get_logger` free function for logger acquisition. Use `logger::get` instead. See [link log.detailed.sources.global_storage here] for more information.
|
||||
* The channel logger now supports changing the channel name after construction. The channel name can be set either by calling the modifier method or by specifying the name in the logging statement. Added `BOOST_LOG_STREAM_CHANNEL` and `BOOST_LOG_STREAM_CHANNEL_SEV` (as well as their shorthands `BOOST_LOG_CHANNEL` and `BOOST_LOG_CHANNEL_SEV`) macros that allow to specify channel name for the log record.
|
||||
|
||||
[*Logging sinks:]
|
||||
|
||||
* Types for integral constants for syslog and event log were renamed to drop the `_t` suffix.
|
||||
* Formatting functionality moved to sink frontends. Sink backends that support record formatting derive from the `basic_formatting_sink_backend` class template, which indicates to the frontend that record formatting is required. This breaks user-side API of the library: the formatter and locale has to be set to the frontend rather than backend.
|
||||
* Formatting support no longer makes frontend thread synchronization mandatory. Formatting is done prior to locking for processing the record in the backend and can be performed concurrently in multiple threads.
|
||||
* Added support for flushing sinks. A sink backend that supports flushing has to define public method with the following signature: `void flush()`.
|
||||
* Asynchronous sink frontend reworkerd, ordering asynchronous sink removed. The [class_sinks_asynchronous_sink] class template now allows to specify record queueing strategy. Several strategies provided, including [class_sinks_unbounded_fifo_queue] (the default) and [class_sinks_unbounded_ordering_queue] which cover the functionality of asynchronous sink frontends in 1.x releases. See the [link log.detailed.sink_frontends.async asynchronous sink frontend] docs for more details.
|
||||
* Lock-free FIFO record queueing in asynchronous sinks reworked to reduce log record processing stalls.
|
||||
* Added `Append` configuration file parameter for text file sinks. If this parameter is set to `true`, the sink will append log records to the existing log file instead of overwriting it.
|
||||
* Added bounded variants of asynchronous sink frontends. Implemented two strategies to handle queue overflows: either log records are dropped or logging threads are blocked until there is space in the queue.
|
||||
|
||||
[*Filters and formatters:]
|
||||
|
||||
* As a result of character type unification, filters no longer depend on the character type.
|
||||
* Two new types were introduced to dynamically store filters and formatters: [class_log_filter] and [class_log_basic_formatter]. Both new types implement type erasure and provide function calling operators to invoke the stored filter or formatter.
|
||||
* Filters and formatters were rewritten. The new implementation is based on __boost_phoenix__ and resides in the `expressions` namespace. Attribute placeholders are now interoperable with other template expressions based on __boost_phoenix__. All template expression headers now reside in the `expressions` subdirectory.
|
||||
* The library now supports defining keywords for attributes (see `BOOST_LOG_ATTRIBUTE_KEYWORD` macro). Keywords can be used in template expressions instead of attribute placeholders and also as a key in container lookups.
|
||||
* Filters and formatters do not throw exceptions by default when an attribute value cannot be used to complete the function (e.g. when the value is missing or has inappropriate type). The offending filter subexpression will return `false` in such cases, the formatter will result in empty string instead of the value. The behavior can be changed by calling `or_default` or `or_throw` member functions on the attribute value placeholder in the filtering/formatting expression.
|
||||
* Date and time formatter implementation is not based on __boost_date_time__ IO facets anymore. The new implementation improves formatting performance. The formatter has been renamed to [link log.detailed.expressions.formatters.date_time `format_date_time`].
|
||||
* Named scope formatter now supports scope format specification. The scope format can include the scope name, as well as file name and line number. The formatter has been renamed to [link log.detailed.expressions.formatters.named_scope `format_named_scope`].
|
||||
* [link log.detailed.expressions.formatters.decorators Character decorators] were renamed to `c_decor`, `c_ascii_decor`, `xml_decor` and `csv_decor`. The generic character decorator is named `char_decor` now.
|
||||
* Added a new [link log.detailed.expressions.predicates.channel_severity_filter channel severity filter]. The filter allows to setup severity thresholds for different channels. The filter checks log record severity level against the threshold corresponding to the channel the record belongs to.
|
||||
|
||||
[*Documentation changes:]
|
||||
|
||||
* Most code examples from the docs have been extracted into compilable standalone examples, which can be used for testing and experimenting with the library.
|
||||
* Added a lot of cross-references to the documentation, which should simplify navigation.
|
||||
|
||||
[*Miscellaneous:]
|
||||
|
||||
* Fixed a bug: the logging core could enter an infinite loop inside `push_record` if a sink throws and the exception is suppressed by the exception handler set in the core.
|
||||
* Changed the type dispatching implementation to reduce the usage of virtual functions. This greatly reduced the library size.
|
||||
* Type dispatchers made more friendly to the setups in which hidden visibility is set by default.
|
||||
* The interface of type dispatchers changed. The dispatcher now returns `type_visitor` instance by value, and the visitor is no longer a base for the actual receiver of the dispatched value. Instead, the visitor now refers to the receiver, if one is capable to consume the value. The `visit` method has been renamed to `operator ()`. The static type dispatcher now requires a reference to the receiver on construction, it doesn't imply that the receiver derives from the dispatcher anymore.
|
||||
* The `slim_string` utility has been removed. There is no replacement.
|
||||
* The library now uses many features from the latest C++ standard (aka C++11). For instance, many library components now support move semantics. __boost_move__ is used for move emulation on C++03-compatible compilers.
|
||||
|
||||
[heading 1.1, 02 December 2011]
|
||||
|
||||
This release mostly fixes bugs in the code and documentation.
|
||||
|
||||
* Added support for __boost_filesystem__ v3.
|
||||
* A number of bugs fixed.
|
||||
* Corrections in the documentation.
|
||||
|
||||
[heading 1.0, 09 May 2010]
|
||||
|
||||
This release mostly fixes bugs in the code and documentation. The next major release (2.0) will contain breaking changes and feature additions. The 1.0 branch will not receive any feature updates.
|
||||
|
||||
* Added some optimization for thread local storage. In Windows setups, if you dynamically load Boost.Log binaries during the application run time, this optimization may lead to crashes. In that case, you may disable it by defining `BOOST_LOG_NO_COMPILER_TLS` during the library build process. The macro also affects other platforms, which may be useful if your compiler does not support TLS.
|
||||
* Added a few public accessors and convenience constructors to severity and channel loggers.
|
||||
* Added ability to rotate log files at the specified time points. The `rotation_interval` keyword is no longer available. The same functionality is achieved with the new `time_based_rotation` keyword and the `rotation_at_time_interval` predicate. See [link log.detailed.sink_backends.text_file here] for more details.
|
||||
* Improved support for MinGW and Cygwin.
|
||||
* A number of bugs fixed. Added workarounds to compile on GCC 4.2.
|
||||
* Lots of corrections in the documentation.
|
||||
|
||||
[heading Release Candidate 4, 08 Jan 2010]
|
||||
|
||||
* Substantial documentation improvement. The tutorial section has been reorganized.
|
||||
* Library headers have been reorganized. Some other Boost libraries that were previously included by headers have been made optional. Such dependencies have been extracted into separate headers in the `support` directory. Top level library headers now mostly include nested headers.
|
||||
* Keywords have moved into a dedicated `keywords` namespace. There are no longer nested `keywords` namespaces in `sinks`, `attributes`, etc. All keywords have been extracted into separate headers in the `keywords` directory.
|
||||
* Removed rotating file stream. As a replacement, a [link log.detailed.sink_backends.text_file new file sink] has been added, which allows to achieve the same results ans adds a few more features.
|
||||
* Added a new [link log.detailed.sink_backends.text_multifile multifile] sink backend.
|
||||
* Added a new ordering asynchronous sink frontend.
|
||||
* The [link log.detailed.sink_backends.syslog syslog] sink backend is now supported on Windows, too. The sink no longer requires native support for POSIX API for syslog, but is able to send syslog packets to a remote server over UDP.
|
||||
* Loggers implementation has been improved. Feature composition mechanism has been cleaned up.
|
||||
* Added support for scoped logging. There is now a distinct [link log.detailed.core.record log record entity], which is returned by the core as a result of filtering. It is possible to fill in the record message in any way the user wants, not necessarilly with a streaming expression. The record object is now processed by sinks and formatters.
|
||||
* Added support for exception control. User can register exception handlers at one of the three layers: for a particular sink, at the core layer, and for a particular logger (given that it has the appropriate feature). Sinks and core will not suppress exceptions by default. Filter and formatters will throw if the requested attribute value is not found.
|
||||
* Added a few new formatters, called character decorators. These can be useful to post-process the formatted output before passing it on to the sink.
|
||||
* Added attributes for thread and process identifiers. These identifiers are automatically added after the call to `add_common_attributes`.
|
||||
* Helper initialization functions, such as `init_log_to_file` now accept more customization options as named arguments.
|
||||
* A new [link log.detailed.utilities.setup.settings initialization interface] has been exposed. One can fill a settings container and use it to initialize the library.
|
||||
* The library setup support code has beed extracted into a separate binary. Further on, this binary will be made optional to build.
|
||||
* Added a new mode of logging, called trivial logging. In this mode the library requires no initialization at all, however it does not offer many ways of customization.
|
||||
* A number of bugs fixed.
|
||||
* A few optimizations added to improve multithreaded applications performance.
|
||||
* Removed some bug workarounds for older Boost releases. The library now requires Boost 1.39 or newer.
|
||||
|
||||
[heading Release Candidate 3, 08 Feb 2009]
|
||||
|
||||
* Substantial documentation improvement.
|
||||
* Added several Windows-specific sinks: Event Log (simplified and advanced), Windows debugger and experimental Event Trace for Windows Vista and later.
|
||||
* Loggers now consist of a number of independent features that can be composed the way the user needs. User-defined features can be developed and injected into the mix.
|
||||
* Attribute value extractors improved. With the new extract function attribute values can be extracted from the attribute values view by employing lambda functors.
|
||||
* Some files and classes were moved or renamed to improve code clarity and shorten names.
|
||||
* A number of bugs fixed.
|
||||
* Added tests.
|
||||
|
||||
[heading Release Candidate 2]
|
||||
|
||||
Noone really remembers these dark ages...
|
||||
|
||||
[endsect]
|
177
doc/core.qbk
Normal file
177
doc/core.qbk
Normal file
@ -0,0 +1,177 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:core Core facilities]
|
||||
|
||||
[section:record Logging records]
|
||||
|
||||
#include <``[boost_log_core_record_hpp]``>
|
||||
|
||||
All the information that the logging library processes is packed into a single object of type [class_log_record]. All attached data, including the message text, is represented as named attribute values that can be fetched and processed by filters, formatters and sinks. Particular attribute values can be accessed in different ways, here are a few quick examples:
|
||||
|
||||
* Through [link log.detailed.attributes.related_components.value_processing value visitation and extraction].
|
||||
|
||||
[example_core_record_visitation_extraction]
|
||||
|
||||
* By searching the [link log.detailed.attributes.related_components.attribute_value_set set of attribute values] accessible with the `attribute_values` method of the record.
|
||||
|
||||
[example_core_record_attr_value_lookup]
|
||||
|
||||
* By applying the subscript operator with the attribute keyword. This is actually a convenience wrapper around the value extraction API.
|
||||
|
||||
[example_core_record_subscript]
|
||||
|
||||
Log records cannot be copied, only moved. A record can be default-constructed in which case it is in an empty state; such records are mostly unusable and should not be passed to the library for processing. Non-empty log records can only be created by the [link log.detailed.core.core logging core] as a result of successful filtering. The non-empty record contains attribute values acquired from attributes. More attribute values can be added to the non-empty record after filtering. The added values will not affect filtering results but can still be used by formatters and sinks.
|
||||
|
||||
In multithreaded environments, after being constructed a non-empty log record is considered to be tied to the current thread as it may refer to some thread-specific resources. For example, the record may contain an attribute value which refers to the named scope list which is stored on the stack. For this reason log records must not be passed between different threads.
|
||||
|
||||
[heading Record views]
|
||||
|
||||
#include <``[boost_log_core_record_view_hpp]``>
|
||||
|
||||
While records are used for filling the information, the library uses another type to actually process it. Record views provide a similar interface to records with a few notable distinctions:
|
||||
|
||||
* Record views are immutable. This prevents formatters and sinks from modifying the record while it is being processed.
|
||||
* Record views are copyable. Since its contents are constant, the copy operation is shallow and therefore cheap.
|
||||
|
||||
The library will automatically create a record view from the record by calling the `lock` method. The call will also make sure the resulting view is not attached to the current thread if a sink is asynchronous. The `lock` call is a one time operation; the record is left in the empty state afterwards. All APIs for interacting with attribute values described for log records are also applicable to record views and can be used in custom formatters and sinks.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:core Logging core]
|
||||
|
||||
#include <``[boost_log_core_core_hpp]``>
|
||||
|
||||
The logging core is a central hub that provides the following facilities:
|
||||
|
||||
* Maintains global and thread-specific attribute sets.
|
||||
* Performs global filtering of log records.
|
||||
* Dispatches log records between sinks by applying sink-specific filters.
|
||||
* Provides a global hook for exception handlers.
|
||||
* Provides an entry point for log sources to put log records to.
|
||||
* Provides the `flush` method that can be used to enforce the synchronized state for all log sinks.
|
||||
|
||||
The logging core is an application-wide singleton, thus every logging source has access to it. The core instance is accessible with the static method `get`.
|
||||
|
||||
void foo()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
[section:attribute_sets Attribute sets]
|
||||
|
||||
In order to add or remove global or thread-specific attributes to the core there are corresponding methods: `add_global_attribute`, `remove_global_attribute`, `add_thread_attribute` and `remove_thread_attribute`. Attribute sets provide interface similar to `std::map`, so the `add_*` methods accept an attribute name string (key) and a pointer to the attribute (mapped value) and return a pair of iterator and boolean value, like `std::map< ... >::insert` does. The `remove_*` methods accept an iterator to a previously added attribute.
|
||||
|
||||
void foo()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Add a global attribute
|
||||
std::pair< logging::attribute_set::iterator, bool > res =
|
||||
core->add_global_attribute("LineID", attrs::counter< unsigned int >());
|
||||
|
||||
// ...
|
||||
|
||||
// Remove the added attribute
|
||||
core->remove_global_attribute(res.first);
|
||||
}
|
||||
|
||||
[tip It must be said that all methods of logging core are thread-safe in multithreaded environments. However, that may not be true for other components, such as iterators or attribute sets.]
|
||||
|
||||
It is possible to acquire a copy of the whole attribute set (global or thread-specific) or install it into the core. Methods `get_global_attributes`, `set_global_attributes`, `get_thread_attributes` and `set_thread_attributes` serve this purpose.
|
||||
|
||||
[warning After installing a whole attribute set into the core, all iterators that were previously returned by the corresponding `add_*` methods are invalidated. In particular, it affects [link log.detailed.attributes.related_components.scoped_attributes scoped attributes], so the user must be careful when to switch attribute sets.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:filtering Global filtering]
|
||||
|
||||
Global filtering is handled by the filter function object, which can be provided with the `set_filter` method. More on creating filters appears in [link log.detailed.expressions.predicates this section]. Here it will suffice to say that the filter accepts a set of attribute values and returns a boolean value that tells whether a log record with these attribute values passed filtering or not. The global filter is applied to every log record made throughout the application, so it can be used to wipe out excessive log records quickly.
|
||||
|
||||
The global filter can be removed by the `reset_filter` method. When there is no filter set in the core it is assumed that no records are filtered away. This is the default after initial construction of the logging core.
|
||||
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
warning,
|
||||
error,
|
||||
critical
|
||||
};
|
||||
|
||||
void foo()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Set a global filter so that only error messages are logged
|
||||
core->set_filter(expr::attr< severity_level >("Severity") >= error);
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
The core also provides another way to disable logging. By calling the `set_logging_enabled` with a boolean argument one may completely disable or reenable logging, including applying filtering. Disabling logging with this method may be more benefical in terms of application performance than setting a global filter that always fails.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:sinks Sink management]
|
||||
|
||||
After global filtering is applied, log sinks step into action. In order to add and remove sinks the core provides `add_sink` and `remove_sink` methods. Both these methods accept a pointer to the sink. The `add_sink` will add the sink to the core if it's not added already. The `remove_sink` method will seek for the provided sink in an internal list of previously added sinks and remove the sink if it finds it. The order in which the core processes sinks internally is unspecified.
|
||||
|
||||
void foo()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Set a sink that will write log records to the console
|
||||
boost::shared_ptr< sinks::text_ostream_backend > backend =
|
||||
boost::make_shared< sinks::text_ostream_backend >();
|
||||
backend->add_stream(
|
||||
boost::shared_ptr< std::ostream >(&std::clog, logging::empty_deleter()));
|
||||
|
||||
typedef sinks::unlocked_sink< sinks::text_ostream_backend > sink_t;
|
||||
boost::shared_ptr< sink_t > sink = boost::make_shared< sink_t >(backend);
|
||||
core->add_sink(sink);
|
||||
|
||||
// ...
|
||||
|
||||
// Remove the sink
|
||||
core->remove_sink(sink);
|
||||
}
|
||||
|
||||
You can read more on the design of sinks in the following sections: [link log.detailed.sink_frontends Sink Frontends] and [link log.detailed.sink_backends Sink Backends].
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:exception_handling Exception handling]
|
||||
|
||||
The core provides a way to set up centralized exception handling. If an exception takes place during filtering or processing in one of the added sinks, the core will invoke an exception handler if one was installed with the `set_exception_handler` method. An exception handler is a nullary function object that is invoked from within a `catch` clause. The library provides [link log.detailed.utilities.exception_handlers tools] to simplify exception handlers construction.
|
||||
|
||||
[tip The exception handler in the logging core is global and thus is intended to perform some common actions on errors. Logging sinks and sources also provide exception handling facilities (see [link log.detailed.sink_frontends.basic_services.exception_handling here] and [link log.detailed.sources.exception_handling here]), which can be used to do a finer grained error processing.]
|
||||
|
||||
[example_utility_exception_handler]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:record_feeding Feeding log records]
|
||||
|
||||
One of the most important functions of the logging core is providing an entry point for all logging sources to feed log records into. This is done with the `open_record` and `push_record` methods.
|
||||
|
||||
The first method is used to initiate the record logging process. It accepts the source-specific set of attributes. The method constructs a common set of attribute values of the three sets of attributes (global, thread-specific and source-specific) and applies filtering. If the filtering succeeded, i.e. at least one sink accepts a record with these attribute values, the method returns a non-empty [link log.detailed.core.record record object], which can be used to fill in the log record message. If the filtering failed, an empty record object is returned.
|
||||
|
||||
When the log source is ready to complete the logging procedure, it has to call the `push_record` method with the record returned by the `open_record` method. Note that one should not call `push_record` with an empty record. The record should be passed as rvalue reference. During the call the record view will be constructed from the record. The view will then be passed on to the sinks that accepted it during filtering. This may involve record formatting and further processing, like storing it into a file or sending it over the network. After that the record object can be destroyed.
|
||||
|
||||
[example_core_core_manual_logging]
|
||||
|
||||
All this logic is usually hidden in the loggers and macros provided by the library. However, this may be useful for those developing new log sources.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
54
doc/design.qbk
Normal file
54
doc/design.qbk
Normal file
@ -0,0 +1,54 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:design Design overview]
|
||||
|
||||
Boost.Log was designed to be very modular and extensible. It supports both narrow-character and wide-character logging. Both narrow and wide-character loggers provide similar capabilities, so through most of the documentation only the narrow-character interface will be described.
|
||||
|
||||
The library consists of three main layers: the layer of log data collection, the layer of processing the collected data and the central hub that interconnects the former two layers. The design is presented on the figure below.
|
||||
|
||||
[$images/log/Design.png]
|
||||
|
||||
The arrows show the direction of logging information flow - from parts of your application, at the left, to the final storage, (if any) at the right. The storage is optional because the result of log processing may include some actions without actually storing the information anywhere. For example, if your application is in a critical state, it can emit a special log record that will be processed so that the user sees an error message as a tool-tip notification over the application icon in the system tray and hears an alarming sound. This is a very important library feature: it is orthogonal to collecting, processing logging data and, in fact, what data logging records consist of. This allows for use of the library not only for classic logging, but to indicate some important events to the application user and accumulate statistical data.
|
||||
|
||||
[heading Logging sources]
|
||||
|
||||
Getting back to the figure, in the left side your application emits log records with help of loggers - special objects that provide streams to format messages that will eventually be put to log. The library provides a number of different logger types and you can craft many more yourself, extending the existing ones. Loggers are designed as a mixture of distinct features that can be combined with each other in any combination. You can simply develop your own feature and add it to the soup. You will be able to use the constructed logger just like the others - embed it into your application classes or create and use a global instance of the logger. Either approach provides its benefits. Embedding a logger into some class provides a way to differentiate logs from different instances of the class. On the other hand, in functional-style programming it is usually more convenient to have a single global logger somewhere and have a simple access to it.
|
||||
|
||||
Generally speaking, the library does not require the use of loggers to write logs. The more generic term "log source" designates the entity that initiates logging by constructing a log record. Other log sources might include captured console output of a child application or data received from network. However, loggers are the most common kind of log sources.
|
||||
|
||||
[heading Attributes and attribute values]
|
||||
|
||||
In order to initiate logging a log source must pass all data, associated with the log record, to the logging core. This data or, more precisely, the logic of the data acquisition is represented with a set of named attributes. Each attribute is, basically, a function, whose result is called "attribute value" and is actually processed on further stages. An example of an attribute is a function that returns the current time. Its result - the particular time point - is the attribute value.
|
||||
|
||||
There are three kinds of attribute sets:
|
||||
|
||||
* global
|
||||
* thread-specific
|
||||
* source-specific
|
||||
|
||||
You can see in the figure that the former two sets are maintained by the logging core and thus need not be passed by the log source in order to initiate logging. Attributes that participate in the global attribute set are attached to any log record ever made. Obviously, thread-specific attributes are attached only to the records made from the thread in which they were registered in the set. The source-specific attribute set is maintained by the source that initiates logging, these attributes are attached only to the records being made through that particular source.
|
||||
|
||||
When a source initiates logging, attribute values are acquired from attributes of all three attribute sets. These attribute values then form a single set of named attribute values, which is processed further. You can add more attribute values to the set; these values will only be attached to the particular log record and will not be associated with the logging source or logging core. As you may notice, it is possible for a same-named attribute to appear in several attribute sets. Such conflicts are solved on priority basis: global attributes have the least priority, source-specific attributes have the highest; the lower priority attributes are discarded from consideration in case of conflicts.
|
||||
|
||||
[heading Logging core and filtering]
|
||||
|
||||
When the set of attribute values is composed, the logging core decides if this log record is going to be processed in sinks. This is called filtering. There are two layers of filtering available: the global filtering is applied first within the logging core itself and allows quickly wiping away unneeded log records; the sink-specific filtering is applied second, for each sink separately. The sink-specific filtering allows directing log records to particular sinks. Note that at this point it is not significant which logging source emitted the record, the filtering relies solely on the set of attribute values attached to the record.
|
||||
|
||||
It must be mentioned that for a given log record filtering is performed only once. Obviously, only those attribute values attached to the record before filtering starts can participate in filtering. Some attribute values, like log record message, are typically attached to the record after the filtering is done; such values cannot be used in filters, they can only be used by formatters and sinks themselves.
|
||||
|
||||
[heading Sinks and formatting]
|
||||
|
||||
If a log record passes filtering for at least one sink the record is considered to be consumable. If the sink supports formatted output, this is the point when log message formatting takes place. The formatted message along with the composed set of attribute values is passed to the sink that accepted the record. Note that formatting is performed on the per-sink basis so that each sink can output log records in its own specific format.
|
||||
|
||||
As you may have noticed on the figure above, sinks consist of two parts: the frontend and the backend. This division is made in order to extract the common functionality of sinks, such as filtering, formatting and thread synchronization, into separate entities (frontends). Sink frontends are provided by the library, most likely users won't have to re-implement them. Backends, on the other hand, are one of the most likely places for extending the library. It is sink backends that do the actual processing of log records. There can be a sink that stores log records into a file; there can be a sink that sends log records over the network to the remote log processing node; there can be the aforementioned sink that puts record messages into tool-tip notifications - you name it. The most commonly used sink backends are already provided by the library.
|
||||
|
||||
Along with the primary facilities described above, the library provides a wide variety of auxiliary tools, such as attributes, support for formatters and filters, represented as lambda expressions, and even basic helpers for the library initialization. You will find their description in the [link log.detailed Detailed features description] section. However, for new users it is recommended to start discovering the library from the [link log.tutorial Tutorial] section.
|
||||
|
||||
[endsect]
|
568
doc/expressions.qbk
Normal file
568
doc/expressions.qbk
Normal file
@ -0,0 +1,568 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:expressions Lambda expressions]
|
||||
|
||||
As it was pointed out in [link log.tutorial tutorial], filters and formatters can be specified as Lambda expressions with placeholders for attribute values. This section will describe the placeholders that can be used to build more complex Lambda expressions.
|
||||
|
||||
There is also a way to specify the filter in the form of a string template. This can be useful for initialization from the application settings. This part of the library is described [link log.detailed.utilities.setup.filter_formatter here].
|
||||
|
||||
[section:attr Generic attribute placeholder]
|
||||
|
||||
#include <``[boost_log_expressions_attr_fwd_hpp]``>
|
||||
#include <``[boost_log_expressions_attr_hpp]``>
|
||||
|
||||
The [funcref boost::log::expressions::attr attr] placeholder represents an attribute value in template expressions. Given the record view or a set of attribute values, the placeholder will attempt to extract the specified attribute value from the argument upon invokation. This can be roughly described with the following pseudo-code:
|
||||
|
||||
logging::value_ref< T, TagT > val = expr::attr< T, TagT >(name)(rec);
|
||||
|
||||
where `val` is the [link log.detailed.utilities.value_ref reference] to the extracted value, `name` and `T` are the attribute value [link log.detailed.attributes.related_components.attribute_name name] and type, `TagT` is an optional tag (we'll return to it in a moment) and `rec` is the log [link log.detailed.core.record record view] or [link log.detailed.attributes.related_components.attribute_value_set attribute value set]. `T` can be a __boost_mpl__ type sequence with possible expected types of the value; the extraction will succeed if the type of the value matches one of the types in the sequence.
|
||||
|
||||
The `attr` placeholder can be used in __boost_phoenix__ expressions, including the `bind` expression.
|
||||
|
||||
[example_tutorial_filtering_bind]
|
||||
|
||||
The placeholder can be used both in filters and formatters:
|
||||
|
||||
sink->set_filter
|
||||
(
|
||||
expr::attr< int >("Severity") >= 5 &&
|
||||
expr::attr< std::string >("Channel") == "net"
|
||||
);
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< expr::attr< int >("Severity")
|
||||
<< " [" << expr::attr< std::string >("Channel") << "] "
|
||||
<< expr::smessage
|
||||
);
|
||||
|
||||
The call to `set_filter` registers a composite filter that consists of two elementary subfilters: the first one checks the severity level, and the second checks the channel name. The call to `set_formatter` installs a formatter that composes a string containing the severity level and the channel name along with the message text.
|
||||
|
||||
[heading Customizing fallback policy]
|
||||
|
||||
By default, when the requested attribute value is not found in the record, `attr` will return an empty reference. In case of filters, this will result in `false` in any ordering expressions, and in case of formatters the output from the placeholder will be empty. This behavior can be changed:
|
||||
|
||||
* To throw an exception ([class_log_missing_value] or [class_log_invalid_type], depending on the reason of the failure). Add the `or_throw` modifier:
|
||||
|
||||
sink->set_filter
|
||||
(
|
||||
expr::attr< int >("Severity").or_throw() >= 5 &&
|
||||
expr::attr< std::string >("Channel").or_throw() == "net"
|
||||
);
|
||||
|
||||
* To use a default value instead. Add the `or_default` modifier with the desired default value:
|
||||
|
||||
sink->set_filter
|
||||
(
|
||||
expr::attr< int >("Severity").or_default(0) >= 5 &&
|
||||
expr::attr< std::string >("Channel").or_default(std::string("general")) == "net"
|
||||
);
|
||||
|
||||
[tip You can also use the [link log.detailed.expressions.predicates.has_attr `has_attr`] predicate to implement filters and formatters conditional on the attribute value presence.]
|
||||
|
||||
The default behavior is also accessible through the `or_none` modifier. The modified placeholders can be used in filters and formatters just the same way as the unmodified ones.
|
||||
|
||||
In `bind` expressions, the bound function object will still receive the [link log.detailed.utilities.value_ref `value_ref`]-wrapped values in place of the modified `attr` placeholder. Even though both `or_throw` and `or_default` modifiers guarantee that the bound function will receive a filled reference, [link log.detailed.utilities.value_ref `value_ref`] is still needed if the value type is specified as a type sequence. Also, the reference wrapper may contain a tag type which may be useful for formatting customization.
|
||||
|
||||
[heading Attribute tags and custom formatting operators]
|
||||
|
||||
The `TagT` type in the [link log.detailed.expressions.attr abstract description] of `attr` above is optional and by default is `void`. This is an attribute tag which can be used to customize the output formatters produce for different attributes. This tag is forwarded to the [link log.detailed.utilities.manipulators.to_log `to_log`] manipulator when the extracted attribute value is put to a stream (this behavior is warranted by [link log.detailed.utilities.value_ref `value_ref`] implementation). Here's a quick example:
|
||||
|
||||
[example_expressions_attr_formatter_stream_tag]
|
||||
|
||||
[@boost:/libs/log/example/doc/expressions_attr_fmt_tag.cpp See the complete code].
|
||||
|
||||
Here we specify a different formatting operator for the severity level wrapped in the [link log.detailed.utilities.manipulators.to_log `to_log_manip`] manipulator marked with the tag `severity_tag`. This operator will be called when log records are formatted while the regular `operator<<` will be used in other contexts.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:attr_keywords Defining attribute keywords]
|
||||
|
||||
#include <``[boost_log_expressions_keyword_fwd_hpp]``>
|
||||
#include <``[boost_log_expressions_keyword_hpp]``>
|
||||
|
||||
Attribute keywords can be used as replacements for the [link log.detailed.expressions.attr `attr`] placeholders in filters and formatters while providing a more concise and less error prone syntax. An attribute keyword can be declared with the [macroref BOOST_LOG_ATTRIBUTE_KEYWORD] macro:
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(keyword, "Keyword", type)
|
||||
|
||||
Here the macro declares a keyword `keyword` for an attribute named "Keyword" with the value type of `type`. Additionally, the macro defines an attribute tag type `keyword` within the `tag` namespace. We can rewrite the previous example in the following way:
|
||||
|
||||
[example_expressions_keyword_formatter_stream_tag]
|
||||
|
||||
Attribute keywords behave the same way as the [link log.detailed.expressions.attr `attr`] placeholders and can be used both in filters and formatters. The `or_throw` and `or_default` modifiers are also supported.
|
||||
|
||||
Keywords can also be used in attribute value lookup expressions in log records and attribute value sets:
|
||||
|
||||
[example_expressions_keyword_lookup]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:record Record placeholder]
|
||||
|
||||
#include <``[boost_log_expressions_record_hpp]``>
|
||||
|
||||
The `record` placeholder can be used in `bind` expressions to pass the whole log [link log.detailed.core.record record view] to the bound function object.
|
||||
|
||||
void my_formatter(logging::formatting_ostream& strm, logging::record_view const& rec)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
namespace phoenix = boost::phoenix;
|
||||
sink->set_formatter(phoenix::bind(&my_formatter, expr::stream, expr::record));
|
||||
|
||||
[note In case of filters, the placeholder will correspond to the [link log.detailed.attributes.related_components.attribute_value_set set of attribute values] rather than the log record itself. This is because the record is not constructed yet at the point of filtering, and filters only operate on the set of attribute values.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:message Message text placeholders]
|
||||
|
||||
#include <``[boost_log_expressions_message_hpp]``>
|
||||
|
||||
Log records typically contain a special attribute "Message" with the value of one of the string types (more specifically, an `std::basic_string` specialization). This attribute contains the text of the log message that is constructed at the point of the record creation. This attribute is only constructed after filtering, so filters cannot use it. There are several keywords to access this attribute value:
|
||||
|
||||
* `smessage` - the attribute value is expected to be an `std::string`
|
||||
* `wmessage` - the attribute value is expected to be an `std::wstring`
|
||||
* `message` - the attribute value is expected to be an `std::string` or `std::wstring`
|
||||
|
||||
The `message` keyword has to dispatch between different string types, so it is slightly less efficient than the other two keywords. If the application is able to guarantee the fixed character type of log messages, it is advised to use the corresponding keyword for better performance.
|
||||
|
||||
// Sets up a formatter that will ignore all attributes and only print log record text
|
||||
sink->set_formatter(expr::stream << expr::message);
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:predicates Predicate expressions]
|
||||
|
||||
This section describes several expressions that can be used as predicates in the filtering expressions.
|
||||
|
||||
[section:has_attr Attribute presence filter]
|
||||
|
||||
#include <``[boost_log_expressions_predicates_has_attr_hpp]``>
|
||||
|
||||
The filter [funcref boost::log::expressions::has_attr `has_attr`] checks if an attribute value with the specified name and, optionally, type is attached to a log record. If no type specified to the filter, the filter returns `true` if any value with the specified name is found. If an MPL-compatible type sequence in specified as a value type, the filter returns `true` if a value with the specified name and one of the specified types is found.
|
||||
|
||||
This filter is usually used in conjunction with [link log.detailed.expressions.formatters.conditional conditional formatters], but it also can be used as a quick filter based on the log record structure. For example, one can use this filter to extract statistic records and route them to a specific sink.
|
||||
|
||||
[example_expressions_has_attr_stat_accumulator]
|
||||
|
||||
[@boost:/libs/log/example/doc/expressions_has_attr_stat_accum.cpp See the complete code].
|
||||
|
||||
In this example, log records emitted with the `PUT_STAT` macto will be directed to the `my_stat_accumulator` sink backend, which will accumulate the changes passed in the "Change" attribute values. All other records (even those made through the same logger) will be passed to the filte sink. This is achieved with the mutually exclusive filters set for the two sinks.
|
||||
|
||||
Please note that in the example above we extended the library in two ways: we defined a new sink backend `my_stat_accumulator` and a new macro `PUT_STAT`. Also note that `has_attr` can accept attribute keywords to identify the attribute to check.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:is_in_range Range checking filter]
|
||||
|
||||
#include <``[boost_log_expressions_predicates_is_in_range_hpp]``>
|
||||
|
||||
The [funcref boost::log::expressions::is_in_range `is_in_range`] predicate checks that the attribute value fits in the half-open range (i.e. it returns `true` if the attribute value `x` satisfies the following condition: `left <= x < right`). For example:
|
||||
|
||||
sink->set_filter
|
||||
(
|
||||
// drops all records that have level below 3 or greater than 4
|
||||
expr::is_in_range(expr::attr< int >("Severity"), 3, 5)
|
||||
);
|
||||
|
||||
The attribute can also be identified by an attribute keyword or name and type:
|
||||
|
||||
sink->set_filter
|
||||
(
|
||||
expr::is_in_range(severity, 3, 5)
|
||||
);
|
||||
|
||||
sink->set_filter
|
||||
(
|
||||
expr::is_in_range< int >("Severity", 3, 5)
|
||||
);
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:simple_string_matching Simple string matching filters]
|
||||
|
||||
#include <``[boost_log_expressions_predicates_begins_with_hpp]``>
|
||||
#include <``[boost_log_expressions_predicates_ends_with_hpp]``>
|
||||
#include <``[boost_log_expressions_predicates_contains_hpp]``>
|
||||
|
||||
Predicates [funcref boost::log::expressions::begins_with `begins_with`], [funcref boost::log::expressions::ends_with `ends_with`] and [funcref boost::log::expressions::contains `contains`] provide an easy way of matching string attribute values. As follows from their names, the functions construct filters that return `true` if an attribute value begins with, ends with or contains the specified substring, respectively. The string comparison is case sensitive.
|
||||
|
||||
sink->set_filter
|
||||
(
|
||||
// selects only records that are related to Russian web domains
|
||||
expr::ends_with(expr::attr< std::string >("Domain"), ".ru")
|
||||
);
|
||||
|
||||
The attribute can also be identified by an attribute keyword or name and type.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:advanced_string_matching Advanced string matching filter]
|
||||
|
||||
#include <``[boost_log_expressions_predicates_matches_hpp]``>
|
||||
|
||||
// Supporting headers
|
||||
#include <``[boost_log_support_regex_hpp]``>
|
||||
#include <``[boost_log_support_xpressive_hpp]``>
|
||||
#include <``[boost_log_support_spirit_qi_hpp]``>
|
||||
#include <``[boost_log_support_spirit_classic_hpp]``>
|
||||
|
||||
The [funcref boost::log::expressions::matches `matches`] function creates a filter that apples a regular expression or a parser to a string attribute value. The regular expression can be provided by __boost_regex__ or __boost_xpressive__. Parsers from __boost_spirit__ and __boost_spirit2__ are also supported. The filter returns `true` if the regular expression matches or the parser successfully parses the attribute value.
|
||||
|
||||
[note In order to use this predicate, a corresponding supporting header should also be included.]
|
||||
|
||||
sink->set_filter
|
||||
(
|
||||
expr::matches(expr::attr< std::string >("Domain"), boost::regex("www\\..*\\.ru"))
|
||||
);
|
||||
|
||||
The attribute can also be identified by an attribute keyword or name and type.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:channel_severity_filter Severity threshold per channel filter]
|
||||
|
||||
#include <``[boost_log_expressions_predicates_channel_severity_filter_hpp]``>
|
||||
|
||||
This filter is aimed for a specific but commonly encountered use case. The [funcref boost::log::expressions::channel_severity_filter `channel_severity_filter`] function creates a predicate that will check log record severity levels against a threshold. The predicate allows setting different thresholds for different channels. The mapping between channel names and severity thresholds can be filled in `std::map` style by using the subscript operator or by calling `add` method on the filter itself (the [class_expressions_channel_severity_filter_actor] instance). Let's see an example:
|
||||
|
||||
[example_expressions_channel_severity_filter]
|
||||
|
||||
[@boost:/libs/log/example/doc/expressions_channel_severity_filter.cpp See the complete code].
|
||||
|
||||
The filter for the console sink is composed from the [class_expressions_channel_severity_filter_actor] filter and a general severity level check. This general check will be used when log records do not have a channel attribute or the channel name is not one of those specified in [class_expressions_channel_severity_filter_actor] initialization. It should be noted that it is possible to set the default result of the threshold filter that will be used in this case; the default result can be set by the `set_default` method. The [class_expressions_channel_severity_filter_actor] filter is set up to limit record severity levels for channels "general", "network" and "gui" - all records in these channels with levels below the specified thresholds will not pass the filter and will be ignored.
|
||||
|
||||
The threshold filter is implemented as an equivalent to `std::map` over the channels, which means that the channel value type must support partial ordering. Obviously, the severity level type must also support ordering to be able to be compared against thresholds. By default the predicate will use `std::less` equivalent for channel name ordering and `std::greater_equal` equivalent to compare severity levels. It is possible to customize the ordering predicates. Consult the reference of the [class_expressions_channel_severity_filter_actor] class and [funcref boost::log::expressions::channel_severity_filter `channel_severity_filter`] generator to see the relevant template parameters.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:is_debugger_present Debugger presence filter]
|
||||
|
||||
#include <``[boost_log_expressions_predicates_is_debugger_present_hpp]``>
|
||||
|
||||
This filter is implemented for Windows only. The `is_debugger_present` filter returns `true` if the application is run under a debugger and `false` otherwise. It does not use any attribute values from the log record. This predicate is typically used with the [link log.detailed.sink_backends.debugger debugger output] sink.
|
||||
|
||||
[example_sinks_debugger]
|
||||
|
||||
[@boost:/libs/log/example/doc/sinks_debugger.cpp See the complete code].
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:formatters Formatting expressions]
|
||||
|
||||
As was noted in the [link log.tutorial.formatters tutorial], the library provides several ways of expressing formatters, most notable being with a stream-style syntax and __boost_format__-style expression. Which of the two formats is chosen is determined by the appropriate anchor expression. To use stream-style syntax one should begin the formatter definition with the `stream` keyword, like that:
|
||||
|
||||
#include <``[boost_log_expressions_formatters_stream_hpp]``>
|
||||
|
||||
sink->set_formatter(expr::stream << expr1 << expr2 << ... << exprN);
|
||||
|
||||
Here expressions `expr1` through `exprN` may be either manipulators, described in this section, or other expressions resulting in an object that supports putting into an STL-stream.
|
||||
|
||||
To use __boost_format__-style syntax one should use `format` construct:
|
||||
|
||||
#include <``[boost_log_expressions_formatters_format_hpp]``>
|
||||
|
||||
sink->set_formatter(expr::format("format string") % expr1 % expr2 % ... % exprN);
|
||||
|
||||
The format string passed to the `format` keyword should contain positional placeholders for the appropriate expressions. In the case of wide-character logging the format string should be wide. Expressions `expr1` through `exprN` have the same meaning as in stream-like variant. It should be noted though that using stream-like syntax usually results in a faster formatter than the one constructed with the `format` keyword.
|
||||
|
||||
Another useful way of expressing formatters is by using string templates. This part of the library is described in [link log.detailed.utilities.setup.filter_formatter this] section and is mostly intended to support initialization from the application settings.
|
||||
|
||||
[section:date_time Date and time formatter]
|
||||
|
||||
#include <``[boost_log_expressions_formatters_date_time_hpp]``>
|
||||
|
||||
// Supporting headers
|
||||
#include <``[boost_log_support_date_time_hpp]``>
|
||||
|
||||
The library provides the [funcref boost::log::expressions::format_date_time `format_date_time`] formatter dedicated to date and time-related attribute value types. The function accepts the attribute value name and the format string compatible with __boost_date_time__.
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
|
||||
);
|
||||
|
||||
The attribute value can alternatively be identified with the [link log.detailed.expressions.attr `attr`] placeholder or the [link log.detailed.expressions.attr_keywords attribute keyword].
|
||||
|
||||
The following placeholders are supported in the format string:
|
||||
|
||||
[table Date format placeholders
|
||||
[[Placeholder][Meaning][Example]]
|
||||
[[%a] [Abbreviated weekday name]["Mon" => Monday]]
|
||||
[[%A] [Long weekday name]["Monday"]]
|
||||
[[%b] [Abbreviated month name]["Feb" => February]]
|
||||
[[%B] [Long month name]["February"]]
|
||||
[[%d] [Numeric day of month with leading zero]["01"]]
|
||||
[[%e] [Numeric day of month with leading space][" 1"]]
|
||||
[[%m] [Numeric month, 01-12]["01"]]
|
||||
[[%w] [Numeric day of week, 1-7]["1"]]
|
||||
[[%y] [Short year]["12" => 2012]]
|
||||
[[%Y] [Long year]["2012"]]
|
||||
]
|
||||
|
||||
[table Time format placeholders
|
||||
[[Placeholder][Meaning][Example]]
|
||||
[[%f] [Fractional seconds with leading zeros]["000231"]]
|
||||
[[%H, %O] [Hours in 24 hour clock or hours in time duration types with leading zero if less than 10]["07"]]
|
||||
[[%I] [Hours in 12 hour clock with leading zero if less than 10]["07"]]
|
||||
[[%k] [Hours in 24 hour clock or hours in time duration types with leading space if less than 10][" 7"]]
|
||||
[[%l] [Hours in 12 hour clock with leading space if less than 10][" 7"]]
|
||||
[[%M] [Minutes]["32"]]
|
||||
[[%p] [AM/PM mark, uppercase]["AM"]]
|
||||
[[%P] [AM/PM mark, lowercase]["am"]]
|
||||
[[%q] [ISO time zone]["-0700" => Mountain Standard Time]]
|
||||
[[%Q] [Extended ISO time zone]["-05:00" => Eastern Standard Time]]
|
||||
[[%S] [Seconds]["26"]]
|
||||
]
|
||||
|
||||
[table Miscellaneous placeholders
|
||||
[[Placeholder][Meaning][Example]]
|
||||
[[%-] [Negative sign in case of time duration, if the duration is less than zero]["-"]]
|
||||
[[%+] [Sign of time duration, even if positive]["+"]]
|
||||
[[%%] [An escaped percent sign]["%"]]
|
||||
[[%T] [Extended ISO time, equivalent to "%H:%M:%S"]["07:32:26"]]
|
||||
]
|
||||
|
||||
Note that in order to use this formatter you will also have to include a supporting header. When [boost_log_support_date_time_hpp] is included, the formatter supports the following types of __boost_date_time__:
|
||||
|
||||
* Date and time types: `boost::posix_time::ptime` and `boost::local_time::local_date_time`.
|
||||
* Gregorian date type: `boost::gregorian::date`.
|
||||
* Time duration types: `boost::posix_time::time_duration` as well as all the specialized time units such as `boost::posix_time::seconds`, including subsecond units.
|
||||
* Date duration types: `boost::gregorian::date_duration`.
|
||||
|
||||
[tip __boost_date_time__ already provides formatting functionality implemented as a number of locale facets. This functionality can be used instead of this formatter, although the formatter is expected to provide better performance.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:named_scope Named scope formatter]
|
||||
|
||||
#include <``[boost_log_expressions_formatters_named_scope_hpp]``>
|
||||
|
||||
The formatter [funcref boost::log::expressions::format_named_scope `format_named_scope`] is intended to add support for flexible formatting of the [link log.detailed.attributes.named_scope named scope] attribute values. The basic usage is quite straightforward and its result is similar to what [link log.detailed.expressions.attr `attr`] provides:
|
||||
|
||||
// Puts the scope stack from outer ones towards inner ones: outer scope -> inner scope
|
||||
sink->set_formatter(expr::stream << expr::format_named_scope("Scopes", "%n"));
|
||||
|
||||
The first argument names the attribute and the second is the format string. The string can contain the following placeholders:
|
||||
|
||||
[table Named scope format placeholders
|
||||
[[Placeholder][Meaning][Example]]
|
||||
[[%n] [Scope name]["void foo()"]]
|
||||
[[%f] [Source file name of the scope]["foo.cpp"]]
|
||||
[[%l] [Line number in the source file]["45"]]
|
||||
]
|
||||
|
||||
While the format string describes the presentation of each named scope in the list, the following named arguments allow to customize the list traversal and formatting:
|
||||
|
||||
* `format`. The named scope format string, as described above. This parameter is used to specify the format when other named parameters are used.
|
||||
* `iteration`. The argument describes the direction of iteration through scopes. Can have values `forward` (default) or `reverse`.
|
||||
* `delimiter`. The argument can be used to specify the delimiters between scopes. The default delimiter depends on the `iteration` argument. If `iteration == forward` the default `delimiter` will be "->", otherwise it will be "<-".
|
||||
* `depth`. The argument can be used to limit the number of scopes to put to log. The formatter will print `depth` innermost scopes and, if there are more scopes left, append an ellipsis to the written sequence. By default the formatter will write all scope names.
|
||||
|
||||
Here are a few usage examples:
|
||||
|
||||
// Puts the scope stack in reverse order:
|
||||
// inner scope (file:line) <- outer scope (file:line)
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< expr::format_named_scope(
|
||||
"Scopes",
|
||||
keywords::format = "%n (%f:%l)",
|
||||
keywords::iteration = expr::reverse)
|
||||
);
|
||||
|
||||
// Puts the scope stack in reverse order with a custom delimiter:
|
||||
// inner scope | outer scope
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< expr::named_scope(
|
||||
"Scopes",
|
||||
keywords::format = "%n",
|
||||
keywords::iteration = expr::reverse,
|
||||
keywords::delimiter = " | ")
|
||||
);
|
||||
|
||||
// Puts the scope stack in forward order, no more than 2 inner scopes:
|
||||
// ... -> outer scope -> inner scope
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< expr::named_scope(
|
||||
"Scopes",
|
||||
keywords::format = "%n",
|
||||
keywords::iteration = expr::forward,
|
||||
keywords::depth = 2)
|
||||
);
|
||||
|
||||
// Puts the scope stack in reverse order, no more than 2 inner scopes:
|
||||
// inner scope <- outer scope <- ...
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< expr::named_scope(
|
||||
"Scopes",
|
||||
keywords::format = "%n",
|
||||
keywords::iteration = expr::reverse,
|
||||
keywords::depth = 2)
|
||||
);
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:conditional Conditional formatters]
|
||||
|
||||
#include <``[boost_log_expressions_formatters_if_hpp]``>
|
||||
|
||||
There are cases when one would want to check some condition about the log record and format it depending on that condition. One example of such a need is formatting an attribute value depending on its runtime type. The general syntax of the conditional formatter is as follows:
|
||||
|
||||
expr::if_ (filter)
|
||||
[
|
||||
true_formatter
|
||||
]
|
||||
.else_
|
||||
[
|
||||
false_formatter
|
||||
]
|
||||
|
||||
Those familiar with __boost_phoenix__ lambda expressions will find this syntax quite familiar. The `filter` argument is a filter that is applied to the record being formatted. If it returns `true`, the `true_formatter` is executed, otherwise `false_formatter` is executed. The `else_` section with `false_formatter` is optional. If it is omitted and `filter` yields `false`, no formatter is executed. Here is an example:
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
// First, put the current time
|
||||
<< expr::format_date_time("TimeStamp", "%Y-%m-%d %H:%M:%S.%f") << " "
|
||||
<< expr::if_ (expr::has_attr< int >("ID"))
|
||||
[
|
||||
// if "ID" is present then put it to the record
|
||||
expr::stream << expr::attr< int >("ID")
|
||||
]
|
||||
.else_
|
||||
[
|
||||
// otherwise put a missing marker
|
||||
expr::stream << "--"
|
||||
]
|
||||
// and after that goes the log record text
|
||||
<< " " << expr::message
|
||||
);
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:decorators Character decorators]
|
||||
|
||||
There are times when one would like to additionally post-process the composed string before passing it to the sink backend. For example, in order to store log into an XML file the formatted log record should be checked for special characters that have a special meaning in XML documents. This is where decorators step in.
|
||||
|
||||
[note Unlike most other formatters, decorators are dependent on the character type of the formatted output and this type cannot be deduced from the decorated formatter. By default, the character type is assumed to be `char`. If the formatter is used to compose a wide-character string, prepend the decorator name with the `w` letter (e.g. use `wxml_decor` instead of `xml_decor`). Also, for each decorator there is a generator function that accepts the character type as a template parameter; the function is named similarly to the decorator prepended with the `make_` prefix (e.g. `make_xml_decor`).]
|
||||
|
||||
[heading XML character decorator]
|
||||
|
||||
#include <``[boost_log_expressions_formatters_xml_decorator_hpp]``>
|
||||
|
||||
This decorator replaces XML special characters (&, <, > and \') with the corresponding tokens (`&`, `<`, `>` and `'`, correspondingly). The usage is as follows:
|
||||
|
||||
xml_sink->set_formatter
|
||||
(
|
||||
// Apply the decoration to the whole formatted record
|
||||
expr::xml_decor
|
||||
[
|
||||
expr::stream << expr::message
|
||||
]
|
||||
);
|
||||
|
||||
Since character decorators are yet another kind of formatters, it's fine to use them in other contexts where formatters are appropriate. For example, this is also a valid example:
|
||||
|
||||
xml_sink->set_formatter
|
||||
(
|
||||
expr::format("<message>%1%: %2%</message>")
|
||||
% expr::attr< unsigned int >("LineID")
|
||||
% expr::xml_decor[ expr::stream << expr::message ]; // Only decorate the message text
|
||||
);
|
||||
|
||||
There is an example of the library set up for logging into an XML file, see [@boost:/libs/log/example/doc/sinks_xml_file.cpp here].
|
||||
|
||||
[heading CSV character decorator]
|
||||
|
||||
#include <``[boost_log_expressions_formatters_csv_decorator_hpp]``>
|
||||
|
||||
This decorator allows to ensure that the resulting string conforms to the [@http://en.wikipedia.org/wiki/Comma-separated_values CSV] format requirements. In particular, it duplicates the quote characters in the formatted string.
|
||||
|
||||
csv_sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< expr::attr< unsigned int >("LineID") << ","
|
||||
<< expr::csv_decor[ expr::stream << expr::attr< std::string >("Tag") ] << ","
|
||||
<< expr::csv_decor[ expr::stream << expr::message ]
|
||||
);
|
||||
|
||||
[heading C-style character decorators]
|
||||
|
||||
#include <``[boost_log_expressions_formatters_c_decorator_hpp]``>
|
||||
|
||||
The header defines two character decorators: `c_decor` and `c_ascii_decor`. The first one replaces the following characters with their escaped counerparts: \\ (backslash, 0x5c), \\a (bell character, 0x07), \\b (backspace, 0x08), \\f (formfeed, 0x0c), \\n (newline, 0x0a), \\r (carriage return, 0x0d), \\t (horizontal tabulation, 0x09), \\v (vertical tabulation, 0x0b), \' (apostroph, 0x27), \" (quote, 0x22), ? (question mark, 0x3f). The `c_ascii_decor` decorator does the same but also replaces all other non-printable and non-ASCII characters with escaped hexadecimal character codes in C notation (e.g. "\\x8c"). The usage is similar to other character decorators:
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< expr::attr< unsigned int >("LineID") << ": ["
|
||||
<< expr::c_decor[ expr::stream << expr::attr< std::string >("Tag") ] << "] "
|
||||
<< expr::c_ascii_decor[ expr::stream << expr::message ]
|
||||
);
|
||||
|
||||
[heading General character decorator]
|
||||
|
||||
#include <``[boost_log_expressions_formatters_char_decorator_hpp]``>
|
||||
|
||||
This decorator allows the user to define his own character replacement mapping in one of the two forms. The first form is a range of `std::pair`s of strings (which can be C-style strings or ranges of characters, including `std::string`s). The strings in the `first` elements of pairs will be replaced with the `second` elements of the corresponding pair.
|
||||
|
||||
std::array< std::pair< const char*, const char* >, 3 > shell_escapes =
|
||||
{
|
||||
{ "\"", "\\\"" },
|
||||
{ "'", "\\'" },
|
||||
{ "$", "\\$" }
|
||||
};
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::char_decor(shell_escapes)
|
||||
[
|
||||
expr::stream << expr::message
|
||||
]
|
||||
);
|
||||
|
||||
The second form is two same-sized sequences of strings; the first containing the search patterns and the second - the corresponding replacements.
|
||||
|
||||
std::array< const char*, 3 > shell_patterns =
|
||||
{
|
||||
"\"", "'", "$"
|
||||
};
|
||||
std::array< const char*, 3 > shell_replacements =
|
||||
{
|
||||
"\\\"", "\\'", "\\$"
|
||||
};
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::char_decor(shell_patterns, shell_replacements)
|
||||
[
|
||||
expr::stream << expr::message
|
||||
]
|
||||
);
|
||||
|
||||
In both cases the patterns are not interpreted and are sought in the formatted characters in the original form.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
364
doc/extension.qbk
Normal file
364
doc/extension.qbk
Normal file
@ -0,0 +1,364 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:extension Extending the library]
|
||||
|
||||
[section:sinks Writing your own sinks]
|
||||
|
||||
#include <``[boost_log_sinks_basic_sink_backend_hpp]``>
|
||||
|
||||
As was described in the [link log.design Design overview] section, sinks consist of two parts: frontend and backend. Frontends are provided by the library and usually do not need to be reimplemented. Thanks to frontends, implementing backends is much easier than it could be: all filtering, formatting and thread synchronization is done there.
|
||||
|
||||
In order to develop a sink backend, you derive your class from either [class_sinks_basic_sink_backend] or [class_sinks_basic_formatted_sink_backend], depending on whether your backend requires formatted log records or not. Both base classes define a set of types that are required to interface with sink frontends. One of these types is `frontend_requirements`.
|
||||
|
||||
[heading Frontend requirements]
|
||||
|
||||
#include <``[boost_log_sinks_frontend_requirements_hpp]``>
|
||||
|
||||
In order to work with sink backends, frontends use the `frontend_requirements` type defined by all backends. The type combines one or several requirement tags:
|
||||
|
||||
* [class_sinks_synchronized_feeding]. If the backend has this requirement, it expects log records to be passed from frontend in synchronized manner (i.e. only one thread should be feeding a record at a time). Note that different threads may be feeding different records, the requirement merely states that there will be no concurrent feeds.
|
||||
* [class_sinks_concurrent_feeding]. This requirement extends [class_sinks_synchronized_feeding] by allowing different threads to feed records concurrently. The backend implements all necessary thread synchronization in this case.
|
||||
* [class_sinks_formatted_records]. The backend expects formatted log records. The frontend implements formatting to a string with character type defined by the `char_type` typedef within the backend. The formatted string will be passed along with the log record to the backend. The [class_sinks_basic_formatted_sink_backend] base class automatically adds this requirement to the `frontend_requirements` type.
|
||||
* [class_sinks_flushing]. The backend supports flushing its internal buffers. If the backend indicates this requirement it has to implement the `flush` method taking no arguments; this method will be called by the frontend when flushed.
|
||||
|
||||
[tip By chosing either of the thread synchronization requirements you effectively allow or prohibit certain [link log.detailed.sink_frontends sink frontends] from being used with your backend.]
|
||||
|
||||
Multiple requirements can be combined into `frontend_requirements` type with the [class_sinks_combine_requirements] metafunction:
|
||||
|
||||
typedef sinks::combine_requirements<
|
||||
sinks::synchronized_feeding,
|
||||
sinks::formatted_records,
|
||||
sinks::flushing
|
||||
>::type frontend_requirements;
|
||||
|
||||
It must be noted that [class_sinks_synchronized_feeding] and [class_sinks_concurrent_feeding] should not be combined together as it would make the synchronization requirement ambiguous. The [class_sinks_synchronized_feeding] is a more strict requirement than [class_sinks_concurrent_feeding], so whenever the backend requires concurrent feeding it is also capable of synchronized feeding.
|
||||
|
||||
The [class_sinks_has_requirement] metafunction can be used to test for a specific requirement in the `frontend_requirements` typedef.
|
||||
|
||||
[heading Minimalistic sink backend]
|
||||
|
||||
As an example of the [class_sinks_basic_sink_backend] class usage, let's implement a simple statistical information collector backend. Assume we have a network server and we want to monitor how many incoming connections are active and how much data was sent or received. The collected information should be written to a CSV-file every minute. The backend definition could look something like this:
|
||||
|
||||
[example_extension_stat_collector_definition]
|
||||
|
||||
As you can see, the public interface of the backend is quite simple. Only the `consume` and `flush` methods are called by frontends. The `consume` function is called every time a logging record passes filtering in the frontend. The record, as was stated before, contains a set of attribute values and the message string. Since we have no need for the record message, we will ignore it for now. But from the other attributes we can extract the statistical data to accumulate and write to the file. We can use [link log.detailed.expressions.attr_keywords attribute keywords] and [link log.detailed.attributes.related_components.value_processing.visitation value visitation] to accomplish this.
|
||||
|
||||
[example_extension_stat_collector_consume]
|
||||
|
||||
Note that we used __boost_phoenix__ to automatically generate visitor function objects for attribute values.
|
||||
|
||||
The last bit of implementation is the `flush` method. It is used to flush all buffered data to the external storage, which is a file in our case. The method can be implemented in the following way:
|
||||
|
||||
[example_extension_stat_collector_flush]
|
||||
|
||||
You can find the complete code of this example [@boost:/libs/log/example/doc/extension_stat_collector.cpp here].
|
||||
|
||||
[heading Formatting sink backend]
|
||||
|
||||
As an example of a formatting sink backend, let's implement a sink that will display text notifications for every log record passed to it.
|
||||
|
||||
[tip Real world applications would probably use some GUI toolkit API to display notifications but GUI programming is out of scope of this documentation. In order to display notifications we shall use an external program which does just that. In this example we shall employ `notify-send` program which is available on Linux (Ubuntu/Debian users can install it with the `libnotify-bin` package; other distros should also have it available in their package repositories). The program takes the notification parameters in the command line, displays the notification in the current desktop environment and then exits. Other platforms may also have similar tools.]
|
||||
|
||||
The definition of the backend is very similar to what we have seen in the previous section:
|
||||
|
||||
[example_extension_app_launcher_definition]
|
||||
|
||||
The first thing to notice is that the `app_launcher` backend derives from [class_sinks_basic_formatted_sink_backend] rather than [class_sinks_basic_sink_backend]. This base class accepts the character type in addition to the requirements. The specified character type defines the target string type the formatter will compose in the frontend and it typically corresponds to the underlying API the backend uses to process records. It must be mentioned that the character type the backend requires is not related to the character types of string attribute values, including the message text. The formatter will take care of character code conversion when needed.
|
||||
|
||||
The second notable difference from the previous examples is that `consume` method takes an additional string parameter besides the log record. This is the result of formatting. The `string_type` type is defined by the [class_sinks_basic_formatted_sink_backend] base class and it corresponds to the requested character type.
|
||||
|
||||
We don't need to flush any buffers in this example, so we didn't specify the [class_sinks_flushing] requirement and omitted the `flush` method in the backens. Although we don't need any synchronization in our backend, we specified [class_sinks_synchronized_feeding] requirement so that we don't spawn multiple instances of `notify-send` program and cause a "fork bomb".
|
||||
|
||||
Now, the `consume` implementation is trivial:
|
||||
|
||||
[example_extension_app_launcher_consume]
|
||||
|
||||
So the formatted string is expected to actually be a command line to start the application. The exact application name and arguments are to be determined by the formatter. This approach adds flexibility because the backend can be used for different purposes and updating the command line is as easy as updating the formatter.
|
||||
|
||||
The sink can be configured with the following code:
|
||||
|
||||
[example_extension_app_launcher_formatting]
|
||||
|
||||
The most interesting part is the sink setup. The [class_sinks_synchronous_sink] frontend (as well as any other frontend) will detect that the `app_launcher` backend requires formatting and enable the corresponding functionality. The `set_formatter` method becomes available and can be used to set the formatting expression that composes the command line to start the `notify-send` program. We used [link log.detailed.expressions.attr_keywords attribute keywords] to identify particular attribute values in the formatter. Notice that string attribute values have to be preprocessed so that special characters interpreted by the shell are escaped in the command line. We achieve that with the [funcref boost::log::expressions::char_decor `char_decor`] decorator with our custom replacement map. After the sink is configured we also add the [link log.detailed.attributes.process_name current process name] attribute to the core so that we don't have to add it to every record.
|
||||
|
||||
After all this is done, we can finally display some notifications:
|
||||
|
||||
[example_extension_app_launcher_logging]
|
||||
|
||||
The complete code of this example is available [@boost:/libs/log/example/doc/extension_app_launcher.cpp here].
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:sources Writing your own sources]
|
||||
|
||||
#include <``[boost_log_sources_threading_models_hpp]``>
|
||||
#include <``[boost_log_sources_basic_logger_hpp]``>
|
||||
|
||||
You can extend the library by developing your own sources and, for that matter, ways of collecting log data. Basically, you have two choices of how to start: you can either develop a new logger feature or design a whole new type of source. The first approach is good if all you need is to tweak the functionality of the existing loggers. The second approach is reasonable if the whole mechanism of collecting logs by the provided loggers is unsuitable for your needs.
|
||||
|
||||
[heading Creating a new logger feature]
|
||||
|
||||
Every logger provided by the library consists of a number of features that can be combined with each other. Each feature is responsible for a single and independent aspect of the logger functionality. For example, loggers that provide the ability to assign severity levels to logging records include the [class_sources_severity] feature. You can implement your own feature and use it along with the ones provided by the library.
|
||||
|
||||
A logger feature should follow these basic requirements:
|
||||
|
||||
* A logging feature should be a class template. It should have at least one template parameter type (let's name it `BaseT`).
|
||||
* The feature must publicly derive from the `BaseT` template parameter.
|
||||
* The feature must be default-constructible and copy-constructible.
|
||||
* The feature must be constructible with a single argument of a templated type. The feature may not use this argument itself, but it should pass this argument to the `BaseT` constructor.
|
||||
|
||||
These requirements allow composition of a logger from a number of features derived from each other. The root class of the features hierarchy will be the [class_sources_basic_logger] class template instance. This class implements most of the basic functionality of loggers, like storing logger-specific attributes and providing the interface for log message formatting. The hierarchy composition is done by the [class_sources_basic_composite_logger] class template, which is instantiated on a sequence of features (don't worry, this will be shown in an example in a few moments). The constructor with a templated argument allows initializing features with named parameters, using the __boost_parameter__ library.
|
||||
|
||||
A logging feature may also contain internal data. In that case, to maintain thread safety for the logger, the feature should follow these additional guidelines:
|
||||
|
||||
# Usually there is no need to introduce a mutex or another synchronization mechanism in each feature. Moreover, it is advised not to do so, because the same feature can be used in both thread-safe and not thread-safe loggers. Instead, features should use the threading model of the logger as a synchronization primitive, similar to how they would use a mutex. The threading model is accessible through the `get_threading_model` method, defined in the [class_sources_basic_logger] class template.
|
||||
# If the feature has to override `*_unlocked` methods of the protected interface of the [class_sources_basic_logger] class template (or the same part of the base feature interface), the following should be considered with regard to such methods:
|
||||
|
||||
* The public methods that eventually call these methods are implemented by the [class_sources_basic_composite_logger] class template. These implementations do the necessary locking and then pass control to the corresponding `_unlocked` method of the base features.
|
||||
* The thread safety requirements for these methods are expressed with lock types. These types are available as typedefs in each feature and the [class_sources_basic_logger] class template. If the feature exposes a protected function `foo_unlocked`, it will also expose type `foo_lock`, which will express the locking requirements of `foo_unlocked`. The corresponding method `foo` in the [class_sources_basic_composite_logger] class template will use this typedef in order to lock the threading model before calling `foo_unlocked`.
|
||||
* Feature constructors don't need locking, and thus there's no need for lock types for them.
|
||||
|
||||
# The feature may implement a copy constructor. The argument of the constructor is already locked with a shared lock when the constructor is called. Naturally, the feature is expected to forward the copy constructor call to the `BaseT` class.
|
||||
# The feature need not implement an assignment operator. The assignment will be automatically provided by the [class_sources_basic_composite_logger] class instance. However, the feature may provide a `swap_unlocked` method that will swap contents of this feature and the method argument, and call similar method in the `BaseT` class. The automatically generated assignment operator will use this method, along with copy constructor.
|
||||
|
||||
In order to illustrate all these lengthy recommendations, let's implement a simple logger feature. Suppose we want our logger to be able to tag individual log records. In other words, the logger has to temporarily add an attribute to its set of attributes, emit the logging record, and then automatically remove the attribute. Somewhat similar functionality can be achieved with scoped attributes, although the syntax may complicate wrapping it into a neat macro:
|
||||
|
||||
// We want something equivalent to this
|
||||
{
|
||||
BOOST_LOG_SCOPED_LOGGER_TAG(logger, "Tag", "[GUI]");
|
||||
BOOST_LOG(logger) << "The user has confirmed his choice";
|
||||
}
|
||||
|
||||
Let's declare our logger feature:
|
||||
|
||||
[example_extension_record_tagger_declaration]
|
||||
|
||||
You can see that we use the [class_log_strictest_lock] template in order to define lock types that would fulfill the base class thread safety requirements for methods that are to be called from the corresponding methods of `record_tagger_feature`. The `open_record_lock` definition shows that the `open_record_unlocked` implementation for the `record_tagger_feature` feature requires exclusive lock (which `lock_guard` is) for the logger, but it also takes into account locking requirements of the `open_record_unlocked`, `add_attribute_unlocked` and `remove_attribute_unlocked` methods of the base class, because it will have to call them. The generated `open_record` method of the final logger class will make use of this typedef in order to automatically acquire the corresponding lock type before forwarding to the `open_record_unlocked` methods.
|
||||
|
||||
Actually, in this particular example, there was no need to use the [class_log_strictest_lock] trait, because all our methods require exclusive locking, which is already the strictest one. However, this template may come in handy, should you use shared locking.
|
||||
|
||||
The implementation of the public interface becomes quite trivial:
|
||||
|
||||
[example_extension_record_tagger_structors]
|
||||
|
||||
Now, since all locking is extracted into the public interface, we have the most of our feature logic to be implemented in the protected part of the interface. In order to set up tag value in the logger, we will have to introduce a new __boost_parameter__ keyword. Following recommendations from that library documentation, it's better to introduce the keyword in a special namespace:
|
||||
|
||||
[example_extension_record_tagger_keyword]
|
||||
|
||||
Opening a new record can now look something like this:
|
||||
|
||||
[example_extension_record_tagger_open_record]
|
||||
|
||||
Here we add a new attribute with the tag value, if one is specified in call to `open_record`. When a log record is opened, all attribute values are acquired and locked after the record, so we remove the tag from the attribute set with the __boost_scope_exit__ block.
|
||||
|
||||
Ok, we got our feature, and it's time to inject it into a logger. Assume we want to combine it with the standard severity level logging. No problems:
|
||||
|
||||
[example_extension_record_tagger_my_logger]
|
||||
|
||||
As you can see, creating a logger is a quite simple procedure. The `BOOST_LOG_FORWARD_LOGGER_MEMBERS_TEMPLATE` macro you see here is for mere convenience purpose: it unfolds into a default constructor, copy constructor, assignment operator and a number of constructors to support named arguments. For non-template loggers there is a similar `BOOST_LOG_FORWARD_LOGGER_MEMBERS` macro.
|
||||
|
||||
Assuming we have defined severity levels like this:
|
||||
|
||||
[example_extension_record_tagger_severity]
|
||||
|
||||
we can now use our logger as follows:
|
||||
|
||||
[example_extension_record_tagger_manual_logging]
|
||||
|
||||
All this verbosity is usually not required. One can define a special macro to make the code more concise:
|
||||
|
||||
[example_extension_record_tagger_macro_logging]
|
||||
|
||||
[@boost:/libs/log/example/doc/extension_record_tagger.cpp See the complete code].
|
||||
|
||||
[heading Guidelines for designers of standalone logging sources]
|
||||
|
||||
In general, you can implement new logging sources the way you like, the library does not mandate any design requirements on log sources. However, there are some notes regarding the way log sources should interact with logging core.
|
||||
|
||||
# Whenever a logging source is ready to emit a log record, it should call the `open_record` in the core. The source-specific attributes should be passed into that call. During that call the core allocates resources for the record being made and performs filtering.
|
||||
# If the call to `open_record` returned a valid log record, then the record has passed the filtering and is considered to be opened. The record may later be either confirmed by the source by subsequently calling `push_record` or withdrawn by destroying it.
|
||||
# If the call to `open_record` returned an invalid (empty) log record, it means that the record has not been opened (most likely due to filtering rejection). In that case the logging core does not hold any resources associated with the record, and thus the source must not call `push_record` for that particular logging attempt.
|
||||
# The source may subsequently open more than one record. Opened log records exist independently from each other.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:attributes Writing your own attributes]
|
||||
|
||||
#include <``[boost_log_attributes_attribute_hpp]``>
|
||||
#include <``[boost_log_attributes_attribute_value_hpp]``>
|
||||
#include <``[boost_log_attributes_attribute_value_impl_hpp]``>
|
||||
|
||||
Developing your own attributes is quite simple. Generally, you need to do the following:
|
||||
|
||||
# Define what will be the attribute value. Most likely, it will be a piece of constant data that you want to participate in filtering and formatting. Envelop this data into a class that derives from the [class_log_attribute_value_impl] interface; this is the attribute value implementation class. This object will have to implement the `dispatch` method that will extract the stored data (or, in other words, the stored value) to a type dispatcher. In most cases the class [class_attributes_attribute_value_impl] provided by the library can be used for this.
|
||||
# Use the [class_log_attribute_value] class as the interface class that holds a reference to the attribute value implementation.
|
||||
# Define how attribute values are going to be produced. In a corner case the values do not need to be produced (like in the case of the [class_attributes_constant] attribute provided by the library), but often there is some logic that needs to be invoked to acquire the attribute value. This logic has to be concentrated in a class derived from the [class_log_attribute_impl] interface, more precisely - in the `get_value` method. This class is the attribute implementation class. You can think of it as an attribute value factory.
|
||||
# Define the attribute interface class that derives from [class_log_attribute]. By convention, the interface class should create the corresponding implementation on construction and pass the pointer to it to the [class_log_attribute] class constructor. The interface class may have interface methods but it typically should not contain any data members as the data will be lost when the attribute is added to the library. All relevant data should be placed in the implementation class instead.
|
||||
|
||||
While designing an attribute, one has to strive to make it as independent from the values it produces, as possible. The attribute can be called from different threads concurrently to produce a value. Once produced, the attribute value can be used several times by the library (maybe even concurrently), it can outlive the attribute object that created it, and several attribute values produced by the same attribute can exist simultaneously.
|
||||
|
||||
From the library perspective, each attribute value is considered independent from other attribute values or the attribute itself. That said, it is still possible to implement attributes that are also attribute values, which allows to optimize performance in some cases. This is possible if the following requirements are fulfilled:
|
||||
|
||||
* The attribute value never changes, so it's possible to store it in the attribute itself. The [class_attributes_constant] attribute is an example.
|
||||
* The attribute stores its value in a global (external with regard to the attribute) storage, that can be accessed from any attribute value. The attribute values must guarantee, though, that their stored values do not change over time and are safely accessible concurrently from different threads.
|
||||
|
||||
As a special case for the second point, it is possible to store attribute values (or their parts) in a thread-specific storage. However, in that case the user has to implement the `detach_from_thread` method of the attribute value implementation properly. The result of this method - another attribute value - must be independent from the thread it is being called in, but its stored value should be equivalent to the original attribute value. This method will be called by the library when the attribute value passes to a thread that is different from the thread where it was created. As of this moment, this will only happen in the case of [link log.detailed.sink_frontends.async asynchronous logging sinks].
|
||||
|
||||
But in the vast majority of cases attribute values must be self-contained objects with no dependencies on other entities. In fact, this case is so common that the library provides a ready to use attribute value implementation class template [class_attributes_attribute_value_impl] and [funcref boost::log::attributes::make_attribute_value make_attribute_value] generator function. The class template has to be instantiated on the stored value type, and the stored value has to be provided to the class constructor. For example, let's implement an attribute that returns system uptime in seconds. This is the attribute implementation class.
|
||||
|
||||
[example_extension_system_uptime_attr_impl]
|
||||
|
||||
Since there is no need for special attribute value classes we can use the [funcref boost::log::attributes::make_attribute_value make_attribute_value] function to create the value envelop.
|
||||
|
||||
[tip For cases like this, when the attribute value can be obtained in a single function call, it is typically more convenient to use the [link log.detailed.attributes.function `function`] attribute.]
|
||||
|
||||
The interface class of the attribute can be defined as follows:
|
||||
|
||||
[example_extension_system_uptime_attr]
|
||||
|
||||
As it was mentioned before, the default constructor creates the implementation instance so that the default constructed attribute can be used by the library.
|
||||
|
||||
The second constructor adds support for [link log.detailed.attributes attribute casting]. The constructor argument contains a reference to an attribute implementation object, and by calling `as` on it the constructor attempts to upcast it to the implementation object of our custom attribute. The `as` method will return `NULL` if the upcast fails, which will result in an empty attribute constructed in case of failure.
|
||||
|
||||
Having defined these two classes, the attribute can be used with the library as usual:
|
||||
|
||||
[example_extension_system_uptime_use]
|
||||
|
||||
[@boost:/libs/log/example/doc/extension_system_uptime_attr.cpp See the complete code].
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:settings Extending library settings support]
|
||||
|
||||
If you write your own logging sinks or use your own types in attributes, you may want to add support for these components to the settings parser provided by the library. Without doing this, the library will not be aware of your types and thus will not be able to use them when parsing settings.
|
||||
|
||||
[heading Adding support for user-defined types to the formatter parser]
|
||||
|
||||
#include <``[boost_log_utility_setup_formatter_parser_hpp]``>
|
||||
|
||||
In order to add support for user-defined types to the formatter parser, one has to register a formatter factory. The factory is basically an object that derives from [class_log_formatter_factory] interface. The factory mainly implements the single `create_formatter` method which, when called, will construct a formatter for the particular attribute value.
|
||||
|
||||
When the user-defined type supports putting to a stream with `operator<<` and this operator behavior is suitable for logging, one can use a simple generic formatter factory provided by the library out of the box. For example, let's assume we have the following user-defined type that we want to use as an attribute value:
|
||||
|
||||
[example_extension_formatter_parser_point_definition]
|
||||
|
||||
Then, in order to register this type with the simple formatter factory, a single call to [funcref boost::log::register_simple_formatter_factory `register_simple_formatter_factory`] will suffice:
|
||||
|
||||
[example_extension_simple_formatter_factory]
|
||||
|
||||
[note The `operator<<` for the attribute value stored type must be visible from the point of this call.]
|
||||
|
||||
The function takes the stored attribute value type (`point`, in our case) and the target character type used by formatters as template parameters. From the point of this call, whenever the formatter parser encounters a reference to the "Coordinates" attribute in the format string, it will invoke the formatter factory, which will construct the formatter that calls our `operator<<` for class `point`.
|
||||
|
||||
[tip It is typically a good idea to register all formatter factories at an early stage of the application initialization, before any other library initialization, such as reading config files.]
|
||||
|
||||
From the [link log.detailed.utilities.setup.filter_formatter formatter parser description] it is known that the parser supports passing additional parameters from the format string to the formatter factory. We can use these parameters to customize the output generated by the formatter.
|
||||
|
||||
For example, let's implement customizable formatting of our `point` objects, so that the following format string works as expected:
|
||||
|
||||
[pre %TimeStamp% %Coordinates(format\="{%0.3f; %0.3f}")% %Message%]
|
||||
|
||||
The simple formatter factory ignores all additional parameters from the format string, so we have to implement our own factory instead. Custom factories are registered with the [funcref boost::log::register_formatter_factory `register_formatter_factory`] function, which is similar to [funcref boost::log::register_simple_formatter_factory `register_simple_formatter_factory`] but accepts a pointer to the factory instead of the explicit template parameters.
|
||||
|
||||
[example_extension_custom_formatter_factory]
|
||||
|
||||
Let's walk through this code sample. Our `point_formatter_factory` class derives from the [class_log_basic_formatter_factory] base class provided by the library. This class derives from the base [class_log_formatter_factory] interface and defines a few useful types, such as `formatter_type` and `args_map` that we use. The only thing left to do in our factory is to define the `create_formatter` method. The method analyzes the parameters from the format string which are passed as the `args` argument, which is basically `std::map` of string keys (parameter names) to string values (the parameter values). We seek for the `format` parameter and expect it to contain a __boost_format__-compatible format string for our `point` objects. If the parameter is found we create a formatter that invokes `point_formatter` for the attribute values. Otherwise we create a default formatter that simply uses the `operator<<`, like the simple formatter factory does. Note that we use the `name` argument of `create_formatter` to identify the attribute so that the same factory can be used for different attributes.
|
||||
|
||||
The `point_formatter` is our custom formatter based on __boost_format__. With help of __boost_phoenix__ and [link log.detailed.expressions.attr expression placeholders] we can construct a formatter that will extract the attribute value and pass it along with the target stream to the `point_formatter` function object. Note that the formatter accepts the attribute value wrapped into the [link log.detailed.utilities.value_ref `value_ref`] wrapper which can be empty if the value is not present.
|
||||
|
||||
Lastly, the call to [funcref boost::log::register_formatter_factory `register_formatter_factory`] creates the factory and adds it to the library.
|
||||
|
||||
You can find the complete code of this example [@boost:/libs/log/example/doc/extension_formatter_parser.cpp here].
|
||||
|
||||
[heading Adding support for user-defined types to the filter parser]
|
||||
|
||||
#include <``[boost_log_utility_setup_filter_parser_hpp]``>
|
||||
|
||||
You can extend filter parser in the similar way you can extend the formatter parser - by registering filter factories for your attribute values into the library. However, since it takes a considerably more complex syntax to describe filters, a filter factory typically implements several generator functions.
|
||||
|
||||
Like with formatter parser extension, you can avoid spelling out the filter factory and register a simple factlry provided by the library:
|
||||
|
||||
[example_extension_simple_filter_factory]
|
||||
|
||||
In order this to work the user's type should fullfill these requirements:
|
||||
|
||||
# Support reading from an input stream with `operator>>`.
|
||||
# Support the complete set of comparison and ordering operators.
|
||||
|
||||
Naturally, all these operators must be visible from the point of the [funcref boost::log::register_simple_filter_factory `register_simple_filter_factory`] call. Note that unlike the simple formatter factory, the filter factory requires the user's type to support reading from a stream. This is so because the filter factory would have to parse the argument of the filter relation from a string.
|
||||
|
||||
But we won't get away with a simple filter factory, because our `point` class doesn't have a sensible ordering semantics and thus we cannot define the complete set of operators. We'll have to implement our own filter factory instead. Filter factories derive from the [class_log_filter_factory] interface. This base class declares a number of virtual functions that will be called in order to create filters, according to the filter expression. If some functions are not overriden by the factory, the corresponding operations are considered to be not supported by the attribute value. But before we define the filter factory we have to improve our `point` class slightly:
|
||||
|
||||
[example_extension_filter_parser_point_definition]
|
||||
|
||||
We have added comparison and input operators for the `point` class. The output operator is still used by formatters and not required by the filter factory. Now we can define and register the filter factory:
|
||||
|
||||
[example_extension_custom_filter_factory]
|
||||
|
||||
Having called the [funcref boost::log::register_filter_factory `register_filter_factory`] function, whenever the [link log.detailed.utilities.setup.filter_formatter filter parser] encounters the "Coordinates" attribute mentioned in the filter, it will use the `point_filter_factory` object to construct the appropriate filter. For example, in the case of the following filter
|
||||
|
||||
[pre %Coordinates% \= "(10, 10)"]
|
||||
|
||||
the `on_equality_relation` method will be called with `name` argument being "Coordinates" and `arg` being "(10, 10)".
|
||||
|
||||
[note The quotes around the parenthesis are necessary because the filter parser should interpret the point coordinates as a single string. Also, round brackets are already used to group subexpressions of the filter expression. Whenever there is need to pass several parameters to the relation (like in this case - a number of components of the `point` class) the parameters should be encoded into a quoted string. The string may include C-style escape sequences that will be unfolded upon parsing.]
|
||||
|
||||
The constructed filter will use the corresponding comparison operators for the `point` class. Ordering operations, like ">" or "<=", will not be supported for attributes named "Coordinates", and this is exactly the way we want it, because the `point` class does not support them either. The complete example is available [@boost:/libs/log/example/doc/extension_filter_parser.cpp here].
|
||||
|
||||
The library allows not only adding support for new types, but also associating new relations with them. For instance, we can create a new relation "is_in_rect" that will yield positive if the coordinates fit into a rectangle denoted with two points. The filter might look like this:
|
||||
|
||||
[pre %Coordinates% is\_in\_rect "{(10, 10) - (20, 20)}"]
|
||||
|
||||
First, let's define our rectangle class:
|
||||
|
||||
[example_extension_filter_parser_rectangle_definition]
|
||||
|
||||
As it was said, the rectangle is described by two points - the top left and the bottom right corners of the rectangle area. Now let's extend our filter factory with the `on_custom_relation` method:
|
||||
|
||||
[example_extension_custom_filter_factory_with_custom_rel]
|
||||
|
||||
The `on_custom_relation` method is called with the relation name (the "is\_in\_rect" string in our case) and the right-hand argument for the relation (the rectangle description). All we have to do is to construct the filter, which is implemented by our `is_in_rect` function. We use `bind` from __boost_phoenix__ to compose the filter from the function and the [link log.detailed.expressions.attr attribute placeholder]. You can find the complete code of this example [@boost:/libs/log/example/doc/extension_filter_parser_custom_rel.cpp here].
|
||||
|
||||
[heading Adding support for user-defined sinks]
|
||||
|
||||
#include <``[boost_log_utility_setup_from_settings_hpp]``>
|
||||
#include <``[boost_log_utility_setup_from_stream_hpp]``>
|
||||
|
||||
The library provides mechanism of extending support for sinks similar to the formatter and filter parsers. In order to be able to mention user-defined sinks in a settings file, the user has to register a sink factory, which essentially contains the `create_sink` method that receives a [link log.detailed.utilities.setup.settings settings subsection] and returns a pointer to the initialized sink. The factory is registered for a specific destination (see the [link log.detailed.utilities.setup.settings settings file description]), so whenever a sink with the specified destination is mentioned in the settings file, the factory gets called.
|
||||
|
||||
For example, let's register the `stat_collector` sink we described [link log.extension.sinks before] in the library. First, let's remember the sink definition:
|
||||
|
||||
[example_extension_stat_collector_settings_definition]
|
||||
|
||||
Compared to the earlier definition we added the `write_interval` constructor parameter so we can set the statistical information flush interval in the settings file. The implementation of the sink stays pretty much the same as before. Now we have to define the factory:
|
||||
|
||||
[example_extension_stat_collector_factory]
|
||||
|
||||
As you can see, we read parameters from settings and simply create our sink with them as a result of `create_sink` method. Generally, users are free to name parameters of their sinks the way they like, as long as [link log.detailed.utilities.setup.settings_file settings file format] is adhered. However, it is a good idea to follow the pattern established by the library and reuse parameter names with the same meaning. That is, it should be obvious that the parameter "Filter" means the same for both the library-provided "TextFile" sink and out custom "StatCollector" sink.
|
||||
|
||||
After defining the factory we only have to register it with the [funcref boost::log::register_sink_factory `register_sink_factory`] call. The first argument is the new value of the "Destination" parameter in the settings. Whenever the library finds sink description with destination "StatCollector", our factory will be invoked to create the sink. It is also possible to override library-provided destination types with user-defined factories, however it is not possble to restore the default factories afterwards.
|
||||
|
||||
[note As the "Destination" parameter is used to determine the sink factory, this parameter is reserved and cannot be used by sink factories for their own purposes.]
|
||||
|
||||
Now that the factory is registered, we can use it when initializing from files or settings. For example, this is what the settings file could look like:
|
||||
|
||||
[pre
|
||||
\[Sinks.MyStat\]
|
||||
|
||||
Destination\=StatCollector
|
||||
FileName\=stat.csv
|
||||
WriteInterval\=30
|
||||
]
|
||||
|
||||
The complete code of the example in this section can be found [@boost:/libs/log/example/doc/extension_stat_collector_settings.cpp here].
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
40
doc/filters_reference.qbk
Normal file
40
doc/filters_reference.qbk
Normal file
@ -0,0 +1,40 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
|
||||
This document was automatically generated, DO NOT EDIT!
|
||||
/]
|
||||
|
||||
[template boost_log_filters_attr_hpp[][headerref boost/log/filters/attr.hpp]]
|
||||
|
||||
[template class_filters_flt_attr[][classref boost::log::filters::flt_attr flt_attr]]
|
||||
|
||||
[template boost_log_filters_basic_filters_hpp[][headerref boost/log/filters/basic_filters.hpp]]
|
||||
|
||||
[template class_filters_filter_base[][classref boost::filters::filter_base filter_base]]
|
||||
|
||||
[template class_filters_is_filter[][classref boost::filters::is_filter is_filter]]
|
||||
|
||||
[template class_filters_basic_filter[][classref boost::filters::basic_filter basic_filter]]
|
||||
|
||||
[template class_filters_flt_wrap[][classref boost::filters::flt_wrap flt_wrap]]
|
||||
|
||||
[template class_filters_flt_negation[][classref boost::filters::flt_negation flt_negation]]
|
||||
|
||||
[template class_filters_flt_and[][classref boost::filters::flt_and flt_and]]
|
||||
|
||||
[template class_filters_flt_or[][classref boost::filters::flt_or flt_or]]
|
||||
|
||||
[template boost_log_filters_exception_policies_hpp[][headerref boost/log/filters/exception_policies.hpp]]
|
||||
|
||||
[template class_filters_no_throw_policy[][classref boost::filters::no_throw_policy no_throw_policy]]
|
||||
|
||||
[template class_filters_throw_policy[][classref boost::filters::throw_policy throw_policy]]
|
||||
|
||||
[template boost_log_filters_has_attr_hpp[][headerref boost/log/filters/has_attr.hpp]]
|
||||
|
||||
[template class_filters_flt_has_attr[][classref boost::filters::flt_has_attr flt_has_attr]]
|
82
doc/formatters_reference.qbk
Normal file
82
doc/formatters_reference.qbk
Normal file
@ -0,0 +1,82 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
|
||||
This document was automatically generated, DO NOT EDIT!
|
||||
/]
|
||||
|
||||
[template boost_log_formatters_attr_hpp[][headerref boost/log/formatters/attr.hpp]]
|
||||
|
||||
[template class_formatters_fmt_attr[][classref boost::formatters::fmt_attr fmt_attr]]
|
||||
|
||||
[template class_formatters_fmt_attr_formatted[][classref boost::formatters::fmt_attr_formatted fmt_attr_formatted]]
|
||||
|
||||
[template boost_log_formatters_basic_formatters_hpp[][headerref boost/log/formatters/basic_formatters.hpp]]
|
||||
|
||||
[template class_formatters_formatter_base[][classref boost::formatters::formatter_base formatter_base]]
|
||||
|
||||
[template class_formatters_is_formatter[][classref boost::formatters::is_formatter is_formatter]]
|
||||
|
||||
[template class_formatters_basic_formatter[][classref boost::formatters::basic_formatter basic_formatter]]
|
||||
|
||||
[template boost_log_formatters_c_decorator_hpp[][headerref boost/log/formatters/c_decorator.hpp]]
|
||||
|
||||
[template class_formatters_fmt_c_ascii_decorator[][classref boost::formatters::fmt_c_ascii_decorator fmt_c_ascii_decorator]]
|
||||
|
||||
[template boost_log_formatters_chain_hpp[][headerref boost/log/formatters/chain.hpp]]
|
||||
|
||||
[template class_formatters_fmt_chain[][classref boost::formatters::fmt_chain fmt_chain]]
|
||||
|
||||
[template boost_log_formatters_char_decorator_hpp[][headerref boost/log/formatters/char_decorator.hpp]]
|
||||
|
||||
[template class_formatters_fmt_char_decorator[][classref boost::log::formatters::fmt_char_decorator fmt_char_decorator]]
|
||||
|
||||
[template boost_log_formatters_csv_decorator_hpp[][headerref boost/log/formatters/csv_decorator.hpp]]
|
||||
|
||||
[template boost_log_formatters_date_time_hpp[][headerref boost/log/formatters/date_time.hpp]]
|
||||
|
||||
[template class_formatters_fmt_date_time_facade[][classref boost::formatters::fmt_date_time_facade fmt_date_time_facade]]
|
||||
|
||||
[template boost_log_formatters_exception_policies_hpp[][headerref boost/log/formatters/exception_policies.hpp]]
|
||||
|
||||
[template class_formatters_no_throw_policy[][classref boost::formatters::no_throw_policy no_throw_policy]]
|
||||
|
||||
[template class_formatters_throw_policy[][classref boost::formatters::throw_policy throw_policy]]
|
||||
|
||||
[template boost_log_formatters_format_hpp[][headerref boost/log/formatters/format.hpp]]
|
||||
|
||||
[template class_formatters_fmt_format[][classref boost::formatters::fmt_format fmt_format]]
|
||||
|
||||
[template boost_log_formatters_if_hpp[][headerref boost/log/formatters/if.hpp]]
|
||||
|
||||
[template class_formatters_fmt_if_else[][classref boost::formatters::fmt_if_else fmt_if_else]]
|
||||
|
||||
[template class_formatters_fmt_if[][classref boost::formatters::fmt_if fmt_if]]
|
||||
|
||||
[template boost_log_formatters_message_hpp[][headerref boost/log/formatters/message.hpp]]
|
||||
|
||||
[template class_formatters_fmt_message[][classref boost::formatters::fmt_message fmt_message]]
|
||||
|
||||
[template boost_log_formatters_named_scope_hpp[][headerref boost/log/formatters/named_scope.hpp]]
|
||||
|
||||
[template class_formatters_fmt_named_scope[][classref boost::formatters::fmt_named_scope fmt_named_scope]]
|
||||
|
||||
[template boost_log_formatters_stream_hpp[][headerref boost/log/formatters/stream.hpp]]
|
||||
|
||||
[template class_formatters_stream_placeholder[][classref boost::formatters::stream_placeholder stream_placeholder]]
|
||||
|
||||
[template boost_log_formatters_wrappers_hpp[][headerref boost/log/formatters/wrappers.hpp]]
|
||||
|
||||
[template class_formatters_fmt_wrapper[][classref boost::formatters::fmt_wrapper fmt_wrapper]]
|
||||
|
||||
[template class_formatters_wrap_if_c[][classref boost::formatters::wrap_if_c wrap_if_c]]
|
||||
|
||||
[template class_formatters_wrap_if[][classref boost::formatters::wrap_if wrap_if]]
|
||||
|
||||
[template class_formatters_wrap_if_not_formatter[][classref boost::formatters::wrap_if_not_formatter wrap_if_not_formatter]]
|
||||
|
||||
[template boost_log_formatters_xml_decorator_hpp[][headerref boost/log/formatters/xml_decorator.hpp]]
|
79
doc/gen_references.xsl
Normal file
79
doc/gen_references.xsl
Normal file
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!--
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This stylesheet extracts information about headers, classes, etc.
|
||||
from the Doxygen-generated reference documentation and writes
|
||||
it as QuickBook templates that refer to the according Reference sections.
|
||||
-->
|
||||
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
<xsl:output method="text"/>
|
||||
<xsl:template match="/library-reference">
|
||||
<xsl:text disable-output-escaping="yes">[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
|
||||
This document was automatically generated, DO NOT EDIT!
|
||||
/]
|
||||
</xsl:text>
|
||||
<xsl:apply-templates>
|
||||
<xsl:with-param name="namespace"/>
|
||||
<xsl:with-param name="enclosing_namespace"/>
|
||||
</xsl:apply-templates>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Skip any text nodes -->
|
||||
<xsl:template match="text()"/>
|
||||
|
||||
<!-- Headers -->
|
||||
<xsl:template match="header">
|
||||
<xsl:param name="namespace"/>
|
||||
<xsl:param name="enclosing_namespace"/>
|
||||
[template <xsl:value-of select="translate(@name, '/.', '__')"/>[][headerref <xsl:value-of select="@name"/>]]
|
||||
<xsl:apply-templates>
|
||||
<xsl:with-param name="namespace" select="$namespace"/>
|
||||
<xsl:with-param name="enclosing_namespace" select="$enclosing_namespace"/>
|
||||
</xsl:apply-templates>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Namespaces - only needed to construct fully qualified class names -->
|
||||
<xsl:template match="namespace">
|
||||
<xsl:param name="namespace"/>
|
||||
<xsl:param name="enclosing_namespace"/>
|
||||
<xsl:variable name="namespace_prefix">
|
||||
<xsl:value-of select="$namespace"/><xsl:if test="string-length($namespace) > 0"><xsl:text>::</xsl:text></xsl:if>
|
||||
</xsl:variable>
|
||||
<xsl:apply-templates>
|
||||
<xsl:with-param name="namespace" select="concat($namespace_prefix, @name)"/>
|
||||
<xsl:with-param name="enclosing_namespace" select="@name"/>
|
||||
</xsl:apply-templates>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Classses -->
|
||||
<xsl:template match="class|struct">
|
||||
<xsl:param name="namespace"/>
|
||||
<xsl:param name="enclosing_namespace"/>
|
||||
[template <xsl:value-of select="concat('class_', $enclosing_namespace, '_', @name)"/>[][classref <xsl:value-of select="concat($namespace, '::', @name)"/><xsl:text> </xsl:text><xsl:value-of select="@name"/>]]
|
||||
<xsl:apply-templates>
|
||||
<xsl:with-param name="namespace" select="concat($namespace, '::', @name)"/>
|
||||
<xsl:with-param name="enclosing_namespace" select="concat($enclosing_namespace, '_', @name)"/>
|
||||
</xsl:apply-templates>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Free functions - currently disabled because multiple overloads generate duplicate QuickBook templates -->
|
||||
<!--
|
||||
<xsl:template match="function">
|
||||
<xsl:param name="namespace"/>
|
||||
<xsl:param name="enclosing_namespace"/>
|
||||
[template <xsl:value-of select="concat('func_', $enclosing_namespace, '_', @name)"/>[][funcref <xsl:value-of select="concat($namespace, '::', @name)"/><xsl:text> </xsl:text><xsl:value-of select="@name"/>]]
|
||||
</xsl:template>
|
||||
-->
|
||||
|
||||
</xsl:transform>
|
303
doc/log.qbk
Normal file
303
doc/log.qbk
Normal file
@ -0,0 +1,303 @@
|
||||
[library Boost.Log
|
||||
[quickbook 1.5]
|
||||
[version 2.0]
|
||||
[authors [Semashev, Andrey]]
|
||||
[copyright 2007 - 2013 Andrey Semashev]
|
||||
[license
|
||||
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]).
|
||||
]
|
||||
[id log]
|
||||
[source-mode c++]
|
||||
[last-revision $Date: 2013-03-10 18:29:41 +0400 (Sun, 10 Mar 2013) $]
|
||||
]
|
||||
|
||||
[c++]
|
||||
|
||||
[/ Links to external resources /]
|
||||
[def __boost_smart_ptr__ [@http://www.boost.org/doc/libs/release/libs/smart_ptr/smart_ptr.htm Boost.SmartPtr]]
|
||||
[def __boost_function__ [@http://www.boost.org/doc/libs/release/doc/html/function.html Boost.Function]]
|
||||
[def __boost_filesystem__ [@http://www.boost.org/doc/libs/release/libs/filesystem/doc/index.htm Boost.Filesystem]]
|
||||
[def __boost_system__ [@http://www.boost.org/doc/libs/release/libs/system/doc/index.html Boost.System]]
|
||||
[def __boost_date_time__ [@http://www.boost.org/doc/libs/release/doc/html/date_time.html Boost.DateTime]]
|
||||
[def __boost_date_time_format__ [@http://www.boost.org/doc/libs/release/doc/html/date_time/date_time_io.html#date_time.format_flags Boost.DateTime]]
|
||||
[def __boost_thread__ [@http://www.boost.org/doc/libs/release/doc/html/thread.html Boost.Thread]]
|
||||
[def __boost_regex__ [@http://www.boost.org/doc/libs/release/libs/regex/index.html Boost.Regex]]
|
||||
[def __boost_xpressive__ [@http://www.boost.org/doc/libs/release/doc/html/xpressive.html Boost.Xpressive]]
|
||||
[def __boost_parameter__ [@http://www.boost.org/doc/libs/release/libs/parameter/doc/html/index.html Boost.Parameter]]
|
||||
[def __boost_format__ [@http://www.boost.org/doc/libs/release/libs/format/index.html Boost.Format]]
|
||||
[def __boost_preprocessor__ [@http://www.boost.org/doc/libs/release/libs/preprocessor/doc/index.html Boost.Preprocessor]]
|
||||
[def __boost_bind__ [@http://www.boost.org/doc/libs/release/libs/bind/bind.html Boost.Bind]]
|
||||
[def __boost_lambda__ [@http://www.boost.org/doc/libs/release/doc/html/lambda.html Boost.Lambda]]
|
||||
[def __boost_phoenix__ [@http://www.boost.org/doc/libs/release/libs/phoenix/doc/html/index.html Boost.Phoenix]]
|
||||
[def __boost_spirit__ [@http://www.boost.org/doc/libs/release/libs/spirit/classic/index.html Boost.Spirit]]
|
||||
[def __boost_spirit2__ [@http://www.boost.org/doc/libs/release/libs/spirit/doc/html/index.html Boost.Spirit2]]
|
||||
[def __boost_optional__ [@http://www.boost.org/doc/libs/release/libs/optional/index.html Boost.Optional]]
|
||||
[def __boost_variant__ [@http://www.boost.org/doc/libs/release/doc/html/variant.html Boost.Variant]]
|
||||
[def __boost_intrusive__ [@http://www.boost.org/doc/libs/release/doc/html/intrusive.html Boost.Intrusive]]
|
||||
[def __boost_iostreams__ [@http://www.boost.org/doc/libs/release/libs/iostreams/doc/index.html Boost.IOStreams]]
|
||||
[def __boost_mpl__ [@http://www.boost.org/doc/libs/release/libs/mpl/doc/index.html Boost.MPL]]
|
||||
[def __boost_exception__ [@http://www.boost.org/doc/libs/release/libs/exception/doc/boost-exception.html Boost.Exception]]
|
||||
[def __boost_scope_exit__ [@http://www.boost.org/doc/libs/release/libs/scope_exit/doc/html/index.html Boost.ScopeExit]]
|
||||
[def __boost_any__ [@http://www.boost.org/doc/libs/release/doc/html/any.html Boost.Any]]
|
||||
[def __boost_property_tree__ [@http://www.boost.org/doc/libs/release/doc/html/property_tree.html Boost.PropertyTree]]
|
||||
[def __boost_asio__ [@http://www.boost.org/doc/libs/release/doc/html/boost_asio.html Boost.ASIO]]
|
||||
[def __boost_move__ [@http://www.boost.org/doc/libs/release/doc/html/move.html Boost.Move]]
|
||||
[def __boost_locale__ [@http://www.boost.org/doc/libs/release/libs/locale/doc/html/index.html Boost.Locale]]
|
||||
[def __boost_quickbook__ [@http://www.boost.org/doc/libs/release/doc/html/quickbook.html Boost.Quickbook]]
|
||||
|
||||
[/ Auto-generated macros that refer to Reference sections /]
|
||||
[include top_level_reference.qbk]
|
||||
[include core_reference.qbk]
|
||||
[include attributes_reference.qbk]
|
||||
[include expressions_reference.qbk]
|
||||
[include sources_reference.qbk]
|
||||
[include sinks_reference.qbk]
|
||||
[include utility_reference.qbk]
|
||||
[include support_reference.qbk]
|
||||
|
||||
[/ Code samples /]
|
||||
[import ../example/doc/tutorial_trivial.cpp]
|
||||
[import ../example/doc/tutorial_trivial_flt.cpp]
|
||||
[import ../example/doc/tutorial_file.cpp]
|
||||
[import ../example/doc/tutorial_file_manual.cpp]
|
||||
[import ../example/doc/tutorial_fmt_stream.cpp]
|
||||
[import ../example/doc/tutorial_fmt_stream_manual.cpp]
|
||||
[import ../example/doc/tutorial_fmt_format.cpp]
|
||||
[import ../example/doc/tutorial_fmt_string.cpp]
|
||||
[import ../example/doc/tutorial_fmt_custom.cpp]
|
||||
[import ../example/doc/tutorial_logging.cpp]
|
||||
[import ../example/doc/tutorial_attributes.cpp]
|
||||
[import ../example/doc/tutorial_filtering.cpp]
|
||||
[import ../example/doc/core_record.cpp]
|
||||
[import ../example/doc/core_core_manual.cpp]
|
||||
[import ../example/doc/attr_value_visitation.cpp]
|
||||
[import ../example/doc/attr_value_extraction.cpp]
|
||||
[import ../example/doc/expressions_attr_fmt_tag.cpp]
|
||||
[import ../example/doc/expressions_keyword_fmt_tag.cpp]
|
||||
[import ../example/doc/expressions_has_attr_stat_accum.cpp]
|
||||
[import ../example/doc/expressions_channel_severity_filter.cpp]
|
||||
[import ../example/doc/sources_net_connection.cpp]
|
||||
[import ../example/doc/sources_net_connection_chan.cpp]
|
||||
[import ../example/doc/sources_net_connection_dynamic_chan.cpp]
|
||||
[import ../example/doc/sources_severity.cpp]
|
||||
[import ../example/doc/sources_severity_channel.cpp]
|
||||
[import ../example/doc/exception_handling.cpp]
|
||||
[import ../example/doc/sinks_unlocked.cpp]
|
||||
[import ../example/doc/sinks_sync.cpp]
|
||||
[import ../example/doc/sinks_async.cpp]
|
||||
[import ../example/doc/sinks_async_bounded.cpp]
|
||||
[import ../example/doc/sinks_async_ordering.cpp]
|
||||
[import ../example/doc/sinks_ostream.cpp]
|
||||
[import ../example/doc/sinks_file.cpp]
|
||||
[import ../example/doc/sinks_xml_file.cpp]
|
||||
[import ../example/doc/sinks_multifile.cpp]
|
||||
[import ../example/doc/sinks_syslog.cpp]
|
||||
[import ../example/doc/sinks_debugger.cpp]
|
||||
[import ../example/doc/sinks_simple_event_log.cpp]
|
||||
[import ../example/doc/util_static_type_disp.cpp]
|
||||
[import ../example/doc/util_dynamic_type_disp.cpp]
|
||||
[import ../example/doc/util_manip_to_log.cpp]
|
||||
[import ../example/doc/extension_stat_collector.cpp]
|
||||
[import ../example/doc/extension_app_launcher.cpp]
|
||||
[import ../example/doc/extension_record_tagger.cpp]
|
||||
[import ../example/doc/extension_system_uptime_attr.cpp]
|
||||
[import ../example/doc/extension_formatter_parser.cpp]
|
||||
[import ../example/doc/extension_filter_parser.cpp]
|
||||
[import ../example/doc/extension_filter_parser_custom_rel.cpp]
|
||||
[import ../example/doc/extension_stat_collector_settings.cpp]
|
||||
[import ../example/wide_char/main.cpp]
|
||||
[import ../example/event_log/main.cpp]
|
||||
|
||||
[section:moti Motivation]
|
||||
|
||||
Today applications grow rapidly, becoming complicated and difficult to test and debug. Most of the time applications run on a remote site, leaving the developer little chance to monitor their execution and figure out the reasons for their failure, once it should happen. Moreover, even the local debugging may become problematic if the application behavior depends heavily on asynchronous side events, such as a device feedback or another process activity.
|
||||
|
||||
This is where logging can help. The application stores all essential information about its execution to a log, and when something goes wrong this information can be used to analyze the program behavior and make the necessary corrections. There are other very useful applications of logging, such as gathering statistical information and highlighting events (i.e. indicating that some situation has occurred or that the application is experiencing some problems). These tasks have proved to be vital for many real-world industrial applications.
|
||||
|
||||
This library aims to make logging significantly easier for the application developer. It provides a wide range of out-of-the-box tools along with public interfaces for extending the library. The main goals of the library are:
|
||||
|
||||
* Simplicity. A small example code snippet should be enough to get the feel of the library and be ready to use its basic features.
|
||||
* Extensibility. A user should be able to extend functionality of the library for collecting and storing information into logs.
|
||||
* Performance. The library should have as little performance impact on the user's application as possible.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:how_to_read How to read the documentation]
|
||||
|
||||
The documentation is oriented to both new and experienced library users. However, users are expected to be familiar with commonly used Boost components, such as `shared_ptr`, `make_shared` (see __boost_smart_ptr__), and `function` (__boost_function__). Some parts of the documentation will refer to other Boost libraries, as needed.
|
||||
|
||||
If this is your first experience with the library, it is recommended to read the [link log.design Design overview] section for a first glance at the library's capabilities and architecture. The [link log.installation Installation] and [link log.tutorial Tutorial] sections will help to get started experimenting with the library. The tutorial gives an overview of the library features with sample code snippets. Some tutorial steps are presented in two forms: simple and advanced. The simple form typically describes the most common and easy way to do the task and it is being recommended to be read by new users. The advanced form usually gives an expanded way to do the same thing but with an in-depth explanation and the ability to do some extra customization. This form may come in handy for more experienced users and should generally be read if the easy way does not satisfy your needs.
|
||||
|
||||
Besides the tutorial there is a [link log.detailed Detailed features description] chapter. This part describes other tools provided by the library that were not covered by the tutorial. This chapter is best read on a case by case basis.
|
||||
|
||||
Last, but not least, there is a reference which gives the formal description of library components.
|
||||
|
||||
To keep the code snippets in this documentation simple, the following namespace aliases are assumed to be defined:
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:installation Installation and compatibility]
|
||||
|
||||
[section:supported_compilers Supported compilers and platforms]
|
||||
|
||||
The library should build and work with a reasonably compliant compiler. The library was successfully built and tested on the following platforms:
|
||||
|
||||
* Windows XP, Windows Vista, Windows 7. MSVC 9.0 and newer.
|
||||
* Linux. GCC 4.5 and newer. Older versions may work too, but it was not tested.
|
||||
* Linux. Intel C++ 13.1.0.146 Build 20130121.
|
||||
* Linux. Clang 3.2.
|
||||
|
||||
The following compilers/platforms are not supported and will likely fail to compile the library:
|
||||
|
||||
* C++11 compilers with non-C++11 standard libraries (like Clang with libstdc++ from GCC 4.2). Please, use a C++11 standard library in C++11 mode.
|
||||
* MSVC 8.0 and older.
|
||||
* GCC 4.0 and older.
|
||||
* Borland C++ 5.5.1 (free version). Newer versions might or might not work.
|
||||
* Windows 9x, ME, NT4 and older are not supported.
|
||||
|
||||
[heading Notes for MinGW, Cygwin and Visual Studio Express Edition users]
|
||||
|
||||
In order to compile the library under these compilers special preparations are needed. First, in case of MinGW or Cygwin make sure you have installed the latest GCC version. The library will most likely fail to compile with GCC 3.x.
|
||||
|
||||
Second, at some point the library will require a Message Compiler tool (`mc.exe`), which is not available in MinGW, Cygwin and MSVC Express Edition. You have three options to settle the problem. The recommended solution is to obtain the original `mc.exe`. This tool is available in Windows SDK, which can be downloaded from the Microsoft site freely (for example, [@http://www.microsoft.com/downloads/details.aspx?FamilyID=71deb800-c591-4f97-a900-bea146e4fae1&displaylang=en here]). Also, this tool should be available in Visual Studio 2010 Express Edition. During the compilation, `mc.exe` should be accessible through one of the directories in your `PATH` environment variable.
|
||||
|
||||
Another way is to attempt to use the `windmc.exe` tool distributed with MinGW and Cygwin, which is the analogue of the original `mc.exe`. In order to do that you will have to patch Boost.Build files (in particular, the `tools/build/v2/tools/mc.jam` file) as described in [@https://svn.boost.org/trac/boost/ticket/4111 this] ticket. After that you will be able to specify the `mc-compiler=windmc` option to bjam to build the library.
|
||||
|
||||
Lastly, you can disable support for event log backend by defining the `BOOST_LOG_WITHOUT_EVENT_LOG` configuration macro when building the library. This will remove the need for the message compiler. See [link log.installation.config this section] for more configuration options.
|
||||
|
||||
[heading Additional notes for Cygwin users]
|
||||
|
||||
Cygwin support is rather preliminary. Some functionality is not available. In particular, the socket-based syslog backend is not supported, as it is based on __boost_asio__, which doesn't compile on this platform. However, the native syslog support is still in place.
|
||||
|
||||
Furthermore, in order to compile the library the following parameters should be explicitly specified in the bjam command line: `target-os=cygwin logapi=unix`.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:config Configuring and building the library]
|
||||
|
||||
The library has a separately compiled part which should be built as described in the [@http://www.boost.org/doc/libs/release/more/getting_started/ Getting Started guide]. One thing should be noted, though. If your application consists of more than one module (e.g. an exe and one or several dll's) that use Boost.Log, the library _must_ be built as a shared object. If you have a single executable or a single module that works with Boost.Log, you may build the library as a static library.
|
||||
|
||||
The library supports a number of configuration macros:
|
||||
|
||||
[table Configuration macros
|
||||
[[Macro name] [Effect]]
|
||||
[[`BOOST_LOG_DYN_LINK`] [If defined in user code, the library will assume the binary is built as a dynamically loaded library ("dll" or "so"). Otherwise it is assumed that the library is built in static mode. This macro must be either defined or not defined for all translation units of user application that uses logging. This macro can help with auto-linking on platforms that support it.]]
|
||||
[[`BOOST_ALL_DYN_LINK`] [Same as `BOOST_LOG_DYN_LINK` but also affects other Boost libraries the same way.]]
|
||||
[[`BOOST_LOG_NO_THREADS`] [If defined, disables multithreading support. Affects the compilation of both the library and users' code. The macro is automatically defined if no threading support is detected.]]
|
||||
[[`BOOST_LOG_WITHOUT_CHAR`] [If defined, disables support for narrow character logging. Affects the compilation of both the library and users' code.]]
|
||||
[[`BOOST_LOG_WITHOUT_WCHAR_T`] [If defined, disables support for wide character logging. Affects the compilation of both the library and users' code.]]
|
||||
[[`BOOST_LOG_NO_QUERY_PERFORMANCE_COUNTER`] [This macro is only useful on Windows. It affects the compilation of both the library and users' code. If defined, disables support for the `QueryPerformanceCounter` API in the `timer` attribute. This will result in significantly less accurate time readings. The macro is intended to solve possible problems with earlier revisions of AMD Athlon CPU, described [@http://support.microsoft.com/?scid=kb;en-us;895980 here] and [@http://support.microsoft.com/?id=896256 here]. There are also known chipset hardware failures that may prevent this API from functioning properly (see [@http://support.microsoft.com/kb/274323 here]).]]
|
||||
[[`BOOST_LOG_USE_NATIVE_SYSLOG`] [Affects only the compilation of the library. If for some reason support for the native SysLog API is not detected automatically, define this macro to forcibly enable it]]
|
||||
[[`BOOST_LOG_WITHOUT_SETTINGS_PARSERS`] [Affects only the compilation of the library. If defined, none of the facilities related to the parsers for settings will be built. This can substantially reduce the binary size.]]
|
||||
[[`BOOST_LOG_WITHOUT_DEBUG_OUTPUT`] [Affects only the compilation of the library. If defined, the support for debugger output on Windows will not be built.]]
|
||||
[[`BOOST_LOG_WITHOUT_EVENT_LOG`] [Affects only the compilation of the library. If defined, the support for Windows event log will not be built. Defining the macro also makes Message Compiler toolset unnecessary.]]
|
||||
[[`BOOST_LOG_WITHOUT_SYSLOG`] [Affects only the compilation of the library. If defined, the support for syslog backend will not be built.]]
|
||||
[[`BOOST_LOG_NO_SHORTHAND_NAMES`] [Affects only the compilation of users' code. If defined, some deprecated shorthand macro names will not be available.]]
|
||||
[[`BOOST_LOG_USE_WINNT6_API`] [Affects the compilation of both the library and users' code. This macro is Windows-specific. If defined, the library makes use of the Windows NT 6 (Vista, Server 2008) and later APIs to generate more efficient code. This macro will also enable some experimental features of the library. Note, however, that the resulting binary will not run on Windows prior to NT 6. In order to use this feature Platform SDK 6.0 or later is required.]]
|
||||
[[`BOOST_LOG_USE_COMPILER_TLS`] [Affects only the compilation of the library. This macro enables support for compiler intrinsics for thread-local storage. Defining it may improve performance of Boost.Log if certain usage limitations are acceptable. See below for more comments.]]
|
||||
]
|
||||
|
||||
You can define configuration macros in the `bjam` command line, like this:
|
||||
|
||||
[pre
|
||||
bjam --with-log variant=release define=BOOST_LOG_WITHOUT_EVENT_LOG define=BOOST_LOG_USE_WINNT6_API stage
|
||||
]
|
||||
|
||||
However, it may be more convenient to define configuration macros in the "boost/config/user.hpp" file in order to automatically define them both for the library and user's projects. If none of the options are specified, the library will try to support the most comprehensive setup, including support for all character types and features available for the target platform.
|
||||
|
||||
The logging library uses several other Boost libraries that require building too. These are __boost_filesystem__, __boost_system__, __boost_date_time__ and __boost_thread__. Refer to their documentation for detailed instructions on the building procedure.
|
||||
|
||||
One final thing should be added. The library requires run-time type information (RTTI) to be enabled for both the library compilation and user's code compilation. Normally, this won't need anything from you except to verify that RTTI support is not disabled in your project.
|
||||
|
||||
[heading Notes about compiler-supplied intrinsics for TLS]
|
||||
|
||||
Many widely used compilers support builtin intrinsics for managing thread-local storage, which is used in several parts of the library. This feature is also included in the C++11 standard. Generally, these intrinsics allow for a much more efficient access to the storage than any surrogate implementation, be that __boost_thread__ or even native operating system API. However, this feature has several caveats:
|
||||
|
||||
* Some operating systems don't support the use of these intrinsics in case if the TLS is defined in a shared library that is dynamically loaded during the application run time. These systems include Linux and Windows prior to Vista. Windows Vista and later do not have this issue.
|
||||
* The TLS may not be reliably accessed from global constructors and destructors. At least MSVC 8.0 on Windows is known to have this problem.
|
||||
|
||||
The library provides the `BOOST_LOG_USE_COMPILER_TLS` configuration macro that allows to enable the use of this feature, which will improve the library performance at the cost of these limitations:
|
||||
|
||||
* The application executable must be linked with the Boost.Log library. It should not be loaded dynamically during run time.
|
||||
* The application must not use logging in global constructors or destructors.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:defs Definitions]
|
||||
|
||||
Here are definitions of some terms that will be used widely throughout the documentation:
|
||||
|
||||
[variablelist
|
||||
[[Log record][A single bundle of information, collected from the user's application, that is a candidate to be put into the log. In a simple case the log record will be represented as a line of text in the log file after being processed by the logging library.]]
|
||||
[[Attribute][An "attribute" is a piece of meta-information that can be used to specialize a log record. In Boost.Log attributes are represented by function objects with a specific interface, which return the actual attribute value when invoked.]]
|
||||
[[Attribute value][Attribute values are the actual data acquired from attributes. This data is attached to the specific log record and processed by the library. Values can have different types (integers, strings and more complex, including user defined types). Some examples of attribute values: current time stamp value, file name, line number, current scope name, etc.. Attribute values are enveloped in a type erasing wrapper, so the actual type of the attribute is not visible in the interface. The actual (erased) type of the value is sometimes called the stored type.]]
|
||||
[[(Attribute) value visitation][A way of processing the attribute value. This approach involves a function object (a visitor) which is applied to the attribute value. The visitor should know the stored type of the attribute value in order to process it.]]
|
||||
[[(Attribute) value extraction][A way of processing the attribute value when the caller attempts to obtain a reference to the stored value. The caller should know the stored type of the attribute value in order to be able to extract it.]]
|
||||
[[Log sink][A target, to which all log records are fed after being collected from the user's application. The sink defines where and how the log records are going to be stored or processed.]]
|
||||
[[Log source][An entry point for the user's application to put log records to. In a simple case it is an object (logger) which maintains a set of attributes that will be used to form a log record upon the user's request. However, one can surely create a source that would emit log records on some side events (for example, by intercepting and parsing console output of another application).]]
|
||||
[[Log filter][A predicate that takes a log record and tells whether this record should be passed through or discarded. The predicate typically forms its decision based on the attribute values attached to the record.]]
|
||||
[[Log formatter][A function object that generates the final textual output from a log record. Some sinks, e.g. a binary logging sink, may not need it, although almost any text-based sink would use a formatter to compose its output.]]
|
||||
[[Logging core][The global entity that maintains connections between sources and sinks and applies filters to records. It is mainly used when the logging library is initialized.]]
|
||||
[[i18n][Internationalization. The ability to manipulate wide characters.]]
|
||||
[[TLS][Thread-local storage. The concept of having a variable that has independent values for each thread that attempts to access it.]]
|
||||
[[RTTI][Run-time type information. This is the C++ language support data structures required for `dynamic_cast` and `typeid` operators to function properly.]]
|
||||
]
|
||||
|
||||
[endsect]
|
||||
|
||||
[include:log design.qbk]
|
||||
|
||||
[include:log tutorial.qbk]
|
||||
|
||||
[section:detailed Detailed features description]
|
||||
|
||||
[include:log core.qbk]
|
||||
[include:log sources.qbk]
|
||||
[include:log sink_frontends.qbk]
|
||||
[include:log sink_backends.qbk]
|
||||
[include:log expressions.qbk]
|
||||
[include:log attributes.qbk]
|
||||
[include:log utilities.qbk]
|
||||
|
||||
[endsect]
|
||||
|
||||
[include:log extension.qbk]
|
||||
[include:log rationale.qbk]
|
||||
|
||||
[section:reference Reference]
|
||||
|
||||
[xinclude top_level_reference.xml]
|
||||
[xinclude core_reference.xml]
|
||||
[xinclude attributes_reference.xml]
|
||||
[xinclude expressions_reference.xml]
|
||||
[xinclude sources_reference.xml]
|
||||
[xinclude sinks_reference.xml]
|
||||
[xinclude utility_reference.xml]
|
||||
[xinclude support_reference.xml]
|
||||
|
||||
[endsect]
|
||||
|
||||
[include:log changelog.qbk]
|
||||
[include:log todo.qbk]
|
||||
|
||||
[section:acknowledgments Acknowledgments]
|
||||
|
||||
* Vladimir Prus managed the library review in Boost and actually reviewed it in the process.
|
||||
* Luca Rigini wrote the initial implementation of the NT event log sink and made a lot of suggestions on how to improve the library with regard to writing user-defined sinks.
|
||||
* Jean-Daniel Michaud, Michael Lacher and all others who took part in the discussion of the requirements to the library on [@http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost.Logging Wiki].
|
||||
* John Torjo, Gennadiy Rozental and others for their discussion on John's logging library on the Boost developers list. It helped a lot to learn the requirements and possible solutions for the library.
|
||||
* All authors of the great Boost libraries that were involved in this library (notably, __boost_smart_ptr__, __boost_thread__, __boost_date_time__, __boost_filesystem__, __boost_intrusive__, __boost_spirit2__ and others) and __boost_quickbook__ authors for a simple yet powerful documenting tool.
|
||||
* All the reviewers and the users who made suggestions and offered their feedback on the library. Most notably, Steven Watanabe for his in-depth studying the docs and the code, with a lot of fruitful comments on both.
|
||||
|
||||
[endsect]
|
188
doc/rationale.qbk
Normal file
188
doc/rationale.qbk
Normal file
@ -0,0 +1,188 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:rationale Rationale and FAQ]
|
||||
|
||||
[section:why_str_lit Why string literals as scope names?]
|
||||
|
||||
One may wonder why not allow arbitrary strings to be used as named scope names. The answer is simple: for performance and safety reasons. Named scope support functionality has one significant difference from other attribute-related features of the library. The scope stack is maintained even when no logging is done, so if a function `foo` has a `BOOST_LOG_FUNCTION()` statement in its body, it is always a slowdown. Allowing the scope name to be an arbitrary string would make the slowdown significantly greater because of the need to allocate memory and copy the string (not to mention that there would be a need to previously format it, which also takes its toll).
|
||||
|
||||
Dynamic memory allocation also introduces exception safety issues: the `BOOST_LOG_FUNCTION()` statement (and alikes) would become a potential source of exceptions. These issues would complicate user's code if he wants to solve memory allocation problems gracefully.
|
||||
|
||||
One possible alternative solution would be pooling pre-formatted and pre-allocated scope names somewhere but this would surely degrade performance even more and introduce the problem of detecting when to update or free pooled strings.
|
||||
|
||||
Therefore restricting to string literals seemed to be the optimal decision, which reduced dynamic memory usage and provided enough flexibility for common needs.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:why_weak_scoped_attributes Why scoped attributes don't override existing attributes?]
|
||||
|
||||
Initially scoped attributes were able to override other attributes with the same name if they were already registered by the time when a scoped attribute encountered. This allowed some interesting use cases like this:
|
||||
|
||||
BOOST_LOG_DECLARE_GLOBAL_LOGGER(my_logger, src::logger_mt)
|
||||
|
||||
void foo()
|
||||
{
|
||||
// This scoped attribute would temporarily replace the existing tag
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("Section", std::string, "In foo");
|
||||
|
||||
// This log record will have a "Section" attribute with value "In foo"
|
||||
BOOST_LOG(get_my_logger()) << "We're in foo section";
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("Section", std::string, "In main");
|
||||
|
||||
// This log record will have a "Section" attribute with value "In main"
|
||||
BOOST_LOG(get_my_logger()) << "We're in main section";
|
||||
|
||||
foo();
|
||||
|
||||
// This log record will have a "Section" attribute with value "In main" again
|
||||
BOOST_LOG(get_my_logger()) << "We're in main section again";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
However, this feature introduced a number of safety problems, including thread safety issues, that could be difficult to track down. For example, it was no longer safe to use logger-wide scoped attributes on the same logger from different threads, because the resulting attribute would be undefined:
|
||||
|
||||
BOOST_LOG_DECLARE_GLOBAL_LOGGER(my_logger, src::logger_mt)
|
||||
|
||||
void thread1()
|
||||
{
|
||||
BOOST_LOG_SCOPED_LOGGER_TAG(get_my_logger(), "Tag", std::string, "thread1");
|
||||
BOOST_LOG(get_my_logger()) << "We're in thread1";
|
||||
}
|
||||
|
||||
void thread2()
|
||||
{
|
||||
BOOST_LOG_SCOPED_LOGGER_TAG(get_my_logger(), "Tag", int, 10);
|
||||
BOOST_LOG(get_my_logger()) << "We're in thread2";
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
BOOST_LOG_SCOPED_LOGGER_TAG(get_my_logger(), "Tag", double, -2.2);
|
||||
|
||||
BOOST_LOG(get_my_logger()) << "We're in main";
|
||||
|
||||
boost::thread t1(&thread1);
|
||||
boost::thread t2(&thread2);
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
// Which "Tag" is registered here?
|
||||
BOOST_LOG(get_my_logger()) << "We're in main again";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
There were other issues, like having an attribute set iterator that points to one attribute object, then suddenly without seemingly modifying it it becomes pointing to a different attribute object (of, possibly, a different type). Such behavior could lead to tricky failures that would be difficult to investigate. Therefore this feature was eventually dropped, which simplified the scoped attributes implementation significantly.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:why_weak_record_ordering Why log records are weakly ordered in a multithreaded application?]
|
||||
|
||||
Although the library guarantees that log records made in a given thread are always delivered to sinks in the same order as they were made in, the library cannot provide such guarantee for different threads. For instance, it is possible that thread A emits a log record and gets preempted, then thread B emits its log record and manages to deliver it to a sink before being preempted. The resulting log will contain log record from thread B before the record made in thread A. However, attribute values attached to the records will always be actual with regard to the moment of emitting the record and not the moment of passing the record to the sink. This is the reason for a strange, at first glance, situation when a log record with an earlier time stamp follows a record with a later time stamp. The problem appears quite rarely, usually when thread contention on logging is very high.
|
||||
|
||||
There are few possible ways to cope with the problem:
|
||||
|
||||
* Enforce strict serialization of log record being made throughout the application. This solution implies a severe performance impact in multithreaded applications because log records that otherwise could be processed concurrently would have to go serial. Since this controverses one of the [link log.moti main library goals], it was rejected.
|
||||
* Attempt to maintain log record ordering on the sink level. This solution is more or less viable. On the downside, it would introduce log record buffering, which in turn would compromise logs reliability. In the case of application crash all buffered records would be lost.
|
||||
* Bear with the problem and let mis-ordered records appear in log files occasionally. Order log records upon reading the files, if needed.
|
||||
|
||||
The second solution was implemented as a special policy for the [link log.detailed.sink_frontends.async asynchronous sink frontend].
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:why_attribute_manips_dont_affect_filters Why attributes set with stream manipulators do not participate in filtering?]
|
||||
|
||||
One can add attributes to log records in the followinf way:
|
||||
|
||||
BOOST_LOG(logger) << logging::add_value("MyInt", 10) << logging::add_value("MyString", "string attribute value")
|
||||
<< "Some log message";
|
||||
|
||||
However, filters will not be able to use MyInt and MyString attributes. The reason for this behavior is quite simple. The streaming expression is executed /after/ the filtering takes place and only /if/ the filter passed the log record. At this point these attributes have not been added to the record yet. The easiest way to pass attributes to the filter is to use scoped attributes or tags (see [link log.detailed.attributes.related_components.scoped_attributes here]).
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:why_not_lazy_streaming Why not using lazy streaming?]
|
||||
|
||||
One of the possible library implementations would be using lazy expressions to delay log record formatting. In essence, the expression:
|
||||
|
||||
logger << "Hello, world!";
|
||||
|
||||
would become a lambda-expression that is only invoked if the filtering is successful. Although this approach has advantages, it must be noted that lazy expression construction is not zero-cost in terms of performance, code size and compile times. The following expression:
|
||||
|
||||
logger << "Received packet from " << ip << " of " << packet.size() << " bytes";
|
||||
|
||||
would generate a considerable amount of code (proportional to the number of streaming operators) to be executed before filtering takes place. Another drawback is that the `packet.size()` is always called, whether or not the record is actually written to the log. In order to delay this call, yet more scaffolding is needed, possibly involving __boost_bind__, __boost_lambda__ or __boost_phoenix__. This complication is not acceptable for such a basic use case, like this.
|
||||
|
||||
Although lazy streaming is not provided by the library out of the box, nothing prevents developing it in a separate hierarchy of loggers. See the [link log.extension.sources Extending the library] section for more information.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:why_not_log4j Why not using hierarchy of loggers, like in log4j? Why not Boost.Log4j? Etc.]
|
||||
|
||||
There are enough [@http://logging.apache.org/log4j/ log4j]-like libraries available for C++ already (see [@http://logging.apache.org/log4cxx/ here], [@http://log4cplus.sourceforge.net/ here] and [@http://log4cpp.sourceforge.net/ here]), so there is no point in implementing yet another one. Instead, this library was aimed to solve more complex tasks, including ones that do not directly fall under the common definition of "logging" term as a debugging tool. Additionally, as Boost.Log was to be a generic library, it had to provide more ways of extending itself, while keeping performance as high as possible. Log4j concept seemed too limiting and inappropriate for these tasks and therefore was rejected.
|
||||
|
||||
As for hierarchical loggers, there is no need for this feature in the current library design. One of the main benefits it provides in log4j is determining the appenders (sinks, in terms of this library) in which a log record will end up. This library achieves the same result by filtering. The other application of this feature in Boost.Log could be that the loggers in the hierarchy could combine their sets of attributes for each log record, but there was no demand in real world applications for such a feature. It can be added though, if it proves useful.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:fork_support Does Boost.Log support process forking?]
|
||||
|
||||
No, currently Boost.Log does not support process forking (i.e. `fork` call in UNIX systems). There are several issues with process forking, for instance:
|
||||
|
||||
* File sinks do not attempt to reopen log files or synchronize access to files between parent and child processes. The resulting output may be garbled.
|
||||
* File collectors do not expect several processes attempting to collect log files to the same target directory. This may result in spurious failures at log file rotation.
|
||||
* The [link log.detailed.attributes.process_id current_process_id] attribute value will not update in the child process.
|
||||
* In multithreaded applications, one can generally not guarantee that a thread is not executing some Boost.Log code while an other thread forks. Some Boost.Log resources may be left irreversibly locked or broken in the forked process. This reservation is not specific to Boost.Log, other libraries and even the application itself are susceptible to this problem.
|
||||
|
||||
There may be other issues as well. It seems unlikely that support for forking will be added to Boost.Log any time soon.
|
||||
|
||||
[note This does not preclude the `fork`+`exec` sequence from working. As long as the forked process doesn't try to use any of Boost.Log code, the process should be able to call `exec` or a similar function to load and start another executable.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:init_term_support Does Boost.Log support logging at process initialization and termination?]
|
||||
|
||||
It should be fine to use logging during the application initialization (i.e. before `main()` starts). But there are a number of known problems with Boost.Log that prevent it from being used at process termination (i.e. after the `main()` function returns), so the official answer to the second part is no. It may work though, in some very restricted setups, if several rules are followed:
|
||||
|
||||
* Do not create any objects at process termination, including loggers, attributes or sinks. Try to create and cache the required objects as soon as the application starts (maybe even before `main()` starts).
|
||||
* Do not use global loggers at process termination.
|
||||
* Do not call `logging::core::get()` at process termination. Get that pointer as early as possible and keep it until the process terminates.
|
||||
* Do not use named scopes in termination code.
|
||||
|
||||
These rules don't guarantee that the library will work in termination context but they may help to avoid problems. The library will get improved to support this use case better.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:namespace_mangling Why my application fails to link with Boost.Log? What's the fuss about library namespaces?]
|
||||
|
||||
The library declares the `boost::log` namespace which should be used in client code to access library components. However, internally the library uses another nested namespace for actual implementation. The namespace name is configuration and platform dependent, it can change between different releases of the library, so it should never be used in the user side code. This is done in order to make the library configuration synchronized with the application as much as possible and eliminate problems caused by configuration mismatch.
|
||||
|
||||
Most of the time users won't even notice the existence of this internal namespace, but it often appears in compiler and linker errors and in some cases it is useful to know how to decode its name. Currently, the namespace name is composed from the following elements:
|
||||
|
||||
[pre <version><linkage>\_<threading>\_<system>]
|
||||
|
||||
* The `<version>` component describes the library major version. It is currently `v2`.
|
||||
* The `<linkage>` component tells whether the library is linked statically or dynamically. It is `s` if the library is linked statically and empty otherwise.
|
||||
* The `<threading>` component is `st` for single-threaded builds and `mt` for multi-threaded ones.
|
||||
* The `<system>` component describes the underlying OS API used by the library. Currently, it is only specified for multi-threaded builds. Depending on the target platform and configuration, it can be `posix`, `nt5` or `nt6`.
|
||||
|
||||
As a couple quick examples, `v2s_st` corresponds to v2 static single-threaded build of the library and `v2_mt_posix` - to v2 dynamic multi-threaded build for POSIX system API.
|
||||
|
||||
Namespace mangling may lead to linkinkg errors if the application is misconfigured. One common mistake is to build dynamic version of the library and not define `BOOST_LOG_DYN_LINK` or `BOOST_ALL_DYN_LINK` when building the application, so that the library assumes static linking by default. Whenever such linking errors appear, one can decode the namespace name in the missing symbols and the exported symbols of Boost.Log library and adjust library or application [link log.installation.config configuration] accordingly.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
320
doc/sink_backends.qbk
Normal file
320
doc/sink_backends.qbk
Normal file
@ -0,0 +1,320 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:sink_backends Sink backends]
|
||||
|
||||
[section:text_ostream Text stream backend]
|
||||
|
||||
#include <``[boost_log_sinks_text_ostream_backend_hpp]``>
|
||||
|
||||
The text output stream sink backend is the most generic backend provided by the library out of the box. The backend is implemented in the [class_sinks_basic_text_ostream_backend] class template (`text_ostream_backend` and `wtext_ostream_backend` convenience typedefs provided for narrow and wide character support). It supports formatting log records into strings and putting into one or several streams. Each attached stream gets the same result of formatting, so if you need to format log records differently for different streams, you will need to create several sinks - each with its own formatter.
|
||||
|
||||
The backend also provides a feature that may come useful when debugging your application. With the `auto_flush` method one can tell the sink to automatically flush the buffers of all attached streams after each log record is written. This will, of course, degrade logging performance, but in case of an application crash there is a good chance that last log records will not be lost.
|
||||
|
||||
[example_sinks_ostream]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:text_file Text file backend]
|
||||
|
||||
#include <``[boost_log_sinks_text_file_backend_hpp]``>
|
||||
|
||||
Although it is possible to write logs into files with the [link log.detailed.sink_backends.text_ostream text stream backend] the library also offers a special sink backend with an extended set of features suitable for file-based logging. The features include:
|
||||
|
||||
* Log file rotation based on file size and/or time
|
||||
* Flexible log file naming
|
||||
* Placing the rotated files into a special location in the file system
|
||||
* Deleting the oldest files in order to free more space on the file system
|
||||
|
||||
The backend is called [class_sinks_text_file_backend].
|
||||
|
||||
[heading File rotation]
|
||||
|
||||
File rotation is implemented by the sink backend itself. The file name pattern and rotation thresholds can be specified when the [class_sinks_text_file_backend] backend is constructed.
|
||||
|
||||
[example_sinks_file]
|
||||
|
||||
[note The file size at rotation can be imprecise. The implementation counts the number of characters written to the file, but the underlying API can introduce additional auxiliary data, which would increase the log file's actual size on disk. For instance, it is well known that Windows and DOS operating systems have a special treatment with regard to new-line characters. Each new-line character is written as a two byte sequence 0x0D 0x0A instead of a single 0x0A. Other platform-specific character translations are also known.]
|
||||
|
||||
The time-based rotation is not limited by only time points. There are following options available out of the box:
|
||||
|
||||
# Time point rotations: [class_file_rotation_at_time_point] class. This kind of rotation takes place whenever the specified time point is reached. The following variants are available:
|
||||
|
||||
* Every day rotation, at the specified time. This is what was presented in the code snippet above:
|
||||
``
|
||||
sinks::file::rotation_at_time_point(12, 0, 0)
|
||||
``
|
||||
|
||||
* Rotation on the specified day of every week, at the specified time. For instance, this will make file rotation to happen every Tuesday, at midnight:
|
||||
``
|
||||
sinks::file::rotation_at_time_point(date_time::Tuesday, 0, 0, 0)
|
||||
``
|
||||
in case of midnight, the time can be omitted:
|
||||
``
|
||||
sinks::file::rotation_at_time_point(date_time::Tuesday)
|
||||
``
|
||||
|
||||
* Rotation on the specified day of each month, at the specified time. For example, this is how to rotate files on the 1-st of every month:
|
||||
``
|
||||
sinks::file::rotation_at_time_point(gregorian::greg_day(1), 0, 0, 0)
|
||||
``
|
||||
like with weekdays, midnight is implied:
|
||||
``
|
||||
sinks::file::rotation_at_time_point(gregorian::greg_day(1))
|
||||
``
|
||||
|
||||
# Time interval rotations: [class_file_rotation_at_time_interval] class. With this predicate the rotation is not bound to any time points and happens as soon as the specified time interval since the previous rotation elapses. This is how to make rotations every hour:
|
||||
``
|
||||
sinks::file::rotation_at_time_interval(posix_time::hours(1))
|
||||
``
|
||||
|
||||
If none of the above applies, one can specify his own predicate for time-based rotation. The predicate should take no arguments and return `bool` (the `true` value indicates that the rotation should take place). The predicate will be called for every log record being written to the file.
|
||||
|
||||
bool is_it_time_to_rotate();
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
// ...
|
||||
|
||||
boost::shared_ptr< sinks::text_file_backend > backend =
|
||||
boost::make_shared< sinks::text_file_backend >(
|
||||
keywords::file_name = "file_%5N.log",
|
||||
keywords::time_based_rotation = &is_it_time_to_rotate
|
||||
);
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
[note The log file rotation takes place on an attempt to write a new log record to the file. Thus the time-based rotation is not a strict threshold, either. The rotation will take place as soon as the library detects that the rotation should have happened.]
|
||||
|
||||
The file name pattern may contain a number of wildcards, like the one you can see in the example above. Supported placeholders are:
|
||||
|
||||
* Current date and time components. The placeholders conform to the ones specified by __boost_date_time__ library.
|
||||
* File counter (`%N`) with an optional width specification in the `printf`-like format. The file counter will always be decimal, zero filled to the specified width.
|
||||
* A percent sign (`%%`).
|
||||
|
||||
A few quick examples:
|
||||
|
||||
[table
|
||||
[[Template] [Expands to]]
|
||||
[[file\_%N.log] [file\_1.log, file\_2.log...]]
|
||||
[[file\_%3N.log] [file\_001.log, file\_002.log...]]
|
||||
[[file\_%Y%m%d.log] [file\_20080705.log, file\_20080706.log...]]
|
||||
[[file\_%Y-%m-%d\_%H-%M-%S.%N.log] [file\_2008-07-05\_13-44-23.1.log, file\_2008-07-06\_16-00-10.2.log...]]
|
||||
]
|
||||
|
||||
[important Although all __boost_date_time__ format specifiers will work, there are restrictions on some of them, if you intend to scan for old log files. This functionality is discussed in the next section.]
|
||||
|
||||
The sink backend allows hooking into the file rotation process in order to perform pre- and post-rotation actions. This can be useful to maintain log file validity by writing headers and footers. For example, this is how we could modify the `init_logging` function in order to write logs into XML files:
|
||||
|
||||
[example_sinks_xml_file]
|
||||
|
||||
[@boost:/libs/log/example/doc/sinks_xml_file.cpp See the complete code].
|
||||
|
||||
Finally, the sink backend also supports the auto-flush feature, like the [link log.detailed.sink_backends.text_ostream text stream backend] does.
|
||||
|
||||
[heading Managing rotated files]
|
||||
|
||||
After being closed, the rotated files can be collected. In order to do so one has to set up a file collector by specifying the target directory where to collect the rotated files and, optionally, size thresholds. For example, we can modify the `init_logging` function to place rotated files into a distinct directory and limit total size of the files. Let's assume the following function is called by `init_logging` with the constructed sink:
|
||||
|
||||
[example_sinks_xml_file_collecting]
|
||||
|
||||
The `max_size` and `min_free_space` parameters are optional, the corresponding threshold will not be taken into account if the parameter is not specified.
|
||||
|
||||
One can create multiple file sink backends that collect files into the same target directory. In this case the most strict thresholds are combined for this target directory. The files from this directory will be erased without regard for which sink backend wrote it, i.e. in the strict chronological order.
|
||||
|
||||
[warning The collector does not resolve log file name clashes between different sink backends, so if the clash occurs the behavior is undefined, in general. Depending on the circumstances, the files may overwrite each other or the operation may fail entirely.]
|
||||
|
||||
The file collector provides another useful feature. Suppose you ran your application 5 times and you have 5 log files in the "logs" directory. The file sink backend and file collector provide a `scan_for_files` method that searches the target directory for these files and takes them into account. So, if it comes to deleting files, these files are not forgotten. What's more, if the file name pattern in the backend involves a file counter, scanning for older files allows updating the counter to the most recent value. Here is the final version of our `init_logging` function:
|
||||
|
||||
[example_sinks_xml_file_final]
|
||||
|
||||
There are two methods of file scanning: the scan that involves file name matching with the file name pattern (the default) and the scan that assumes that all files in the target directory are log files. The former applies certain restrictions on the placeholders that can be used within the file name pattern, in particular only file counter placeholder and these placeholders of __boost_date_time__ are supported: `%y`, `%Y`, `%m`, `%d`, `%H`, `%M`, `%S`, `%f`. The latter scanning method, in its turn, has its own drawback: it does not allow updating the file counter in the backend. It is also considered to be more dangerous as it may result in unintended file deletion, so be cautious. The all-files scanning method can be enabled by passing it as an additional parameter to the `scan_for_files` call:
|
||||
|
||||
// Look for all files in the target directory
|
||||
backend->scan_for_files(sinks::file::scan_all);
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:text_multifile Text multi-file backend]
|
||||
|
||||
#include <``[boost_log_sinks_text_multifile_backend_hpp]``>
|
||||
|
||||
While the text stream and file backends are aimed to store all log records into a single file/stream, this backend serves a different purpose. Assume we have a banking request processing application and we want logs related to every single request to be placed into a separate file. If we can associate some attribute with the request identity then the [class_sinks_text_multifile_backend] backend is the way to go.
|
||||
|
||||
[example_sinks_multifile]
|
||||
|
||||
You can see we used a regular [link log.detailed.expressions.formatters formatter] in order to specify file naming pattern. Now, every log record with a distinct value of the "RequestID" attribute will be stored in a separate file, no matter how many different requests are being processed by the application concurrently. You can also find the [@boost:/libs/log/example/multiple_files/main.cpp `multiple_files`] example in the library distribution, which shows a similar technique to separate logs generated by different threads of the application.
|
||||
|
||||
If using formatters is not appropriate for some reason, you can provide your own file name composer. The composer is a mere function object that accepts a log record as a single argument and returns a value of the `text_multifile_backend::path_type` type.
|
||||
|
||||
[note The multi-file backend has no knowledge of whether a particular file is going to be used or not. That is, if a log record has been written into file A, the library cannot tell whether there will be more records that fit into the file A or not. This makes it impossible to implement file rotation and removing unused files to free space on the file system. The user will have to implement such functionality himself.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:syslog Syslog backend]
|
||||
|
||||
#include <``[boost_log_sinks_syslog_backend_hpp]``>
|
||||
|
||||
The syslog backend, as comes from its name, provides support for the syslog API that is available on virtually any UNIX-like platform. On Windows there exists at least [@http://syslog-win32.sourceforge.net one] public implementation of the syslog client API. However, in order to provide maximum flexibibity and better portability the library offers built-in support for the syslog protocol described in [@http://tools.ietf.org/html/rfc3164 RFC 3164]. Thus on Windows only the built-in implementation is supported, while on UNIX-like systems both built-in and system API based implementations are supported.
|
||||
|
||||
The backend is implemented in the [class_sinks_syslog_backend] class. The backend supports formatting log records, and therefore requires thread synchronization in the frontend. The backend also supports severity level translation from the application-specific values to the syslog-defined values. This is achieved with an additional function object, level mapper, that receives a set of attribute values of each log record and returns the appropriate syslog level value. This value is used by the backend to construct the final priority value of the syslog record. The other component of the syslog priority value, the facility, is constant for each backend object and can be specified in the backend constructor arguments.
|
||||
|
||||
Level mappers can be written by library users to translate the application log levels to the syslog levels in the best way. However, the library provides two mappers that would fit this need in obvious cases. The [class_syslog_direct_severity_mapping] class template provides a way to directly map values of some integral attribute to syslog levels, without any value conversion. The [class_syslog_custom_severity_mapping] class template adds some flexibility and allows to map arbitrary values of some attribute to syslog levels.
|
||||
|
||||
Anyway, one example is better than a thousand words.
|
||||
|
||||
[example_sinks_syslog]
|
||||
|
||||
Please note that all syslog constants, as well as level extractors, are declared within a nested namespace `syslog`. The library will not accept (and does not declare in the backend interface) native syslog constants, which are macros, actually.
|
||||
|
||||
Also note that the backend will default to the built-in implementation and `user` logging facility, if the corresponding constructor parameters are not specified.
|
||||
|
||||
[tip The `set_target_address` method will also accept DNS names, which it will resolve to the actual IP address. This featue, however, is not available in single threaded builds.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:debugger Windows debugger output backend]
|
||||
|
||||
#include <``[boost_log_sinks_debug_output_backend_hpp]``>
|
||||
|
||||
Windows API has an interesting feature: a process, being run under a debugger, is able to emit messages that will be intercepted and displayed in the debugger window. For example, if an application is run under the Visual Studio IDE it is able to write debug messages to the IDE window. The [class_sinks_basic_debug_output_backend] backend provides a simple way of emitting such messages. Additionally, in order to optimize application performance, a [link log.detailed.expressions.predicates.is_debugger_present special filter] is available that checks whether the application is being run under a debugger. Like many other sink backends, this backend also supports setting a formatter in order to compose the message text.
|
||||
|
||||
The usage is quite simple and straightforward:
|
||||
|
||||
[example_sinks_debugger]
|
||||
|
||||
Note that the sink backend is templated on the character type. This type defines the Windows API version that is used to emit messages. Also, `debug_output_backend` and `wdebug_output_backend` convenience typedefs are provided.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:event_log Windows event log backends]
|
||||
|
||||
#include <``[boost/log/sinks/event_log_backend.hpp]``>
|
||||
|
||||
Windows operating system provides a special API for publishing events related to application execution. A wide range of applications, including Windows components, use this facility to provide the user with all essential information about computer health in a single place - an event log. There can be more than one event log. However, typically all user-space applications use the common Application log. Records from different applications or their parts can be selected from the log by a record source name. Event logs can be read with a standard utility, an Event Viewer, that comes with Windows.
|
||||
|
||||
Although it looks very tempting, the API is quite complicated and intrusive, which makes it difficult to support. The application is required to provide a dynamic library with special resources that describe all events the application supports. This library must be registered in the Windows registry, which pins its location in the file system. The Event Viewer uses this registration to find the resources and compose and display messages. The positive feature of this approach is that since event resources can describe events differently for different languages, it allows the application to support event internationalization in a quite transparent manner: the application simply provides event identifiers and non-localizable event parameters to the API, and it does the rest of the work.
|
||||
|
||||
In order to support both the simplistic approach "it just works" and the more elaborate event composition, including internationalization support, the library provides two sink backends that work with event log API.
|
||||
|
||||
[heading Simple event log backend]
|
||||
|
||||
The [class_sinks_basic_simple_event_log_backend] backend is intended to encapsulate as much of the event log API as possible, leaving interface and usage model very similar to other sink backends. It contains all resources that are needed for the Event Viewer to function properly, and registers the Boost.Log library in the Windows registry in order to populate itself as the container of these resources.
|
||||
|
||||
[important The library must be built as a dynamic library in order to use this backend flawlessly. Otherwise event description resources are not linked into the executable, and the Event Viewer is not able to display events properly.]
|
||||
|
||||
The only thing user has to do to add Windows event log support to his application is to provide event source and log names (which are optional and can be automatically suggested by the library), set up an appropriate filter, formatter and event severity mapping.
|
||||
|
||||
[example_sinks_simple_event_log]
|
||||
|
||||
Having done that, all logging records that pass to the sink will be formatted the same way they are in the other sinks. The formatted message will be displayed in the Event Viewer as the event description.
|
||||
|
||||
[heading Advanced event log backend]
|
||||
|
||||
The [class_sinks_basic_event_log_backend] allows more detailed control over the logging API, but requires considerably more scaffolding during initialization and usage.
|
||||
|
||||
First, the user has to build his own library with the event resources (the process is described in [@http://msdn.microsoft.com/en-us/library/aa363681(VS.85).aspx MSDN]). As a part of this process one has to create a message file that describes all events. For the sake of example, let's assume the following contents were used as the message file:
|
||||
|
||||
[teletype]
|
||||
|
||||
; /* --------------------------------------------------------
|
||||
; HEADER SECTION
|
||||
; */
|
||||
SeverityNames=(Debug=0x0:MY_SEVERITY_DEBUG
|
||||
Info=0x1:MY_SEVERITY_INFO
|
||||
Warning=0x2:MY_SEVERITY_WARNING
|
||||
Error=0x3:MY_SEVERITY_ERROR
|
||||
)
|
||||
|
||||
; /* --------------------------------------------------------
|
||||
; MESSAGE DEFINITION SECTION
|
||||
; */
|
||||
|
||||
MessageIdTypedef=WORD
|
||||
|
||||
MessageId=0x1
|
||||
SymbolicName=MY_CATEGORY_1
|
||||
Language=English
|
||||
Category 1
|
||||
.
|
||||
|
||||
MessageId=0x2
|
||||
SymbolicName=MY_CATEGORY_2
|
||||
Language=English
|
||||
Category 2
|
||||
.
|
||||
|
||||
MessageId=0x3
|
||||
SymbolicName=MY_CATEGORY_3
|
||||
Language=English
|
||||
Category 3
|
||||
.
|
||||
|
||||
MessageIdTypedef=DWORD
|
||||
|
||||
MessageId=0x100
|
||||
Severity=Warning
|
||||
Facility=Application
|
||||
SymbolicName=LOW_DISK_SPACE_MSG
|
||||
Language=English
|
||||
The drive %1 has low free disk space. At least %2 Mb of free space is recommended.
|
||||
.
|
||||
|
||||
MessageId=0x101
|
||||
Severity=Error
|
||||
Facility=Application
|
||||
SymbolicName=DEVICE_INACCESSIBLE_MSG
|
||||
Language=English
|
||||
The drive %1 is not accessible.
|
||||
.
|
||||
|
||||
MessageId=0x102
|
||||
Severity=Info
|
||||
Facility=Application
|
||||
SymbolicName=SUCCEEDED_MSG
|
||||
Language=English
|
||||
Operation finished successfully in %1 seconds.
|
||||
.
|
||||
|
||||
[c++]
|
||||
|
||||
After compiling the resource library, the path to this library must be provided to the sink backend constructor, among other parameters used with the simple backend. The path may contain placeholders that will be expanded with the appropriate environment variables.
|
||||
|
||||
[example_sinks_event_log_create_backend]
|
||||
|
||||
Like the simple backend, [class_sinks_basic_event_log_backend] will register itself in the Windows registry, which will enable the Event Viewer to display the emitted events.
|
||||
|
||||
Next, the user will have to provide the mapping between the application logging attributes and event identifiers. These identifiers were provided in the message compiler output as a result of compiling the message file. One can use [class_event_log_basic_event_composer] and one of the event ID mappings, like in the following example:
|
||||
|
||||
[example_sinks_event_log_event_composer]
|
||||
|
||||
As you can see, one can use regular [link log.detailed.expressions.formatters formatters] to specify which attributes will be inserted instead of placeholders in the final event message. Aside from that, one can specify mappings of attribute values to event types and categories. Suppose our application has the following severity levels:
|
||||
|
||||
[example_sinks_event_log_severity]
|
||||
|
||||
Then these levels can be mapped onto the values in the message description file:
|
||||
|
||||
[example_sinks_event_log_mappings]
|
||||
|
||||
[tip As of Windows NT 6 (Vista, Server 2008) it is not needed to specify event type mappings. This information is available in the message definition resources and need not be duplicated in the API call.]
|
||||
|
||||
Now that initialization is done, the sink can be registered into the core.
|
||||
|
||||
[example_sinks_event_log_register_sink]
|
||||
|
||||
In order to emit events it is convenient to create a set of functions that will accept all needed parameters for the corresponding events and announce that the event has occurred.
|
||||
|
||||
[example_sinks_event_log_facilities]
|
||||
|
||||
Now you are able to call these helper functions to emit events. The complete code from this section is available in the [@boost:/libs/log/example/event_log/main.cpp `event_log`] example in the library distribution.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
157
doc/sink_frontends.qbk
Normal file
157
doc/sink_frontends.qbk
Normal file
@ -0,0 +1,157 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:sink_frontends Sink frontends]
|
||||
|
||||
Sink frontends are the part of sinks provided by the library, that implements the common functionality shared between all sinks. This includes support for filtering, exception handling and thread synchronization. Also, since formatting is typical for text-based sinks, it is implemented by frontends as well. Every sink frontend receives log records from the logging core and then passes them along to the associated sink backend. The frontend does not define how to process records but rather in what way the core should interact with the backend. It is the backend that defines the processing rules of the log records. You probably won't have to write your own frontend when you need to create a new type of sink, because the library provides a number of frontends that cover most use cases.
|
||||
|
||||
Sink frontends derive from the [class_sinks_sink] class template, which is used by the logging core to supply log records. Technically speaking, one can derive his class from the [class_sinks_sink] template and have his new-found sink, but using sink frontends saves from quite an amount of routine work. As every sink frontend is associated with a backend, the corresponding backend will also be constructed by the frontend upon its construction (unless the user provides the backend instance himself), making the sink complete. Therefore, when the frontend is constructed it can be registered in the logging core to begin processing records. See the [link log.detailed.sink_backends Sink Backends] section for more details on interactions between frontends and backends.
|
||||
|
||||
Below is a more detailed overview of the services provided by sink frontends.
|
||||
|
||||
[section:basic_services Basic sink frontend services]
|
||||
|
||||
There are a number of basic functionalities that all sink frontends provide.
|
||||
|
||||
[section:filtering Filtering]
|
||||
|
||||
All sink frontends support filtering. The user can specify a custom filtering function object or a filter constructed with the [link log.detailed.expressions library-provided tools]. The filter can be set with the `set_filter` method or cleared with the `reset_filter` method. The filter is invoked during the call to the `will_consume` method that is issued by the logging core. If the filter is not set, it is assumed that the sink will accept any log record.
|
||||
|
||||
[note Like the logging core, all sink frontends assume it is safe to call filters from multiple threads concurrently. This is fine with the library-provided filters.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:formatting Formatting]
|
||||
|
||||
For text-based sink backends, frontends implement record formatting. Like with filters, [link log.detailed.expressions lambda expressions] can be used to construct formatters. The formatter can be set for a text-based sink by calling the `set_formatter` method or cleared by calling `reset_formatter`.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:exception_handling Exception handling]
|
||||
|
||||
All sink frontends allow setting up exception handlers in order to customize error processing on a per-sink basis. One can install an exception handling function with the `set_exception_handler` method, this function will be called with no arguments from a `catch` block if an exception occurs during record processing in the backend or during the sink-specific filtering. The exception handler is free to rethrow an exception or to suppress it. In the former case the exception is propagated to the core, where another layer of exception handling can come into action.
|
||||
|
||||
[tip [link log.detailed.core.core.exception_handling Logging core] and [link log.detailed.sources.exception_handling loggers] also support installing exception handlers.]
|
||||
|
||||
The library provides a [link log.detailed.utilities.exception_handlers convenient tool] for dispatching exceptions into an unary polymorphic function object.
|
||||
|
||||
[note An exception handler is not allowed to return a value. This means you are not able to alter the filtering result once an exception occurs, and thus filtering will always fail.]
|
||||
|
||||
[note All sink frontends assume it is safe to call exception handlers from multiple threads concurrently. This is fine with the library-provided exception dispatchers.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:unlocked Unlocked sink frontend]
|
||||
|
||||
#include <``[boost_log_sinks_unlocked_frontend.hpp]``>
|
||||
|
||||
The unlocked sink frontend is implemented with the [class_sinks_unlocked_sink] class template. This frontend provides the most basic service for the backend. The [class_sinks_unlocked_sink] frontend performs no thread synchronization when accessing the backend, assuming that synchronization either is not needed or is implemented by the backend. Nevertheless, setting up a filter is still thread-safe (that is, one can safely change the filter in the [class_sinks_unlocked_sink] frontend while other threads are writing logs through this sink). This is the only sink frontend available in a single threaded environment. The example of use is as follows:
|
||||
|
||||
[example_sinks_unlocked]
|
||||
|
||||
[@boost:/libs/log/example/doc/sinks_unlocked.cpp See the complete code].
|
||||
|
||||
All sink backends provided by the library require thread synchronization on the frontend part. If we tried to instantiate the frontend on the backend that requires more strict threading guarantees than what the frontend provides, the code wouldn't have compiled. Therefore this frontend is mostly useful in single-threaded environments and with custom backends.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:sync Synchronous sink frontend]
|
||||
|
||||
#include <``[boost_log_sinks_sync_frontend_hpp]``>
|
||||
|
||||
The synchronous sink frontend is implemented with the [class_sinks_synchronous_sink] class template. It is similar to the [class_sinks_unlocked_sink] but additionally provides thread synchronization with a mutex before passing log records to the backend. All sink backends that support formatting currently require thread synchronization in the frontend.
|
||||
|
||||
The synchronous sink also introduces the ability to acquire a pointer to the locked backend. As long as the pointer exists, the backend is guaranteed not to be accessed from other threads, unless the access is done through another frontend or a direct reference to the backend. This feature can be useful if there is a need to perform some updates on the sink backend while other threads may be writing logs. Beware, though, that while the backend is locked any other thread that tries to write a log record to the sink gets blocked until the backend is released.
|
||||
|
||||
The usage is similar to the [class_sinks_unlocked_sink].
|
||||
|
||||
[example_sinks_sync]
|
||||
|
||||
[@boost:/libs/log/example/doc/sinks_sync.cpp See the complete code].
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:async Asynchronous sink frontend]
|
||||
|
||||
#include <``[boost_log_sinks_async_frontend_hpp]``>
|
||||
|
||||
// Related headers
|
||||
#include <``[boost_log_sinks_unbounded_fifo_queue_hpp]``>
|
||||
#include <``[boost_log_sinks_unbounded_ordering_queue_hpp]``>
|
||||
#include <``[boost_log_sinks_bounded_fifo_queue_hpp]``>
|
||||
#include <``[boost_log_sinks_bounded_ordering_queue_hpp]``>
|
||||
#include <``[boost_log_sinks_drop_on_overflow_hpp]``>
|
||||
#include <``[boost_log_sinks_block_on_overflow_hpp]``>
|
||||
|
||||
The frontend is implemented in the [class_sinks_asynchronous_sink] class template. Like the synchronous one, asynchronous sink frontend provides a way of synchronizing access to the backend. All log records are passed to the backend in a dedicated thread, which makes it suitable for backends that may block for a considerable amount of time (network and other hardware device-related sinks, for example). The internal thread of the frontend is spawned on the frontend constructor and joined on its destructor (which implies that the frontend destruction may block).
|
||||
|
||||
[note The current implementation of the asynchronous sink frontend use record queueing. This introduces a certain latency between the fact of record emission and its actual processing (such as writing into a file). This behavior may be inadequate in some contexts, such as debugging an application that is prone to crashes.]
|
||||
|
||||
[example_sinks_async_init]
|
||||
|
||||
[important If asynchronous logging is used in a multi-module application, one should decide carefully when to unload dynamically loaded modules that write logs. The library has many places where it may end up using resources that reside in the dynamically loaded module. Examples of such resources are virtual tables, string literals and functions. If any of these resources are still used by the library when the module in which they reside gets unloaded, the application will most likely crash. Strictly speaking, this problem exists with any sink type (and is not limited to sinks in the first place), but asynchronous sinks introduce an additional problem. One cannot tell which resources are used by the asynchronous sink because it works in a dedicated thread and uses buffered log records. There is no general solution for this issue. Users are advised to either avoid dynamic module unloading during the application's work time, or to avoid asynchronous logging. As an additional way to cope with the problem, one may try to shutdown all asynchronous sinks before unloading any modules, and after unloading re-create them. However, avoiding dynamic unloading is the only way to solve the problem completely.]
|
||||
|
||||
In order to stop the dedicated thread feeding log records to the backend one can call the `stop` method of the frontend. This method will be called automatically in the frontend destructor. The `stop` method, unlike thread interruption, will only terminate the feeding loop when a log record that is being fed is processed by the backend (i.e. it will not interrupt the record processing that has already started). However, it may happen that some records are still left in the queue after returning from the `stop` method. In order to flush them to the backend an additional call to the `feed_records` method is required. This is useful in the application termination stage.
|
||||
|
||||
[example_sinks_async_stop]
|
||||
|
||||
[@boost:/libs/log/example/doc/sinks_async.cpp See the complete code].
|
||||
|
||||
Spawning the dedicated thread for log record feeding can be suppressed with the optional boolean `start_thread` named parameter of the frontend. In this case the user can select either way of processing records:
|
||||
|
||||
* Call the `run` method of the frontend. This call will block in the feeding loop. This loop can be interrupted with the call to `stop`.
|
||||
* Periodically call `feed_records`. This method will process all the log records that were in the frontend queue when the call was issued and then return.
|
||||
|
||||
[note Users should take care not to mix these two approaches concurrently. Also, none of these methods should be called if the dedicated feeding thread is running (i.e., the `start_thread` was not specified in the construction or had the value of `true`.]
|
||||
|
||||
[heading Customizing record queueing strategy]
|
||||
|
||||
The [class_sinks_asynchronous_sink] class template can be customized with the record queueing strategy. Several strategies are provided by the library:
|
||||
|
||||
* [class_sinks_unbounded_fifo_queue]. This strategy is the default. As the name implies, the queue is not limited in depth and does not order log records.
|
||||
* [class_sinks_unbounded_ordering_queue]. Like [class_sinks_unbounded_fifo_queue], the queue has unlimited depth but it applies an order on the queued records. We will return to ordering queues in a moment.
|
||||
* [class_sinks_bounded_fifo_queue]. The queue has limited depth specified in a template parameter as well as the overflow handling strategy. No record ordering is applied.
|
||||
* [class_sinks_bounded_ordering_queue]. Like [class_sinks_bounded_fifo_queue] but also applies log record ordering.
|
||||
|
||||
[warning Be careful with unbounded queueing strategies. Since the queue has unlimited depth, if log records are continuously generated faster than being processed by the backend the queue grows uncontrollably which manifests itself as a memory leak.]
|
||||
|
||||
Bounded queues support the following overflow strategies:
|
||||
|
||||
* [class_sinks_drop_on_overflow]. When the queue is full, silently drop excessive log records.
|
||||
* [class_sinks_block_on_overflow]. When the queue is full, block the logging thread until the backend feeding thread manages to process some of the queued records.
|
||||
|
||||
For example, this is how we could modify the previous example to limit the record queue to 100 elements:
|
||||
|
||||
[example_sinks_bounded_async_init]
|
||||
|
||||
[@boost:/libs/log/example/doc/sinks_async_bounded.cpp See the complete code].
|
||||
|
||||
Also see the [@boost:/libs/log/example/bounded_async_log/main.cpp `bounded_async_log`] example in the library distribution.
|
||||
|
||||
[heading Ordering log records]
|
||||
|
||||
Record ordering can be useful to alleviate the [link log.rationale.why_weak_record_ordering weak record ordering] issue present in multithreaded applications.
|
||||
|
||||
Ordering queueing strategies introduce a small latency to the record processing. The latency duration and the ordering predicate can be specified on the frontend construction. It may be useful to employ the [link log.detailed.utilities.record_ordering log record ordering tools] to implement ordering predicates.
|
||||
|
||||
[example_sinks_ordering_async_init]
|
||||
|
||||
In the code sample above the sink frontend will keep log records in the internal queue for up to one second and apply ordering based on the log record counter of type `unsigned int`. The `ordering_window` parameter is optional and will default to some reasonably small system-specific value that will suffice to maintain chronological flow of log records to the backend.
|
||||
|
||||
The ordering window is maintained by the frontend even upon stopping the internal feeding loop, so that it would be possible to reenter the loop without breaking the record ordering. On the other hand, in order to ensure that all log records are flushed to the backend one has to call the `flush` method at the end of the application.
|
||||
|
||||
[example_sinks_ordering_async_stop]
|
||||
|
||||
This technique is also demonstrated in the [@boost:/libs/log/example/async_log/main.cpp `async_log`] example in the library distribution.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
172
doc/sources.qbk
Normal file
172
doc/sources.qbk
Normal file
@ -0,0 +1,172 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:sources Logging sources]
|
||||
|
||||
[section:basic_logger Basic loggers]
|
||||
|
||||
#include <``[boost_log_sources_basic_logger_hpp]``>
|
||||
|
||||
The simplest logging sources provided by the library are loggers [class_sources_logger] and its thread-safe version, [class_sources_logger_mt] ([class_sources_wlogger] and [class_sources_wlogger_mt] for wide-character logging, accordingly). These loggers only provide the ability to store source-specific attributes within themselves and, of course, to form log records. This type of logger should probably be used when there is no need for advanced features like severity level checks. It may well be used as a tool to collect application statistics and register application events, such as notifications and alarms. In such cases the logger is normally used in conjunction with [link log.detailed.attributes.related_components.scoped_attributes scoped attributes] to attach the needed data to the notification event. Below is an example of usage:
|
||||
|
||||
[example_sources_network_connection]
|
||||
|
||||
The class `network_connection` in the code snippet above represents an approach to implementing simple logging and statistical information gathering in a network-related application. Each of the presented methods of the class effectively marks a corresponding event that can be tracked and collected on the sinks level. Furthermore, other methods of the class, that are not shown here for simplicity, are able to write logs too. Note that every log record ever made in the connected state of the `network_connection` object will be implicitly marked up with the address of the remote site.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:severity_level_logger Loggers with severity level support]
|
||||
|
||||
#include <``[boost_log_sources_severity_feature_hpp]``>
|
||||
#include <``[boost_log_sources_severity_logger_hpp]``>
|
||||
|
||||
The ability to distinguish some log records from others based on some kind of level of severity or importance is one of the most frequently requested features. The class templates [class_sources_severity_logger] and [class_sources_severity_logger_mt] (along with their [class_sources_wseverity_logger] and [class_sources_wseverity_logger_mt] wide-character counterparts) provide this functionality.
|
||||
|
||||
The loggers automatically register a special source-specific attribute "Severity", which can be set for every record in a compact and efficient manner, with a named argument `severity` that can be passed to the constructor and/or the `open_record` method. If passed to the logger constructor, the `severity` argument sets the default value of the severity level that will be used if none is provided in the `open_record` arguments. The `severity` argument passed to the `open_record` method sets the level of the particular log record being made. The type of the severity level can be provided as a template argument for the logger class template. The default type is `int`.
|
||||
|
||||
The actual values of this attribute and their meaning are entirely user-defined. However, it is recommended to use the level of value equivalent to zero as a base point for other values. This is because the default-constructed logger object sets its default severity level to zero. It is also recommended to define the same levels of severity for the entire application in order to avoid confusion in the written logs later. The following code snippet shows the usage of `severity_logger`.
|
||||
|
||||
[example_sources_severity]
|
||||
[example_sources_default_severity]
|
||||
|
||||
Or, if you prefer logging without macros:
|
||||
|
||||
[example_sources_severity_manual]
|
||||
|
||||
And, of course, severity loggers also provide the same functionality the [link log.detailed.sources.basic_logger basic loggers] do.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:channel_logger Loggers with channel support]
|
||||
|
||||
#include <``[boost_log_sources_channel_feature_hpp]``>
|
||||
#include <``[boost_log_sources_channel_logger_hpp]``>
|
||||
|
||||
Sometimes it is important to associate log records with some application component, such as the module or class name, the relation of the logged information to some specific domain of application functionality (e.g. network or file system related messages) or some arbitrary tag that can be used later to route these records to a specific sink. This feature is fulfilled with loggers [class_sources_channel_logger], [class_sources_channel_logger_mt] and their wide-char counterparts [class_sources_wchannel_logger], [class_sources_wchannel_logger_mt]. These loggers automatically register an attribute named "Channel". The default channel name can be set in the logger constructor with a named argument `channel`. The type of the channel attribute value can be specified as a template argument for the logger, with `std::string` (`std::wstring` in case of wide character loggers) as a default. Aside from that, the usage is similar to the [link log.detailed.sources.basic_logger basic loggers]:
|
||||
|
||||
[example_sources_network_connection_channels]
|
||||
|
||||
It is also possible to set the channel name of individual log records. This can be uesful when a [link log.detailed.sources.global_storage global logger] is used instead of an object-specific one. The channel name can be set by calling the `channel` modifier on the logger or by using a special macro for logging. For example:
|
||||
|
||||
[example_sources_network_connection_dynamic_channels]
|
||||
|
||||
Note that changing the channel name is persistent, so unless the channel name is reset, the subsequent records will also belong to the new channel.
|
||||
|
||||
[tip For performance reasons it is advised to avoid dynamically setting the channel name individually for every log record, when possible. Changing the channel name involves dynamic memory allocation. Using distinct loggers for different channels allows to avoid this overhead.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:exception_handling Loggers with exception handling support]
|
||||
|
||||
#include <``[boost_log_sources_exception_handler_feature_hpp]``>
|
||||
|
||||
The library provides a logger feature that enables the user to handle and/or suppress exceptions at the logger level. The [class_sources_exception_handler] feature adds a `set_exception_handler` method to the logger that allows setting a function object to be called if an exception is thrown from the logging core during the filtering or processing of log records. One can use the [link log.detailed.utilities.exception_handlers library-provided adapters] to simplify implementing exception handlers. Usage example is as follows:
|
||||
|
||||
[example_sources_exception_handler]
|
||||
|
||||
[tip [link log.detailed.core.core.exception_handling Logging core] and [link log.detailed.sink_frontends.basic_services.exception_handling sink frontends] also support installing exception handlers.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:mixed_loggers Loggers with mixed features]
|
||||
|
||||
#include <``[boost_log_sources_severity_channel_logger_hpp]``>
|
||||
|
||||
If you wonder whether you can use a mixed set of several logger features in one logger, then yes, you certainly can. The library provides [class_sources_severity_channel_logger] and [class_sources_severity_channel_logger_mt] (with their wide-char analogues [class_sources_wseverity_channel_logger] and [class_sources_wseverity_channel_logger_mt]) which combine features of the described loggers with [link log.detailed.sources.severity_level_logger severity level] and [link log.detailed.sources.channel_logger channels] support. The composite loggers are templates, too, which allows you to specify severity level and channel types. You can also design your own logger features and combine them with the ones provided by the library, as described in the [link log.extension.sources Extending the library] section.
|
||||
|
||||
The usage of the loggers with several features does not conceptually differ from the usage of the single-featured loggers. For instance, here is how a [class_sources_severity_channel_logger_mt] could be used:
|
||||
|
||||
[example_sources_severity_channel]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:global_storage Global storage for loggers]
|
||||
|
||||
#include <``[boost_log_sources_global_logger_storage_hpp]``>
|
||||
|
||||
Sometimes it is inconvenient to have a logger object to be able to write logs. This issue is often present in functional-style code with no obvious places where a logger could be stored. Another domain where the problem persists is generic libraries that would benefit from logging. In such cases it would be more convenient to have one or several global loggers in order to easily access them in every place when needed. In this regard `std::cout` is a good example of such a logger.
|
||||
|
||||
The library provides a way to declare global loggers that can be accessed pretty much like `std::cout`. In fact, this feature can be used with any logger, including user-defined ones. Having declared a global logger, one can be sure to have a thread-safe access to this logger instance from any place of the application code. The library also guarantees that a global logger instance will be unique even across module boundaries. This allows employing logging even in header-only components that may get compiled into different modules.
|
||||
|
||||
One may wonder why there is a need for something special in order to create global loggers. Why not just declare a logger variable at namespace scope and use it wherever you need? While technically this is possible, declaring and using global logger variables is complicated for the following reasons:
|
||||
|
||||
* Order of initialization of namespace scope variables is not specified by the C++ Standard. This means that generally you cannot use the logger during this stage of initialization (i.e. before `main`).
|
||||
* Initialization of namespace scope variables is not thread-safe. You may end up initializing the same logger twice or using an uninitialized logger.
|
||||
* Using namespace scope variables in a header-only library is quite complicated. One either has to declare a variable with external linkage and define it only in a single translation unit (that is, in a separate .cpp file, which defeats the "header-only" thesis), or define a variable with internal linkage, or as a special case in an anonymous namespace (this will most likely break ODR and give unexpected results when the header is used in different translation units). There are other compiler-specific and standard tricks to tackle the problem, but they are not quite trivial and portable.
|
||||
* On most platforms namespace scope variables are local to the module where they were compiled in. That is, if variable `a` has external linkage and was compiled into modules X and Y, each of these modules has its own copy of variable `a`. To make things worse, on other platforms this variable can be shared between the modules.
|
||||
|
||||
Global logger storage is intended to eliminate all these problems.
|
||||
|
||||
The easiest way to declare a global logger is to use the following macro:
|
||||
|
||||
``[macroref BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT]``(my_logger, src::severity_logger_mt< >)
|
||||
|
||||
The `my_logger` argument gives the logger a name that may be used to acquire the logger instance. This name acts as a tag of the declared logger. The second parameter denotes the logger type. In multithreaded applications, when the logger can be accessed from different threads, users will normally want to use the thread-safe versions of loggers.
|
||||
|
||||
If passing arguments to the logger constructor is needed, there is another macro:
|
||||
|
||||
``[macroref BOOST_LOG_INLINE_GLOBAL_LOGGER_CTOR_ARGS]``(
|
||||
my_logger,
|
||||
src::severity_channel_logger< >,
|
||||
(keywords::severity = error)(keywords::channel = "my_channel"))
|
||||
|
||||
The last macro argument is a __boost_preprocessor__ sequence of arguments passed to the logger constructor. Be careful, however, when using non-constant expressions and references to objects as constructor arguments, since the arguments are evaluated only once and it is often difficult to tell the exact moment when it is done. The logger is constructed on the first request from whichever part of the application that has the knowledge of the logger declaration. It is up to user to make sure that all arguments have valid states at that point.
|
||||
|
||||
The third macro of this section provides maximum initialization flexibility, allowing the user to actually define the logic of creating the logger.
|
||||
|
||||
``[macroref BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT]``(my_logger, src::severity_logger_mt)
|
||||
{
|
||||
// Do something that needs to be done on logger initialization,
|
||||
// e.g. add a stop watch attribute.
|
||||
src::severity_logger_mt< > lg;
|
||||
lg.add_attribute("StopWatch", boost::make_shared< attrs::timer >());
|
||||
// The initializing routine must return the logger instance
|
||||
return lg;
|
||||
}
|
||||
|
||||
Like the [macroref BOOST_LOG_INLINE_GLOBAL_LOGGER_CTOR_ARGS] macro, the initialization code is called only once, on the first request of the logger.
|
||||
|
||||
[important Beware of One Definition Rule (ODR) issues. Regardless of the way of logger declaration you choose, you should ensure that [_the logger is declared in exactly the same way at all occurrences] and [_all symbol names involved in the declaration resolve to the same entities]. The latter includes the names used within the initialization routine of the [macroref BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT] macro, such as references to external variables, functions and types. The library tries to protect itself from ODR violations to a certain degree, but in general the behavior is undefined if the rule is violated.]
|
||||
|
||||
In order to alleviate ODR problems, it is possible to separate the logger declaration and its initialization routine. The library provides the following macros to achieve this:
|
||||
|
||||
* [macroref BOOST_LOG_GLOBAL_LOGGER] provides the logger declaration. It can be used in a header, similarly to the `BOOST_LOG_INLINE_GLOBAL_LOGGER*` macros described above.
|
||||
* [macroref BOOST_LOG_GLOBAL_LOGGER_INIT], [macroref BOOST_LOG_GLOBAL_LOGGER_DEFAULT] and [macroref BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS] define the logger initialization routine. Their semantics and usage is similar to the corresponding `BOOST_LOG_INLINE_GLOBAL_LOGGER*` macros, for one exception: these macros should be used in a single .cpp file.
|
||||
|
||||
For example:
|
||||
|
||||
// my_logger.h
|
||||
// ===========
|
||||
|
||||
BOOST_LOG_GLOBAL_LOGGER(my_logger, src::severity_logger_mt)
|
||||
|
||||
|
||||
// my_logger.cpp
|
||||
// ===========
|
||||
|
||||
#include "my_logger.h"
|
||||
|
||||
BOOST_LOG_GLOBAL_LOGGER_INIT(my_logger, src::severity_logger_mt)
|
||||
{
|
||||
src::severity_logger_mt< > lg;
|
||||
lg.add_attribute("StopWatch", boost::make_shared< attrs::timer >());
|
||||
return lg;
|
||||
}
|
||||
|
||||
Regardless of the macro you used to declare the logger, you can acquire the logger instance with the static `get` function of the logger tag:
|
||||
|
||||
src::severity_logger_mt< >& lg = my_logger::get();
|
||||
|
||||
Further usage of the logger is the same as if it was a regular logger object of the corresponding type.
|
||||
|
||||
[warning It should be noted that it is not advised to use global loggers during the deinitialization stage of the application. Like any other global object in your application, the global logger may get destroyed before you try to use it. In such cases it's better to have a dedicated logger object that is guaranteed to be available as long as needed.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
29
doc/todo.qbk
Normal file
29
doc/todo.qbk
Normal file
@ -0,0 +1,29 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:todo TODO in future releases]
|
||||
|
||||
Points in this section are not necessarily going to be implemented. These are mainly some thoughts on further improvements of the library.
|
||||
|
||||
* Optimize single-threaded configuration. In many places dynamic memory allocation can be avoided if multithreading support is disabled.
|
||||
* SNMP support. The idea is to implement a sink backend that would emit SNMP traps as a result of processing log records. This needs quite an amount of research and thinking over.
|
||||
* Provide a compile-time option to remove all logging from the application (the compiled binary should contain no traces of logging internally). There are two reasons for this request: attempting to achieve maximum performance and concealing internal information, such as function names and internal messages, to prevent reverse engineering in no-logging builds. Effectively, this would require not only all library macros to be redefined to emptiness, but also to provide dummy implementations of many library components. Needs more consideration. Perhaps, suppressing only macros would be sufficient.
|
||||
* Provide a macro, like `BOOST_LOG_FUNCTION`, but with ability to automatically log all function arguments.
|
||||
* Think over a header-only configuration. Perhaps, with a reduced functionality.
|
||||
* Update syslog support to [@http://tools.ietf.org/html/rfc5424 RFC 5424].
|
||||
* Provide some kind of shared formatters. The idea is that several sinks may use the same formatter. If a log record passes filtering to multiple such sinks, the formatting is done just once for all sinks that share the formatter. Maybe, it will require refactoring the sinks architecture, transforming them into pipelines with formatter and backends being just steps in log record processing.
|
||||
* Allow to change the locale for the file stream in the text file backend. The locale can alter the character code conversion in wide-character logging.
|
||||
* Improve file collection in the file sink. Make it possible to (i) rename collected files and (ii) collect files in a dedicated thread.
|
||||
* Provide headers with forward declarations of the library components.
|
||||
* Make it possible to update library configuration after loading settings from a file. Probably, this will require a new configuration entity that will be able to detect and apply changes between settings.
|
||||
* Develop a statistics gathering framework. The basic idea is to provide a specific log source and a pin. The user can pin his data or explicitly indicate events by invoking the log source. The source would automatically collect the data from the pinned variables. This source should have a better integration with filters to be able which pins should be collected and which should not.
|
||||
* Allow to specify a process ID in the file name pattern for file-based sinks.
|
||||
* Improve support for `format` formatter, implement placeholder format flags.
|
||||
|
||||
[endsect]
|
375
doc/tutorial.qbk
Normal file
375
doc/tutorial.qbk
Normal file
@ -0,0 +1,375 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:tutorial Tutorial]
|
||||
|
||||
In this section we shall walk through the essential steps to get started with the library. After reading it you should be able to initialize the library and add logging to your application. The code of this tutorial is also available in examples residing in the `libs/log/examples` directory. Feel free to play with them, compile and see the result.
|
||||
|
||||
[section:trivial Trivial logging]
|
||||
|
||||
For those who don't want to read tons of clever manuals and just need a simple tool for logging, here you go:
|
||||
|
||||
[example_tutorial_trivial]
|
||||
|
||||
[@boost:/libs/log/example/doc/tutorial_trivial.cpp See the complete code].
|
||||
|
||||
The `BOOST_LOG_TRIVIAL` macro accepts a severity level and results in a stream-like object that supports insertion operator. As a result of this code, the log messages will be printed on the console. As you can see, this library usage pattern is quite similar to what you would do with `std::cout`. However, the library offers a few advantages:
|
||||
|
||||
# Besides the record message, each log record in the output contains a timestamp, the current thread identifier and severity level.
|
||||
# It is safe to write logs from different threads concurrently, log messages will not be corrupted.
|
||||
# As will be shown later, filtering can be applied.
|
||||
|
||||
It must be said that the macro, along with other similar macros provided by the library, is not the only interface the library offers. It is possible to issue log records without using any macros at all.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:trivial_filtering Trivial logging with filters]
|
||||
|
||||
While severity levels can be used for informative purposes, you will normally want to apply filters to output only significant records and ignore the rest. It is easy to do so by setting a global filter in the library core, like this:
|
||||
|
||||
[example_tutorial_trivial_with_filtering]
|
||||
|
||||
[@boost:/libs/log/example/doc/tutorial_trivial_flt.cpp See the complete code].
|
||||
|
||||
Now, if we run this code sample, the first two log records will be ignored, while the remaining four will pass on to the file.
|
||||
|
||||
[important Remember that the streaming expression is only executed if the record passed filtering. Don't specify business-critical calls in the streaming expression, as these calls may not get invoked if the record is filtered away.]
|
||||
|
||||
A few words must be said about the filter setup expression. Since we're setting up a global filter, we have to acquire the [link log.detailed.core.core logging core] instance. This is what `logging::core::get()` does - it returns a pointer to the core singleton. The `set_filter` method of the logging core sets the global filtering function.
|
||||
|
||||
The filter in this example is built as a __boost_phoenix__ lambda expression. In our case, this expression consists of a single logical predicate, whose left argument is a placeholder that describes the attribute to be checked, and the right argument is the value to be checked against. The `severity` keyword is a placeholder provided by the library. This placeholder identifies the severity attribute value in the template expressions; this value is expected to have name "Severity" and type [enumref boost::log::trivial::severity_level `severity_level`]. This attribute is automatically provided by the library in case of trivial logging; the user only has to supply its value in logging statements. The placeholder along with the ordering operator creates a function object that will be called by the logging core to filter log records. As a result, only log records with severity level not less than `info` will pass the filter and end up on the console.
|
||||
|
||||
It is possible to build more complex filters, combining logical predicates like this with each other, or even define your own function (including a C++11 lambda function) that would act as a filter. We will return to filtering in the following sections.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:sinks Setting up sinks]
|
||||
|
||||
Sometimes trivial logging doesn't provide enough flexibility. For example, one may want a more sophisticated logic of log processing, rather than simply printing it on the console. In order to customize this, you have to construct logging sinks and register them with the logging core. This should normally be done only once somewhere in the startup code of your application.
|
||||
|
||||
[note It must be mentioned that in the previous sections we did not initialize any sinks, and trivial logging worked somehow anyway. This is because the library contains a ['default] sink that is used as a fallback when the user did not set up any sinks. This sink always prints log records to the console in a fixed format which we saw in our previous examples. The default sink is mostly provided to allow trivial logging to be used right away, without any library initialization whatsoever. Once you add any sinks to the logging core, the default sink will no longer be used. You will still be able to use trivial logging macros though.]
|
||||
|
||||
[heading File logging unleashed]
|
||||
|
||||
As a starting point, here is how you would initialize logginig to a file:
|
||||
|
||||
[example_tutorial_file_simple]
|
||||
|
||||
The added piece is the call to the [link log.detailed.utilities.setup.convenience `add_file_log`] function. As the name implies, the function initializes a logging sink that stores log records into a text file. The function also accepts a number of customization options, such as the file rotation interval and size limits. For instance:
|
||||
|
||||
[example_tutorial_file_advanced]
|
||||
|
||||
[@boost:/libs/log/example/doc/tutorial_file.cpp See the complete code].
|
||||
|
||||
You can see that the options are passed to the function in the named form. This approach is also taken in many other places of the library. You'll get used to it. The meaning of the parameters is mostly self-explaining and is documented in this manual (see [link log.detailed.sink_backends.text_file here] for what regards the text file sink). This and other convenience initialization functions are described in [link log.detailed.utilities.setup.convenience this] section.
|
||||
|
||||
[note You can register more than one sink. Each sink will receive and process log records as you emit them independently from others.]
|
||||
|
||||
[heading Sinks in depth: More sinks]
|
||||
|
||||
If you don't want to go into details, you can skip this section and continue reading from the next one. Otherwise, if you need more comprehensive control over sink configuration or want to use more sinks than those available through helper functions, you can register sinks manually.
|
||||
|
||||
In the simplest form, the call to the `add_file_log` function in the section above is nearly equivalent to this:
|
||||
|
||||
[example_tutorial_file_manual]
|
||||
|
||||
[@boost:/libs/log/example/doc/tutorial_file_manual.cpp See the complete code].
|
||||
|
||||
Ok, the first thing you may have noticed about sinks is that they are composed of two classes: the frontend and the backend. The frontend (which is the [link log.detailed.sink_frontends.sync `synchronous_sink`] class template in the snippet above) is responsible for various common tasks for all sinks, such as thread synchronization model, filtering and, for text-based sinks, formatting. The backend (the [link log.detailed.sink_backends.text_ostream `text_ostream_backend`] class above) implements everything specific to the sink, such as writing to a file in this case. The library provides a number of frontends and backends that can be used with each other out of the box.
|
||||
|
||||
The [link log.detailed.sink_frontends.sync `synchronous_sink`] class template above indicates that the sink is synchronous, that is, it allows for several threads to log simultaneously and will block in case of contention. This means that the backend [link log.detailed.sink_backends.text_ostream `text_ostream_backend`] doesn't have to worry about multithreading at all. There are other sink frontends available, you can read more about them [link log.detailed.sink_frontends here].
|
||||
|
||||
The [link log.detailed.sink_backends.text_ostream `text_ostream_backend`] class writes formatted log records into STL-compatible streams. We have used a file stream above but we could have used any type of stream. For example, adding output to console could look as follows:
|
||||
|
||||
#include <``[boost_log_utility_empty_deleter_hpp]``>
|
||||
|
||||
// We have to provide an empty deleter to avoid destroying the global stream object
|
||||
boost::shared_ptr< std::ostream > stream(&std::clog, logging::empty_deleter());
|
||||
sink->locked_backend()->add_stream(stream);
|
||||
|
||||
The [link log.detailed.sink_backends.text_ostream `text_ostream_backend`] supports adding several streams. In that case its output will be duplicated to all added streams. It can be useful to duplicate the output to console and file since all the filtering, formatting and other overhead of the library happen only once per record for the sink.
|
||||
|
||||
[note Please note the difference between registering several distinct sinks and registering one sink with several target streams. While the former allows for independently customizing output to each sink, the latter would work considerably faster if such customization is not needed. This feature is specific to this particular backend.]
|
||||
|
||||
The library provides a number of [link log.detailed.sink_backends backends] that provide different log processing logic. For instance, by specifying the [link log.detailed.sink_backends.syslog syslog] backend you can send log records over the network to the syslog server, or by setting up the [link log.detailed.sink_backends.event_log Windows NT event log] backend you can monitor your application run time with the standard Windows tools.
|
||||
|
||||
The last thing worth noting here is the `locked_backend` member function call to access the sink backend. It is used to get thread-safe access to the backend and is provided by all sink frontends. This function returns a smart-pointer to the backend and as long as it exists the backend is locked (which means even if another thread tries to log and the log record is passed to the sink, it will not be logged until you release the backend). The only exception is the [link log.detailed.sink_frontends.unlocked `unlocked_sink`] frontend which does not synchronize at all and simply returns an unlocked pointer to the backend.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:sources Creating loggers and writing logs]
|
||||
|
||||
[heading Dedicated logger objects]
|
||||
|
||||
Now that we have defined where and how the log is to be stored, it's time to go on and try logging. In order to do this one has to create a logging source. This would be a logger object in our case and it is as simple as that:
|
||||
|
||||
src::logger lg;
|
||||
|
||||
[note A curious reader could have noticed that we did not create any loggers for trivial logging. In fact the logger is provided by the library and is used behind the scenes.]
|
||||
|
||||
Unlike sinks, sources need not be registered anywhere since they interact directly with the logging core. Also note that there are two versions of loggers provided by the library: the thread-safe ones and the non-thread-safe ones. For the non-thread-safe loggers it is safe for different threads to write logs through different instances of loggers and thus there should be a separate logger for each thread that writes logs. The thread-safe counterparts can be accessed from different threads concurrently, but this will involve locking and may slow things down in case of intense logging. The thread-safe logger types have the `_mt` suffix in their name.
|
||||
|
||||
Regardless of the thread safety, all loggers provided by the library are default and copy-constructible and support swapping, so there should be no problem in making a logger a member of your class. As you will see later, such approach can give you additional benefits.
|
||||
|
||||
The library provides a number of loggers with different features, such as severity and channel support. These features can be combined with each other in order to construct more complex loggers. See [link log.detailed.sources here] for more datails.
|
||||
|
||||
[heading Global logger objects]
|
||||
|
||||
In case you cannot put a logger into your class (suppose you don't have one), the library provides a way of declaring global loggers like this:
|
||||
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::logger_mt)
|
||||
|
||||
Here `my_logger` is a user-defined tag name that will be used later to retrieve the logger instance and `logger_mt` is the logger type. Any logger type provided by the library or defined by the user can participate in such declaration. However, since the logger will have a single instance, you will normally want to use thread-safe loggers in a multithreaded application as global ones.
|
||||
|
||||
[tip There are other macros for more sophisticated cases available. The detailed description is in [link log.detailed.sources.global_storage this] section.]
|
||||
|
||||
Later on you can acquire the logger like this:
|
||||
|
||||
src::logger_mt& lg = my_logger::get();
|
||||
|
||||
The `lg` will refer to the one and only instance of the logger throughout the application, even if the application consists of multiple modules. The `get` function itself is thread-safe, so there is no need in additional synchronization around it.
|
||||
|
||||
[heading Writing logs]
|
||||
|
||||
No matter what kind of logger you use (class member or global, thread-safe or not), to write a log record into a logger you can write something like this:
|
||||
|
||||
[example_tutorial_logging_manual_logging]
|
||||
|
||||
Here the `open_record` function call determines if the record to be constructed is going to be consumed by at least one sink. Filtering is applied at this stage. If the record is to be consumed, the function returns a valid record object, and one can fill in the record message string. After that the record processing can be completed with the call to `push_record`.
|
||||
|
||||
Of course, the above syntax can easily be wrapped in a macro and, in fact, users are encouraged to write their own macros instead of using the C++ logger interface directly. The log record above can be written like this:
|
||||
|
||||
BOOST_LOG(lg) << "Hello, World!";
|
||||
|
||||
Looks a bit shorter, doesn't it? The `BOOST_LOG` macro, along with other similar ones, is defined by the library. It automatically provides an STL-like stream in order to format the message with ordinary insertion expressions. Having all that code written, compiled and executed you should be able to see the "Hello, World!" record in the "sample.log" file. You will find the full code of this section [@boost:/libs/log/example/doc/tutorial_logging.cpp here].
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:attributes Adding more information to log: Attributes]
|
||||
|
||||
In previous sections we mentioned attributes and attribute values several times. Here we will discover how attributes can be used to add more data to log records.
|
||||
|
||||
Each log record can have a number of named attribute values attached. Attributes can represent any essental information about the conditions in which the log record occurred, such as position in the code, executable module name, current date and time, or any piece of data relevant to your particular application and execution environment. An attribute may behave as a value generator, in which case it would return a different value for each log record it's involved in. As soon as the attribute generates the value, the latter becomes independent from the creator and can be used by filters, formatters and sinks. But in order to use the attribute value one has to know its name and type, or at least a set of types it may have. There are a number of commonly used attributes implemented in the library, you can find the types of their values in the documentation.
|
||||
|
||||
Aside from that, as described in the [link log.design Design overview] section, there are three possible scopes of attributes: source-specific, thread-specific and global. When a log record is made, attribute values from these three sets are joined into a single set and passed to sinks. This implies that the origin of the attribute makes no difference for sinks. Any attribute can be registered in any scope. When registered, an attribute is given a unique name in order to make it possible to search for it. If it happens that the same named attribute is found in several scopes, the attribute from the most specific scope is taken into consideration in any further processing, including filtering and formatting. Such behavior makes it possible to override global or thread-scoped attributes with the ones registered in your local logger, thus reducing thread interference.
|
||||
|
||||
Below is the description of the attribute registration process.
|
||||
|
||||
[heading Commonly used attributes]
|
||||
|
||||
There are attributes that are likely to be used in nearly any application. Log record counter and a time stamp are good candidates. They can be added with a single function call:
|
||||
|
||||
logging::add_common_attributes();
|
||||
|
||||
With this call attributes "LineID", "TimeStamp", "ProcessID" and "ThreadID" are registered globally. The "LineID" attribute is a counter that increments for each record being made, the first record gets identifier 1. The "TimeStamp" attribute always yields the current time (i.e. the time when the log record is created, not the time it was written to a sink). The last two attributes identify the process and the thread in which every log record is emitted.
|
||||
|
||||
[note In single-threaded builds the "ThreadID" attribute is not registered.]
|
||||
|
||||
[tip By default, when application starts, no attributes are registered in the library. The application has to register all the necessary attributes in the library before it starts writing logs. This can be done as a part of the library initialization. A curious reader could have wondered how trivial logging works then. The answer is that the default sink doesn't really use any attribute values, except for the severity level, to compose its output. This is done to avoid the need for any initialization for trivial logging. Once you use filters or formatters and non-default sinks you will have to register the attributes you need.]
|
||||
|
||||
The [funcref boost::log::add_common_attributes `add_common_attributes`] function is one of the several convenience helpers described [link log.detailed.utilities.setup.convenience here].
|
||||
|
||||
Some attrubutes are registered automatically on loggers construction. For example, [link log.detailed.sources.severity_level_logger `severity_logger`] registers a source-specific attribute "Severity" which can be used to add a level of emphasis for different log records. For example:
|
||||
|
||||
[example_sources_severity]
|
||||
|
||||
[tip You can define your own formatting rules for the severity level by defining `operator <<` for this type. It will be automatically used by the library formatters. See [link log.detailed.expressions.attr this] section for more details.]
|
||||
|
||||
The `BOOST_LOG_SEV` macro acts pretty much like `BOOST_LOG` except that it takes an additional argument for the `open_record` method of the logger. The `BOOST_LOG_SEV` macro can be replaced with this equivalent:
|
||||
|
||||
[example_sources_severity_manual]
|
||||
|
||||
You can see here that the `open_record` can take named arguments. Some logger types provided by the library have support for such additional parameters and this approach can certainly be used by users when writing their own loggers.
|
||||
|
||||
[heading More attributes]
|
||||
|
||||
Let's see what's under the hood of that [link log.detailed.utilities.setup.convenience `add_common_attributes`] function we used in the simple form section. It might look something like this:
|
||||
|
||||
void add_common_attributes()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
core->add_global_attribute("LineID", attrs::counter< unsigned int >(1));
|
||||
core->add_global_attribute("TimeStamp", attrs::local_clock());
|
||||
|
||||
// other attributes skipped for brevity
|
||||
}
|
||||
|
||||
Here the [link log.detailed.attributes.counter `counter`] and [link log.detailed.attributes.clock `local_clock`] components are attribute classes, they derive from the common attribute interface [class_log_attribute]. The library provides a number of other [link log.detailed.attributes attribute classes], including the [link log.detailed.attributes.function `function`] attribute that calls some function object on value acquisition. For example, we can in a similar way register a [link log.detailed.attributes.named_scope `named_scope`] attrubute:
|
||||
|
||||
core->add_global_attribute("Scope", attrs::named_scope());
|
||||
|
||||
This will give us the ability to store scope names in log for every log record the application makes. Here is how it's used:
|
||||
|
||||
[example_tutorial_attributes_named_scope]
|
||||
|
||||
Logger-specific attributes are no less useful than global ones. Severity levels and channel names are the most obvious candidates to be implemented on the source level. Nothing prevents you from adding more attributes to your loggers, like this:
|
||||
|
||||
[example_tutorial_attributes_tagged_logging]
|
||||
|
||||
Now all log records made through this logger will be tagged with the specific attribute. This attribute value may be used later in filtering and formatting.
|
||||
|
||||
Another good use of attributes is the ability to mark log records made by different parts of application in order to highlight activity related to a single process. One can even implement a rough profiling tool to detect performance bottlenecks. For example:
|
||||
|
||||
[example_tutorial_attributes_timed_logging]
|
||||
|
||||
Now every log record made from the `logging_function` function, or any other function it calls, will contain the "Timeline" attribute with a high precision time duration passed since the attribute was registered. Based on these readings, one will be able to detect which parts of the code require more or less time to execute. The "Timeline" attribute will be unregistered upon leaving the scope of function `timed_logging`.
|
||||
|
||||
See the [link log.detailed.attributes Attributes] section for detailed description of attributes provided by the library. The complete code for this section is available [@boost:/libs/log/example/doc/tutorial_attributes.cpp here].
|
||||
|
||||
[heading Defining attribute placeholders]
|
||||
|
||||
As we will see in the coming sections, it is useful to define a keyword describing a particular attribute the application uses. This keyword will be able to participate in filtering and formatting expressions, like the `severity` placeholder we have used in previous sections. For example, to define placeholders for some of the attributes we used in the previous examples we can write this:
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(scope, "Scope", attrs::named_scope::value_type)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(timeline, "Timeline", attrs::timer::value_type)
|
||||
|
||||
Each macro defines a keyword. The first argument is the placeholder name, the second is the attribute name and the last parameter is the attribute value type. Once defined, the placeholder can be used in template expressions and some other contexts of the library. More details on defining attribute keywords are available [link log.detailed.expressions.attr_keywords here].
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:formatters Log record formatting]
|
||||
|
||||
If you tried running examples from the previous sections, you may have noticed that only log record messages are written to the files. This is the default behavior of the library when no formatter is set. Even if you added attributes to the logging core or a logger, the attribute values will not reach the output unless you specify a formatter that will use these values.
|
||||
|
||||
Returning to one of the examples in previous tutorial sections:
|
||||
|
||||
[example_tutorial_file_advanced_no_callouts]
|
||||
|
||||
In the case of the `add_file_log` function, the `format` parameter allows to specify format of the log records. If you prefer to set up sinks manually, sink frontends provide the `set_formatter` member function for this purpose.
|
||||
|
||||
The format can be specified in a number of ways, as described further.
|
||||
|
||||
[heading Lambda-style formatters]
|
||||
|
||||
You can create a formatter with a lambda-style expression like this:
|
||||
|
||||
[example_tutorial_formatters_stream]
|
||||
|
||||
[@boost:/libs/log/example/doc/tutorial_fmt_stream.cpp See the complete code].
|
||||
|
||||
Here the `stream` is a placeholder for the stream to format the record in. Other insertion arguments, such as [link log.detailed.expressions.attr `attr`] and [link log.detailed.expressions.message `message`], are manipulators that define what should be stored in the stream. We have already seen the `severity` placeholder in filtering expressions, and here it is used in a formatter. This is a nice unification: you can use the same placeholders in both filters and formatters. The [link log.detailed.expressions.attr `attr`] placeholder is similar to the `severity` placeholder as it represents the attribute value, too. The difference is that the `severity` placeholder represents the particular attribute with the name "Severity" and type `trivial::severity_level` and [link log.detailed.expressions.attr `attr`] can be used to represent any attribute. Otherwise the two placeholders are equivalent. For instance, it is possible to replace `severity` with the following:
|
||||
|
||||
expr::attr< logging::trivial::severity_level >("Severity")
|
||||
|
||||
[tip As shown in the previous section, it is possible to define placeholders like `severity` for user's attributes. As an additional benefit to the simpler syntax in the template expressions such placeholders allow to concentrate all the information about the attribute (the name and the value type) in the placeholder definition. This makes coding less error-prone (you won't misspel the attribute name or specify incorrect value type) and therefore is the recommended way of defining new attributes and using them in template expressions.]
|
||||
|
||||
There are other [link log.detailed.expressions.formatters formatter manipulators] that provide advanced support for date, time and other types. Some manipulators accept additional arguments that customize their behavior. Most of these arguments are named and can be passed in __boost_parameter__ style.
|
||||
|
||||
For a change, let's see how it's done when manually initializing sinks:
|
||||
|
||||
[example_tutorial_formatters_stream_manual]
|
||||
|
||||
[@boost:/libs/log/example/doc/tutorial_fmt_stream_manual.cpp See the complete code].
|
||||
|
||||
You can see that it is possible to bind format changing manipulators in the expression; these manipulators will affect the subsequent attribute value format when log record is formatted, just like with streams. More manipulators are described in the [link log.detailed.expressions Detailed features description] section.
|
||||
|
||||
[heading Boost.Format-style formatters]
|
||||
|
||||
As an alternative, you can define formatters with with a syntax similar to __boost_format__. The same formatter as described above can be written as follows:
|
||||
|
||||
[example_tutorial_formatters_format]
|
||||
|
||||
[@boost:/libs/log/example/doc/tutorial_fmt_format.cpp See the complete code].
|
||||
|
||||
The `format` placeholder accepts the format string with positional specification of all arguments being formatted. Note that only positional format is currently supported. The same format specification can be used with the `add_file_log` and similar functions.
|
||||
|
||||
[heading Specialized formatters]
|
||||
|
||||
The library provides specialized formatters for a number of types, such as date, time and named scope. These formatters provide extended control over the formatted values. For example, it is possible to describe date and time format with a format string compatible with __boost_date_time__:
|
||||
|
||||
[example_tutorial_formatters_stream_date_time]
|
||||
|
||||
[@boost:/libs/log/example/doc/tutorial_fmt_stream.cpp See the complete code].
|
||||
|
||||
The same formatter can also be used in the context of a __boost_format__-style formatter.
|
||||
|
||||
[heading String templates as formatters]
|
||||
|
||||
In some contexts textual templates are accepted as formatters. In this case library initialization support code is invoked in order to parse the template and reconstruct the appropriate formatter. There are a number of caveats to keep in mind when using this approach, but here it will suffice to just briefly describe the template format.
|
||||
|
||||
[example_tutorial_formatters_string]
|
||||
|
||||
[@boost:/libs/log/example/doc/tutorial_fmt_string.cpp See the complete code].
|
||||
|
||||
Here, the `format` parameter accepts such a format template. The template may contain a number of placeholders enclosed with percent signs (`%`). Each placeholder must contain an attribute value name to insert instead of the placeholder. The `%Message%` placeholder will be replaced with the logging record message.
|
||||
|
||||
[note Textual format templates are not accepted by sink backends in the `set_formatter` method. In order to parse textual template into a formatter function one has to call `parse_formatter` function. See [link log.detailed.utilities.setup.filter_formatter here] for more details.]
|
||||
|
||||
[heading Custom formatting functons]
|
||||
|
||||
You can add a custom formatter to a sink backend that supports formatting. The formatter is actually a function object that supports the following signature:
|
||||
|
||||
void (logging::record_view const& rec, logging::basic_formatting_ostream< CharT >& strm);
|
||||
|
||||
Here `CharT` is the target character type. The formatter will be invoked whenever a log record view `rec` passes filtering and is to be stored in log.
|
||||
|
||||
[tip Record views are very similar to records. The notable distinction is that the view is immutable and implements shallow copy. Formatters and sinks only operate on record views, which prevents them from modifying the record while it can be still in use by other sinks in other threads.]
|
||||
|
||||
The formatted record should be composed by insertion into STL-compatible output stream `strm`. Here's an example of a custom formatter function usage:
|
||||
|
||||
[example_tutorial_formatters_custom]
|
||||
|
||||
[@boost:/libs/log/example/doc/tutorial_fmt_custom.cpp See the complete code].
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:advanced_filtering Filtering revisited]
|
||||
|
||||
We've already touched filtering in the previous sections but we barely scratched the surface. Now that we are able to add attributes to log records and set up sinks, we can build however complex filtering we need. Let's consider this example:
|
||||
|
||||
[example_tutorial_filtering]
|
||||
|
||||
[@boost:/libs/log/example/doc/tutorial_filtering.cpp See the complete code].
|
||||
|
||||
In this sample we initialize two sinks - one for the complete log file and the other for important messages only. Both sinks will be writing to text files with the same log record format, which we initialize first and save to the `fmt` variable. The [classref boost::log::basic_formatter formatter] type is a type-erased function object with the formatter calling signature; in many respects it can be viewed similar to `boost::function` or `std::function` except that it is never empty. There is also [classref boost::log::filter a similar function object] for filters.
|
||||
|
||||
Notably, the formatter itself contains a filter here. As you can see, the format contains a conditional part that is only present when log records contain the "Tag" attribute. The [link log.detailed.expressions.predicates.has_attr `has_attr`] predicate checks whether the record containts the "Tag" attribute value and conrtols whether it is put into the file or not. We used the attribute keyword to specify the name and type of the attribute for the predicate, but it is also possible to specify them in the [link log.detailed.expressions.predicates.has_attr `has_attr`] call site. Conditional formatters are explained in more details [link log.detailed.expressions.formatters.conditional here].
|
||||
|
||||
Further goes the initialization of the two sinks. The first sink does not have any filter, which means it will save every log record to the file. We call `set_filter` on the second sink to only save log records with severity no less than `warning` or having a "Tag" attribute with value "IMPORTANT_MESSAGE". As you can see, the filter syntax resembles usual C++ very much, especially when attribute keywords are used.
|
||||
|
||||
Like with formatters, it is also possible to use custom functions as filters. __boost_phoenix__ can be very helpful in this case as its `bind` implementation is compatible with attribute placeholders. The previous example can be modified in the following way:
|
||||
|
||||
[example_tutorial_filtering_bind]
|
||||
|
||||
As you can see, the custom formatter receives attribute values wrapped into the [link log.detailed.utilities.value_ref `value_ref`] template. This wrapper contains an optional reference to the attribute value of the specified type; the reference is valid if the log record contains the attribute value of the required type. The relational operators used in `my_filter` can be applied unconditionally because they will automatically return `false` if the reference is not valid. The rest is done with the `bind` expression which will recognize the `severity` and `tag_attr` keywords and extract the corresponding values before passing them to `my_filter`.
|
||||
|
||||
You can try how this works by compiling and running the [@boost:/libs/log/example/doc/tutorial_filtering.cpp test].
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:wide_char Wide character logging]
|
||||
|
||||
The library supports logging strings containing national characters. There are basically two ways of doing this. On UNIX-like systems typically some multibyte character encoding (e.g. UTF-8) is used to represent national characters. In this case the library can be used just the way it is used for plain ASCII logging, no additional setup is required.
|
||||
|
||||
On Windows the common practice is to use wide strings to represent national characters. Also, most of the system API is wide character oriented, which requires Windows-specific sinks to also support wide strings. On the other hand, generic sinks, like [link log.detailed.sink_backends.text_file text file sink, are byte-oriented (because, well, you store bytes in files, not characters). This forces the library to perform character code conversion when needed by the sink. To set up the library for this one has to imbue the sink with a locale with the appropriate `codecvt` facet. __boost_locale__ can be used to generate such a locale. Let's see an example:
|
||||
|
||||
[example_wide_char_logging_initialization]
|
||||
|
||||
First let's take a look at the formatter we pass in the `format` parameter. We initialize the sink with a narrow-character formatter because the text file sink processes bytes. It is possible to use wide strings in the formatter, but not in format strings, like the one we used with the [funcref boost::log::expressions::format_date_time `format_date_time`] function. Also note that we used `message` keyword to denote the log record messages. This [link log.detailed.expressions.message placeholder] supports both narrow and wide character messages, so the formatter will work with both. As part of the formatting process, the library will convert wide character messages to multibyte encoding using the imbued locale, which we set to UTF-8.
|
||||
|
||||
[tip Attribute values can also contain wide strings. Like log record messages, these strings will be converted with the imbued locale to the target character encoding.]
|
||||
|
||||
One thing missing here is our `severity_level` type definition. The type is just an enumeration, but if we want to support its formatting for both narrow and wide character sinks, its streaming operator has to be a template. This may be useful if we create multiple sinks with different character types.
|
||||
|
||||
[example_wide_char_severity_level_definition]
|
||||
|
||||
Now we can emit log records. We can use loggers with `w` prefix in their names to compose wide character messages.
|
||||
|
||||
[example_wide_char_logging]
|
||||
|
||||
As you can see, wide character message composition is similar to narrow logging. Note that you can use both narrow and wide character logging at the same time; all records will be processed by our file sink. The complete code of this example can be found [@boost:/libs/log/example/wide_char/main.cpp here].
|
||||
|
||||
It must be noted that some sinks (mostly, Windows-specific ones) allow to specify the target character type. When national characters are expected in log records, one should always use `wchar_t` as the target character type in these cases because the sink will use wide character OS API to process log records. In this case all narrow character strings will be widened using the locale imbued into the sink when formatting is performed.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
608
doc/utilities.qbk
Normal file
608
doc/utilities.qbk
Normal file
@ -0,0 +1,608 @@
|
||||
[/
|
||||
Copyright Andrey Semashev 2007 - 2013.
|
||||
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)
|
||||
|
||||
This document is a part of Boost.Log library documentation.
|
||||
/]
|
||||
|
||||
[section:utilities Utilities]
|
||||
|
||||
[section:string_literal String literals]
|
||||
|
||||
#include <``[boost_log_utility_string_literal_hpp]``>
|
||||
|
||||
String literals are used in several places throughout the library. However, this component can be successfully used outside of the library in users' code. It is header-only and does not require linking with the library binary. String literals can improve performance significantly if there is no need to modify stored strings. What is also important, since string literals do not dynamically allocate memory, it is easier to maintain exception safety when using string literals instead of regular strings.
|
||||
|
||||
The functionality is implemented in the [class_log_basic_string_literal] class template, which is parametrized with the character and character traits, similar to `std::basic_string`. There are also two convenience typedefs provided: `string_literal` and `wstring_literal`, for narrow and wide character types, respectively. In order to ease string literal construction in generic code there is also a `str_literal` function template that accepts a string literal and returns a [class_log_basic_string_literal] instance for the appropriate character type.
|
||||
|
||||
String literals support interface similar to STL strings, except for string modification functions. However, it is possible to assign to or clear string literals, as long as only string literals involved. Relational and stream output operators are also supported.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:type_info_wrapper Type information wrapper]
|
||||
|
||||
#include <``[boost_log_utility_type_info_wrapper_hpp]``>
|
||||
|
||||
The language support for run time type information is essential for the library. But partially because of limitations that the C++ Standard imposes on this feature, partially because of differences of implementation of different compilers, there was a need for a lightweight wrapper around the `std::type_info` class to fill the gaps. The table below briefly shows the differences between the `std::type_info` and [class_log_type_info_wrapper] classes.
|
||||
|
||||
[table Type information classes comparison
|
||||
[[Feature] [`std::type_info`] [`type_info_wrapper`]]
|
||||
[[Is default-constructable] [No] [Yes. The default-constructed wrapper is in an empty state.]]
|
||||
[[Is copy-constructable] [No] [Yes]]
|
||||
[[Is assignable] [No] [Yes]]
|
||||
[[Is swappable] [No] [Yes]]
|
||||
[[Life time duration] [Static, until the application terminates] [Dynamic]]
|
||||
[[Has an empty state] [No] [Yes. In empty state the type info wrapper is never equal to other non-empty type info wrappers. It is equal to other empty type info wrappers and can be ordered with them.]]
|
||||
[[Supports equality comparison] [Yes] [Yes]]
|
||||
[[Supports ordering] [Yes, partial ordering with the `before` method.] [Yes, partial ordering with the complete set of comparison operators. The semantics of ordering is similar to the `std::type_info::before` method.]]
|
||||
[[Supports printable representation of type] [Yes, with the `name` function.] [Yes, with `pretty_name` function. The function does its best to print the type name in a human-readable form. On some platforms it does better than `std::type_info::name`.]]
|
||||
]
|
||||
|
||||
Given the distinctions above, using type information objects becomes much easier. For example, the ability to copy and order with regular operators allows using [class_log_type_info_wrapper] with containers. The ability to default-construct and assign allows using type information as a regular object and not to resort to pointers which may be unsafe.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:type_dispatch Type dispatchers]
|
||||
|
||||
#include <``[boost_log_utility_type_dispatch_type_dispatcher_hpp]``>
|
||||
|
||||
Type dispatchers are used throughout the library in order to work with attribute values. Dispatchers allow acquiring the stored attribute value using the Visitor concept. The most notable places where the functionality is used are filters and formatters. However, this mechanism is orthogonal to attributes and can be used for other purposes as well. Most of the time users won't need to dig into the details of type dispatchers, but this information may be useful for those who intend to extend the library and wants to understand what's under the hood.
|
||||
|
||||
Every type dispatcher supports the [class_log_type_dispatcher] interface. When an attribute value needs to be extracted, this interface is passed to the attribute value object, which then tries to acquire the callback for the actual type of the value. All callbacks are objects of the [class_type_dispatcher_callback] class template, instantiated on the actual type of the value. If the dispatcher is able to consume the value of the requested type, it must return a non-empty callback object. When (and if) the corresponding callback is acquired, the attribute value object only has to pass the contained value to its `operator ()`.
|
||||
|
||||
Happily, there is no need to write type dispatchers from scratch. The library provides two kinds of type dispatchers that implement the [class_log_type_dispatcher] and [class_type_dispatcher_callback] interfaces and encapsulate the callback lookup.
|
||||
|
||||
[heading Static type dispatcher]
|
||||
|
||||
#include <``[boost_log_utility_type_dispatch_static_type_dispatcher_hpp]``>
|
||||
|
||||
Static type dispatchers are used when the set of types that needs to be supported for extraction is known at compile time. The [class_log_static_type_dispatcher] class template is parametrized with a MPL type sequence of types that need to be supported. The dispatcher inherits from the [class_log_type_dispatcher] interface which provides the `get_callback` method for aquiring the function object to invoke on the stored value. All you need to do is provide a visitor function object to the dispatcher at construction point and invoke the callback when dispatching the stored value:
|
||||
|
||||
[example_util_static_type_dispatcher]
|
||||
|
||||
[@boost:/libs/log/example/doc/util_static_type_disp.cpp See the complete code].
|
||||
|
||||
[heading Dynamic type dispatcher]
|
||||
|
||||
#include <``[boost_log_utility_type_dispatch_dynamic_type_dispatcher_hpp]``>
|
||||
|
||||
If the set of types that have to be supported is not available at compile time, the [class_log_dynamic_type_dispatcher] class is there to help. One can use its `register_type` method to add support for a particular type. The user has to pass a function object along with the type, this functor will be called when a visitor for the specified type is invoked. Considering the `my_value` from the code sample for static type dispatcher is intact, the code can be rewritten as follows:
|
||||
|
||||
[example_util_dynamic_type_dispatcher]
|
||||
|
||||
[@boost:/libs/log/example/doc/util_dynamic_type_disp.cpp See the complete code].
|
||||
|
||||
Of course, complex function objects, like those provided by __boost_bind__, are also supported.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:predef_types Predefined type sequences]
|
||||
|
||||
#include <``[boost_log_utility_type_dispatch_standard_types_hpp]``>
|
||||
#include <``[boost_log_utility_type_dispatch_date_time_types_hpp]``>
|
||||
|
||||
One may notice that when using type dispatchers and defining filters and formatters it may be convenient to have some predefined type sequences to designate frequently used sets of types. The library provides several such sets.
|
||||
|
||||
[table Standard types (standard_types.hpp)
|
||||
[[Type sequence] [Meaning]]
|
||||
[[`integral_types`] [All integral types, including `bool`, character and 64 bit integral types, if available]]
|
||||
[[`floating_point_types`] [Floating point types]]
|
||||
[[`numeric_types`] [Includes `integral_types` and `floating_point_types`]]
|
||||
[[`string_types`] [Narrow and wide string types. Currently only includes STL string types and [link log.detailed.utilities.string_literal string literals].]]
|
||||
]
|
||||
|
||||
There are also a number of time-related type sequences available:
|
||||
|
||||
[table Time-related types (date_time_types.hpp)
|
||||
[[Type sequence] [Meaning]]
|
||||
[[`native_date_time_types`] [All types defined in C/C++ standard that have both date and time portions]]
|
||||
[[`boost_date_time_types`] [All types defined in __boost_date_time__ that have both date and time portions]]
|
||||
[[`date_time_types`] [Includes `native_date_time_types` and `boost_date_time_types`]]
|
||||
[[`native_date_types`] [All types defined in C/C++ standard that have date portion. Currently equivalent to `native_date_time_types`.]]
|
||||
[[`boost_date_types`] [All types defined in __boost_date_time__ that have date portion]]
|
||||
[[`date_types`] [Includes `native_date_types` and `boost_date_types`]]
|
||||
[[`native_time_types`] [All types defined in C/C++ standard that have time portion. Currently equivalent to `native_date_time_types`.]]
|
||||
[[`boost_time_types`] [All types defined in __boost_date_time__ that have time portion. Currently equivalent to `boost_date_time_types`.]]
|
||||
[[`time_types`] [Includes `native_time_types` and `boost_time_types`]]
|
||||
[[`native_time_duration_types`] [All types defined in C/C++ standard that are used to represent time duration. Currently only includes `double`, as the result type of the `difftime` standard function.]]
|
||||
[[`boost_time_duration_types`] [All time duration types defined in __boost_date_time__]]
|
||||
[[`time_duration_types`] [Includes `native_time_duration_types` and `boost_time_duration_types`]]
|
||||
[[`boost_time_period_types`] [All time period types defined in __boost_date_time__]]
|
||||
[[`time_period_types`] [Currently equivalent to `boost_time_period_types`]]
|
||||
]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:value_ref Value reference wrapper]
|
||||
|
||||
#include <``[boost_log_utility_value_ref_hpp]``>
|
||||
|
||||
The [class_log_value_ref] class template is an optional reference wrapper which is used by the library to refer to the stored attribute values. To a certain degree it shares features of __boost_optional__ and __boost_variant__ components.
|
||||
|
||||
The template has two type parameters. The first is the referred type. It can also be specified as a __boost_mpl__ type sequence, in which case the [class_log_value_ref] wrapper may refer to either type in the sequence. In this case, the `which` method will return the index of the referred type within the sequence. The second template parameter is an optional tag type which can be used to customize formatting behavior. This tag is forwarded to the [link log.detailed.utilities.manipulators.to_log `to_log`] manipulator when the wrapper is put to a [class_log_basic_formatting_ostream] stream, which is used by the library for record formatting. For an example see how attribute value extraction is implemented:
|
||||
|
||||
[example_attr_value_extraction_multiple_types]
|
||||
|
||||
[@boost:/libs/log/example/doc/attr_value_extraction.cpp See the complete code].
|
||||
|
||||
The [class_log_value_ref] wrapper also supports applying a visitor function object to the referred object. This can be done by calling one of the following methods:
|
||||
|
||||
* `apply_visitor`. This method should only be used on a valid (non-empty) reference. The method returns the visitor result.
|
||||
* `apply_visitor_optional`. The method checks if the reference is valid and applies the visitor to the referred value if it is. The method returns the visitor result wrapped into `boost::optional` which will be filled only if the reference is valid.
|
||||
* `apply_visitor_or_default`. If the reference is valid, the method applies the visitor on the referred value and returns its result. Otherwise the method returns a default value passed as the second argument.
|
||||
|
||||
[note Regardless of the method used, the visitor function object [_must] define the `result_type` typedef. Polymorphic visitors are not supported as this would complicate the [class_log_value_ref] interface too much. This requirement also precludes free functions and C++11 lambda functions from being used as visitors. Please, use __boost_bind__ or similar wrappers in such cases.]
|
||||
|
||||
Here is an example of applying a visitor:
|
||||
|
||||
[example_attr_value_extraction_visitor]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:record_ordering Log record ordering]
|
||||
|
||||
#include <``[boost_log_utility_record_ordering_hpp]``>
|
||||
|
||||
There are cases when log records need to be ordered. One possible use case is storing records in a container or a priority queue. The library provides two types of record ordering predicates out of the box:
|
||||
|
||||
[heading Abstract record ordering]
|
||||
|
||||
The [class_log_abstract_ordering] class allows application of a quick opaque ordering. The result of this ordering is not stable between different runs of the application and in generag cannot be predicted before the predicate is applied, however it provides the best performance. The [class_log_abstract_ordering] class is a template that is specialized with an optional predicate function that will be able to compare `const void*` pointers. By default an `std::less` equivalent is used.
|
||||
|
||||
// A set of unique records
|
||||
std::set< logging::record_view, logging::abstract_ordering< > > m_Records;
|
||||
|
||||
This kind of ordering can be useful if the particular order of log records is not important but nevertheless some order is required.
|
||||
|
||||
[heading Attribute value based ordering]
|
||||
|
||||
This kind of ordering is implemented with the [class_log_attribute_value_ordering] class and is based on the attribute values attached to the record. The predicate will seek for an attribute value with the specified name in both records being ordered and attempt to compare the attribute values.
|
||||
|
||||
// Ordering type definition
|
||||
typedef logging::attribute_value_ordering<
|
||||
int // attribute value type
|
||||
> ordering;
|
||||
|
||||
// Records organized into a queue based on the "Severity" attribute value
|
||||
std::priority_queue<
|
||||
logging::record_view,
|
||||
std::vector< logging::record_view >,
|
||||
ordering
|
||||
> m_Records(ordering("Severity"));
|
||||
|
||||
Like the [class_log_abstract_ordering], [class_log_attribute_value_ordering] also accepts the second optional template parameter, which should be the predicate to compare attribute values (`int`s in the example above). By default, an `std::less` equivalent is used.
|
||||
|
||||
You can also use the [funcref boost::log::make_attr_ordering `make_attr_ordering`] generator function to automatically generate the [class_log_attribute_value_ordering] instance based on the attribute value name and the ordering function. This might be useful if the ordering function has a non-trivial type, like the ones __boost_bind__ provides.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:exception_handlers Exception handlers]
|
||||
|
||||
#include <``[boost_log_utility_exception_handler_hpp]``>
|
||||
|
||||
The library provides exception handling hooks in different places. Tools, defined in this header, provide an easy way of implementing function objects suitable for such hooks.
|
||||
|
||||
An exception handler is a function object that accepts no arguments. The result of the exception handler is ignored and thus should generally be `void`. Exception handlers are called from within `catch` sections by the library, therefore in order to reacquire the exception object it has to rethrow it. The header defines an [class_log_exception_handler] template functor that does just that and then forwards the exception object to an unary user-defined functional object. The [funcref boost::log::make_exception_handler `make_exception_handler`] function can be used to simplify the handler construction. All expected exception types should be specified explicitly in the call, in the order they would appear in the `catch` sections (i.e. from most specific ones to the most general ones).
|
||||
|
||||
[example_utility_exception_handler]
|
||||
|
||||
As you can see, you can either suppress the exception by returning normally from `operator()` in the user-defined handler functor, or rethrow the exception, in which case it will propagate further. If it appears that the exception handler is invoked for an exception type that cannot be caught by any of the specified types, the exception will be propagated without any processing. In order to catch such situations, there exists the [class_log_nothrow_exception_handler] class. It invokes the user-defined functor with no arguments if it cannot determine the exception type.
|
||||
|
||||
[example_utility_exception_handler_nothrow]
|
||||
|
||||
It is sometimes convenient to completely suppress all exceptions at a certain library level. The [funcref boost::log::make_exception_suppressor `make_exception_suppressor`] function creates an exception handler that simply does nothing upon exception being caught. For example, this way we can disable all exceptions from the logging library:
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Disable all exceptions
|
||||
core->set_exception_handler(logging::make_exception_suppressor());
|
||||
}
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:manipulators Output manipulators]
|
||||
|
||||
The library provides a number of stream manipulators that may be useful in some contexts.
|
||||
|
||||
[section:to_log Customized logging manipulator]
|
||||
|
||||
#include <``[boost_log_utility_manipulators_to_log_hpp]``>
|
||||
|
||||
The [funcref boost::log::to_log `to_log`] function creates a stream manipulator that simply outputs the adopted value to the stream. By default its behavior is equivalent to simply putting the value to the stream. However, the user is able to overload the `operator<<` for the adopted value to override formatting behavior when values are formatted for logging purposes. This is typically desired when the regular `operator<<` is employed for other tasks (such as serialization) and its behavior is neither suitable for logging nor can be easily changed. For example:
|
||||
|
||||
[example_utility_manipulators_to_log]
|
||||
|
||||
The second streaming statement in the `test_manip` function will invoke our custom stream insertion operator which defines special formatting rules.
|
||||
|
||||
It is also possible to define different formatting rules for different value contexts as well. The library uses this feature to allow different formatting ruled for different attribute values, even if the stored value type is the same. To do so one has to specify an explicit template argument for [funcref boost::log::to_log `to_log`], a tag type, which will be embedded into the manipulator type and thus will allow to define different insertion operators:
|
||||
|
||||
[example_utility_manipulators_to_log_with_tag]
|
||||
|
||||
[@boost:/libs/log/example/doc/util_manip_to_log.cpp See the complete code].
|
||||
|
||||
[note The library uses [class_log_basic_formatting_ostream] stream type for record formatting, so when customizing attribute value formatting rules the `operator<<` must use [class_log_basic_formatting_ostream] instead of `std::ostream`.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:add_value Attribute value attaching manipulator]
|
||||
|
||||
#include <``[boost_log_utility_manipulators_add_value_hpp]``>
|
||||
|
||||
The [funcref boost::log::add_value `add_value`] function creates a manipulator that attaches an attribute value to a log record. This manipulator can only be used in streaming expressions with the [class_log_basic_record_ostream] stream type (which is the case when log record message is formatted). Since the message text is only formatted after filtering, attribute values attached with this manipulator do not affect filtering and can only be used in formatters and sinks themselves.
|
||||
|
||||
In addition to the value itself, the manipulator also requires the attribute name to be provided. For example:
|
||||
|
||||
// Creates a log record with attribute value "MyAttr" of type int attached
|
||||
BOOST_LOG(lg) << logging::add_value("MyAttr", 10) << "Hello world!";
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:setup Simplified library initialization tools]
|
||||
|
||||
This part of the library is provided in order to simplify logging initialization and provide basic tools to develop user-specific initialization mechanisms. It is known that setup capabilities and preferences may vary widely from application to application, therefore the library does not attempt to provide a universal solution for this task. The provided tools are mostly intended to serve as a quick drop-in support for logging setup and a set of instruments to implement something more elaborate and more fitting users' needs.
|
||||
|
||||
Some of the features described in this section will require the separate library binary, with name based on "boost_log_setup" substring. This binary depends on the main library.
|
||||
|
||||
[section:convenience Convenience functions]
|
||||
|
||||
#include <``[boost_log_utility_setup_console_hpp]``>
|
||||
#include <``[boost_log_utility_setup_file_hpp]``>
|
||||
#include <``[boost_log_utility_setup_common_attributes_hpp]``>
|
||||
|
||||
The library provides a number of functions that simplify some common initialization procedures, like sink and commonly used attributes registration. This is not much functionality. However, it saves a couple of minutes of learning the library for a newcomer.
|
||||
|
||||
Logging to the application console is the simplest way to see the logging library in action. To achieve this, one can initialize the library with a single function call, like this:
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
// Initialize logging to std::clog
|
||||
logging::add_console_log();
|
||||
|
||||
// Here we go, we can write logs right away
|
||||
src::logger lg;
|
||||
BOOST_LOG(lg) << "Hello world!";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Pretty easy, isn't it? There is also the `wadd_console_log` function for wide-character console. If you want to put logs to some other standard stream, you can pass the stream to the [funcref boost::log::add_console_log `add_console_log`] function as an argument. E.g. enabling logging to `std::cout` instead of `std::clog` would look like this:
|
||||
|
||||
logging::add_console_log(std::cout);
|
||||
|
||||
What's important, is that you can further manage the console sink if you save the `shared_ptr` to the sink that this function returns. This allows you to set up things like filter, formatter and auto-flush flag.
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
// Initialize logging to std::clog
|
||||
boost::shared_ptr<
|
||||
sinks::synchronous_sink< sinks::text_ostream_backend >
|
||||
> sink = logging::add_console_log();
|
||||
|
||||
sink->set_filter(expr::attr< int >("Severity") >= 3);
|
||||
sink->locked_backend()->auto_flush(true);
|
||||
|
||||
// Here we go, we can write logs right away
|
||||
src::logger lg;
|
||||
BOOST_LOG(lg) << "Hello world!";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Similarly to console, one can use a single function call to enable logging to a file. All you have to do is to provide the file name:
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
// Initialize logging to the "test.log" file
|
||||
logging::add_file_log("test.log");
|
||||
|
||||
// Here we go, we can write logs right away
|
||||
src::logger lg;
|
||||
BOOST_LOG(lg) << "Hello world!";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
The [funcref boost::log::add_console_log `add_console_log`] and [funcref boost::log::add_file_log `add_file_log`] functions do not conflict and may be combined freely, so it is possible to set up logging to the console and a couple of files, including filtering and formatting, in about 10 lines of code.
|
||||
|
||||
Lastly, there is an [funcref boost::log::add_common_attributes `add_common_attributes`] function that registers two frequently used attributes: "LineID" and "TimeStamp". The former counts log record being made and has attribute value `unsigned int`. The latter, as its name implies, provides the current time for each log record, in the form of `boost::posix_time::ptime` (see __boost_date_time__). These two attributes are registered globally, so they will remain available in all threads and loggers. This makes the final version of our code sample look something like this:
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
// Initialize sinks
|
||||
logging::add_console_log()->set_filter(expr::attr< int >("Severity") >= 4);
|
||||
|
||||
logging::formatter formatter =
|
||||
expr::stream
|
||||
<< expr::attr< unsigned int >("LineID") << ": "
|
||||
<< expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S") << " *"
|
||||
<< expr::attr< int >("Severity") << "* "
|
||||
<< expr::message;
|
||||
|
||||
logging::add_file_log("complete.log")->set_formatter(formatter);
|
||||
|
||||
boost::shared_ptr<
|
||||
sinks::synchronous_sink< sinks::text_ostream_backend >
|
||||
> sink = logging::add_file_log("essential.log");
|
||||
sink->set_formatter(formatter);
|
||||
sink->set_filter(expr::attr< int >("Severity") >= 1);
|
||||
|
||||
// Register common attributes
|
||||
logging::add_common_attributes();
|
||||
|
||||
// Here we go, we can write logs
|
||||
src::logger lg;
|
||||
BOOST_LOG(lg) << "Hello world!";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:filter_formatter Filter and formatter parsers]
|
||||
|
||||
#include <``[boost_log_utility_setup_filter_parser_hpp]``>
|
||||
#include <``[boost_log_utility_setup_formatter_parser_hpp]``>
|
||||
|
||||
Filter and formatter parsers allow constructing filters and formatters from a descriptive string. The function `parse_filter` is responsible for recognizing filters and `parse_formatter` - for recognizing formatters.
|
||||
|
||||
In the case of filters the string is formed of a sequence of condition expressions, interconnected with boolean operations. There are two operations supported: conjunction (designated as "&" or "and") and disjunction ("|" or "or"). Each condition itself may be either a single condition or a sub-filter, taken in round brackets. Each condition can be negated with the "!" sign or "not" keyword. The condition, if it's not a sub-filter, usually consists of an attribute name enclosed in percent characters ("%"), a relation keyword and an operand. The relation and operand may be omitted, in which case the condition is assumed to be the requirement of the attribute presence (with any type).
|
||||
|
||||
[teletype]
|
||||
|
||||
filter:
|
||||
condition { op condition }
|
||||
|
||||
op:
|
||||
&
|
||||
and
|
||||
|
|
||||
or
|
||||
|
||||
condition:
|
||||
!condition
|
||||
not condition
|
||||
(filter)
|
||||
%attribute_name%
|
||||
%attribute_name% relation operand
|
||||
|
||||
relation:
|
||||
>
|
||||
<
|
||||
=
|
||||
!=
|
||||
>=
|
||||
<=
|
||||
begins_with
|
||||
ends_with
|
||||
contains
|
||||
matches
|
||||
|
||||
[c++]
|
||||
|
||||
Below are some examples of filters:
|
||||
|
||||
[table Examples of filters
|
||||
[[Filter string] [Description]]
|
||||
[[`%Severity%`] [The filter returns `true` if an attribute value with name "Severity" is found in a log record.]]
|
||||
[[`%Severity% > 3`] [The filter returns `true` if an attribute value with name "Severity" is found and it is greater than 3. The attribute value must be of one of the [link log.detailed.utilities.predef_types integral types].]]
|
||||
[[!(`%Ratio% > 0.0 & %Ratio% <= 0.5)`] [The filter returns `true` if an attribute value with name "Ratio" of one of the [link log.detailed.utilities.predef_types floating point types] is not found or it is not between 0 and 0.5.]]
|
||||
[[`%Tag% contains "net" or %Tag% contains "io" and not %StatFlow%`] [The filter returns `true` if an attribute value with name "Tag" is found and contains words "net" or "io" and if an attribute value "StatFlow" is not found. The "Tag" attribute value must be of one of the [link log.detailed.utilities.predef_types string types], the "StatFlow" attribute value type is not considered.]]
|
||||
]
|
||||
|
||||
The formatter string syntax is even simpler and pretty much resembles __boost_format__ format string syntax. The string must contain attribute names enclosed in percent signs ("%"), the corresponding attribute value will replace these placeholders. The placeholder "%Message%" will be replaced with the log record text. For instance, `[%TimeStamp%] *%Severity%* %Message%` formatter string will make log records look like this: `[2008-07-05 13:44:23] *0* Hello world`.
|
||||
|
||||
[note Previous releases of the library also supported the "%\_%" placeholder for the message text. This placeholder is deprecated now, although it still works for backward compatibility. Its support will be removed in future releases.]
|
||||
|
||||
It must be noted, though, that by default the library only supports those attribute value types [link log.detailed.utilities.predef_types which are known] at the library build time. User-defined types will not work properly in parsed filters and formatters until registered in the library. More on this is available in the [link log.extension.settings Extending the library] section.
|
||||
|
||||
[note The parsed formatters and filters are generally less optimal than the equivalent ones written in code. This is because of two reasons: (\*) the programmer usually knows more about types of the attribute values that may be involved in formatting or filtering and (\*) the compiler has a better chance to optimize the formatter or filter if it is known in compile time. Therefore, if the performance matters, it is advised to avoid parsed filters and formatters.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:settings Library initialization from a settings container]
|
||||
|
||||
#include <``[boost_log_utility_setup_settings_hpp]``>
|
||||
#include <``[boost_log_utility_setup_from_settings_hpp]``>
|
||||
|
||||
The headers define components for library initialization from a settings container. The settings container is basically a set of named parameters divided into sections. The container is implemented with the [class_log_basic_settings] class template. There are several constraints on how parameters are stored in the container:
|
||||
|
||||
* Every parameter must reside in a section. There can be no parameters that do not belong to a section.
|
||||
* Parameters must have names unique within the section they belong to. Parameters from different sections may have the same name.
|
||||
* Sections can nest. When read from a file or accessed from the code, section names can express arbitrary hierarchy by separating the parent and child section names with '.' (e.g. "\[Parent.Child.ChildChild\]").
|
||||
* Sections must have names unique within the enclosing section (or global scope, if the section is top level).
|
||||
|
||||
So basically, settings container is a layered associative container, with string keys and values. In some respect it is similar to __boost_property_tree__, and in fact it supports construction from `boost::ptree`. The supported parameters are described below.
|
||||
|
||||
[tip In the tables below, the `CharT` type denotes the character type that is used with the settings container.]
|
||||
|
||||
[table Section "Core". Logging core settings.
|
||||
[[Parameter] [Format] [Description]]
|
||||
[[Filter] [Filter string as described [link log.detailed.utilities.setup.filter_formatter here]]
|
||||
[Global filter to be installed to the core. If not specified, the global filter is not set.]
|
||||
]
|
||||
[[DisableLogging] ["true" or "false"]
|
||||
[If `true`, results in calling `set_logging_enabled(false)` on the core. By default, value `false` is assumed.]
|
||||
]
|
||||
]
|
||||
|
||||
Sink settings are divided into separate subsections within the common top-level section "Sinks" - one subsection for each sink. The subsection names should denote a user-defined sink name. For example, "MyFile".
|
||||
|
||||
[note Previous versions of the library also supported top-level sections starting with the "Sink:" prefix to describe sink parameters. This syntax is deprecated now, although it still works when parsing a settings file for backward compatibility. The parser will automatically put these sections under the "Sinks" top-level section in the resulting settings contsiner. Support for this syntax will be removed in future releases.]
|
||||
|
||||
[table Sections under the "Sinks" section. Common sink settings.
|
||||
[[Parameter] [Format] [Description]]
|
||||
[[Destination] [Sink target, see description]
|
||||
[Sink backend type. Mandatory parameter. May have one of these values: [link log.detailed.sink_backends.text_ostream Console], [link log.detailed.sink_backends.text_file TextFile], [link log.detailed.sink_backends.syslog Syslog]. On Windows the following values are additionally supported: [link log.detailed.sink_backends.event_log SimpleEventLog], [link log.detailed.sink_backends.debugger Debugger]. Also, user-defined sink names may also be supported if registered by calling `register_sink_factory`.]
|
||||
]
|
||||
[[Filter] [Filter string as described [link log.detailed.utilities.setup.filter_formatter here]]
|
||||
[Sink-specific filter. If not specified, the filter is not set.]
|
||||
]
|
||||
[[Asynchronous] ["true" or "false"]
|
||||
[If `true`, the [link log.detailed.sink_frontends.async asynchronous sink frontend] will be used. Otherwise the [link log.detailed.sink_frontends.sync synchronous sink frontend] will be used. By default, value `false` is assumed. In single-threaded builds this parameter is not used, as [link log.detailed.sink_frontends.unlocked unlocked sink frontend] is always used.]
|
||||
]
|
||||
]
|
||||
|
||||
Besides the common settings that all sinks support, some sink backends also accept a number of specific parameters. These parameters should be specified in the same section.
|
||||
|
||||
[table "Console" sink settings
|
||||
[[Parameter] [Format] [Description]]
|
||||
[[Format] [Format string as described [link log.detailed.utilities.setup.filter_formatter here]]
|
||||
[Log record formatter to be used by the sink. If not specified, the default formatter is used.]
|
||||
]
|
||||
[[AutoFlush] ["true" or "false"]
|
||||
[Enables or disables the auto-flush feature of the backend. If not specified, the default value `false` is assumed.]
|
||||
]
|
||||
]
|
||||
|
||||
[table "TextFile" sink settings
|
||||
[[Parameter] [Format] [Description]]
|
||||
[[FileName] [File name pattern]
|
||||
[The file name pattern for the sink backend. This parameter is mandatory.]
|
||||
]
|
||||
[[Format] [Format string as described [link log.detailed.utilities.setup.filter_formatter here]]
|
||||
[Log record formatter to be used by the sink. If not specified, the default formatter is used.]
|
||||
]
|
||||
[[AutoFlush] ["true" or "false"]
|
||||
[Enables or disables the auto-flush feature of the backend. If not specified, the default value `false` is assumed.]
|
||||
]
|
||||
[[RotationSize] [Unsigned integer]
|
||||
[File size, in bytes, upon which file rotation will be performed. If not specified, no size-based rotation will be made.]
|
||||
]
|
||||
[[RotationInterval] [Unsigned integer]
|
||||
[Time interval, in seconds, upon which file rotation will be performed. See also the RotationTimePoint parameter and the note below.]
|
||||
]
|
||||
[[RotationTimePoint] [Time point format string, see below]
|
||||
[Time point or a predicate that detects at what moment of time to perform log file rotation. See also the RotationInterval parameter and the note below.]
|
||||
]
|
||||
[[Target] [File system path to a directory]
|
||||
[Target directory name, in which the rotated files will be stored. If this parameter is specified, rotated file collection is enabled. Otherwise the feature is not enabled, and all corresponding parameters are ignored.]
|
||||
]
|
||||
[[MaxSize] [Unsigned integer]
|
||||
[Total size of files in the target directory, in bytes, upon which the oldest file will be deleted. If not specified, no size-based file cleanup will be performed.]
|
||||
]
|
||||
[[MinFreeSpace] [Unsigned integer]
|
||||
[Minimum free space in the target directory, in bytes, upon which the oldest file will be deleted. If not specified, no space-based file cleanup will be performed.]
|
||||
]
|
||||
[[ScanForFiles] ["All" or "Matching"]
|
||||
[Mode of scanning for old files in the target directory, see [enumref boost::log::sinks::file::scan_method `scan_method`]. If not specified, no scanning will be performed.]
|
||||
]
|
||||
]
|
||||
|
||||
The time-based rotation can be set up with one of the two parameters: RotationInterval or RotationTimePoint. Not more than one of these parameters should be specified for a given sink. If none is specified, no time-based rotation will be performed.
|
||||
|
||||
The RotationTimePoint parameter should have one of the following formats, according to the __boost_date_time_format__ format notation:
|
||||
|
||||
* "%H:%M:%S". In this case, file rotation will be performed on a daily basis, at the specified time. For example, "12:00:00".
|
||||
* "%a %H:%M:%S" or "%A %H:%M:%S". File rotation takes place every week, on the weekday specified in the long or short form, at the specified time. For example, "Saturday 09:00:00".
|
||||
* "%d %H:%M:%S". File rotation takes place every month, on the specified day of month, at the specified time. For example, "01 23:30:00".
|
||||
|
||||
[table "Syslog" sink settings
|
||||
[[Parameter] [Format] [Description]]
|
||||
[[LocalAddress] [An IP address]
|
||||
[Local address to initiate connection to the syslog server. If not specified, the default local address will be used.]
|
||||
]
|
||||
[[TargetAddress] [An IP address]
|
||||
[Remote address of the syslog server. If not specified, the local address will be used.]
|
||||
]
|
||||
]
|
||||
|
||||
[table "SimpleEventLog" sink settings
|
||||
[[Parameter] [Format] [Description]]
|
||||
[[LogName] [A string]
|
||||
[Log name to write events into. If not specified, the default log name will be used.]
|
||||
]
|
||||
[[LogSource] [A string]
|
||||
[Log source to write events from. If not specified, the default source will be used.]
|
||||
]
|
||||
[[Registration] ["Never", "OnDemand" or "Forced"]
|
||||
[Mode of log source registration in Windows registry, see [enumref boost::log::sinks::event_log::registration_mode `registration_mode`]. If not specified, on-demand registration will be performed.]
|
||||
]
|
||||
]
|
||||
|
||||
The user is free to fill the settings container from whatever settings source he needs. The usage example is below:
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
logging::settings setts;
|
||||
|
||||
setts["Core"]["Filter"] = "%Severity% >= warning";
|
||||
setts["Core"]["DisableLogging"] = false;
|
||||
|
||||
// Subsections can be referred to with a single path
|
||||
setts["Sinks.Console"]["Destination"] = "Console";
|
||||
setts["Sinks.Console"]["Filter"] = "%Severity% >= fatal";
|
||||
setts["Sinks.Console"]["AutoFlush"] = true;
|
||||
|
||||
// ...as well as the individual parameters
|
||||
setts["Sinks.File.Destination"] = "TextFile";
|
||||
setts["Sinks.File.FileName"] = "MyApp_%3N.log";
|
||||
setts["Sinks.File.AutoFlush"] = true;
|
||||
setts["Sinks.File.RotationSize"] = 10 * 1024 * 1024; // 10 MiB
|
||||
|
||||
logging::init_from_settings(setts);
|
||||
}
|
||||
|
||||
The settings reader also allows to be extended to support custom sink types. See the [link log.extension.settings Extending the library] section for more information.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:settings_file Library initialization from a settings file]
|
||||
|
||||
#include <``[boost_log_utility_setup_from_stream_hpp]``>
|
||||
|
||||
Support for configuration files is a frequently requested feature of the library. And despite the fact there is no ultimately convenient and flexible format of the library settings, the library provides preliminary support for this feature. The functionality is implemented with a simple function [funcref boost::log::init_from_stream `init_from_stream`], which accepts an STL input stream and reads the library settings from it. The function then passes on the read settings to the [funcref boost::log::init_from_settings `init_from_settings`] function, described [link log.detailed.utilities.setup.settings above]. Therefore the parameter names and their meaning is the same as for the [funcref boost::log::init_from_settings `init_from_settings`] function.
|
||||
|
||||
The settings format is quite simple and widely used. Below is the description of syntax and parameters.
|
||||
|
||||
[teletype]
|
||||
|
||||
# Comments are allowed. Comment line begins with the '#' character
|
||||
# and spans until the end of the line.
|
||||
|
||||
# Logging core settings section. May be omitted if no parameters specified within it.
|
||||
[Core]
|
||||
DisableLogging=false
|
||||
Filter="%Severity% > 3"
|
||||
|
||||
# Sink settings sections
|
||||
[Sinks.MySink1]
|
||||
|
||||
# Sink destination type
|
||||
Destination=Console
|
||||
|
||||
# Sink-specific filter. Optional, by default no filter is applied.
|
||||
Filter="%Target% contains \"MySink1\""
|
||||
|
||||
# Formatter string. Optional, by default only log record message text is written.
|
||||
Format="<%TimeStamp%> - %Message%"
|
||||
|
||||
# The flag shows whether the sink should be asynchronous
|
||||
Asynchronous=false
|
||||
|
||||
# Enables automatic stream flush after each log record.
|
||||
AutoFlush=true
|
||||
|
||||
[c++]
|
||||
|
||||
Here's the usage example:
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
// Read logging settings from a file
|
||||
std::ifstream file("settings.ini");
|
||||
logging::init_from_stream(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
23
example/Jamfile.v2
Normal file
23
example/Jamfile.v2
Normal file
@ -0,0 +1,23 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
build-project ./advanced_usage ;
|
||||
build-project ./async_log ;
|
||||
build-project ./bounded_async_log ;
|
||||
build-project ./basic_usage ;
|
||||
build-project ./event_log ;
|
||||
build-project ./multiple_files ;
|
||||
build-project ./multiple_threads ;
|
||||
build-project ./rotating_file ;
|
||||
build-project ./settings_file ;
|
||||
build-project ./settings_file_formatter_factory ;
|
||||
build-project ./syslog ;
|
||||
build-project ./native_syslog ;
|
||||
build-project ./wide_char ;
|
||||
build-project ./keywords ;
|
||||
build-project ./trivial ;
|
||||
build-project ./doc ;
|
32
example/advanced_usage/Jamfile.v2
Normal file
32
example/advanced_usage/Jamfile.v2
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<library>/boost/thread//boost_thread
|
||||
<threading>multi
|
||||
;
|
||||
|
||||
exe advanced_usage
|
||||
: main.cpp
|
||||
;
|
300
example/advanced_usage/main.cpp
Normal file
300
example/advanced_usage/main.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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/shared_ptr.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/empty_deleter.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 simplier, 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, logging::empty_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;
|
||||
}
|
32
example/async_log/Jamfile.v2
Normal file
32
example/async_log/Jamfile.v2
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<library>/boost/thread//boost_thread
|
||||
<threading>multi
|
||||
;
|
||||
|
||||
exe async_log
|
||||
: main.cpp
|
||||
;
|
130
example/async_log/main.cpp
Normal file
130
example/async_log/main.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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 30.08.2009
|
||||
*
|
||||
* \brief An example of asynchronous logging in multiple threads.
|
||||
*/
|
||||
|
||||
// #define BOOST_LOG_DYN_LINK 1
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <boost/ref.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/thread/barrier.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/empty_deleter.hpp>
|
||||
#include <boost/log/utility/record_ordering.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
using boost::shared_ptr;
|
||||
|
||||
enum
|
||||
{
|
||||
LOG_RECORDS_TO_WRITE = 10000,
|
||||
THREAD_COUNT = 2
|
||||
};
|
||||
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::logger_mt)
|
||||
|
||||
//! This function is executed in multiple threads
|
||||
void thread_fun(boost::barrier& bar)
|
||||
{
|
||||
// Wait until all threads are created
|
||||
bar.wait();
|
||||
|
||||
// Here we go. First, identfy the thread.
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id());
|
||||
|
||||
// Now, do some logging
|
||||
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
|
||||
{
|
||||
BOOST_LOG(test_lg::get()) << "Log record " << i;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Open a rotating text file
|
||||
shared_ptr< std::ostream > strm(new std::ofstream("test.log"));
|
||||
if (!strm->good())
|
||||
throw std::runtime_error("Failed to open a text log file");
|
||||
|
||||
// Create a text file sink
|
||||
typedef sinks::text_ostream_backend backend_t;
|
||||
typedef sinks::asynchronous_sink<
|
||||
backend_t,
|
||||
sinks::unbounded_ordering_queue<
|
||||
logging::attribute_value_ordering< unsigned int, std::less< unsigned int > >
|
||||
>
|
||||
> sink_t;
|
||||
shared_ptr< sink_t > sink(new sink_t(
|
||||
boost::make_shared< backend_t >(),
|
||||
// We'll apply record ordering to ensure that records from different threads go sequentially in the file
|
||||
keywords::order = logging::make_attr_ordering("RecordID", std::less< unsigned int >())));
|
||||
|
||||
sink->locked_backend()->add_stream(strm);
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::format("%1%: [%2%] [%3%] - %4%")
|
||||
% expr::attr< unsigned int >("RecordID")
|
||||
% expr::attr< boost::posix_time::ptime >("TimeStamp")
|
||||
% expr::attr< boost::thread::id >("ThreadID")
|
||||
% expr::smessage
|
||||
);
|
||||
|
||||
// Add it to the core
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add some attributes too
|
||||
logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock());
|
||||
logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >());
|
||||
|
||||
// Create logging threads
|
||||
boost::barrier bar(THREAD_COUNT);
|
||||
boost::thread_group threads;
|
||||
for (unsigned int i = 0; i < THREAD_COUNT; ++i)
|
||||
threads.create_thread(boost::bind(&thread_fun, boost::ref(bar)));
|
||||
|
||||
// Wait until all action ends
|
||||
threads.join_all();
|
||||
|
||||
// Flush all buffered records
|
||||
sink->stop();
|
||||
sink->flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "FAILURE: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
33
example/basic_usage/Jamfile.v2
Normal file
33
example/basic_usage/Jamfile.v2
Normal file
@ -0,0 +1,33 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/log//boost_log_setup
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<threading>single:<define>BOOST_LOG_NO_THREADS
|
||||
<threading>multi:<library>/boost/thread//boost_thread
|
||||
;
|
||||
|
||||
exe basic_usage
|
||||
: main.cpp
|
||||
;
|
129
example/basic_usage/main.cpp
Normal file
129
example/basic_usage/main.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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 basic 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 <iostream>
|
||||
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
#include <boost/log/utility/setup/console.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
#include <boost/log/attributes/timer.hpp>
|
||||
#include <boost/log/attributes/named_scope.hpp>
|
||||
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
|
||||
#include <boost/log/support/date_time.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
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 main(int argc, char* argv[])
|
||||
{
|
||||
// This is a simple 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.
|
||||
logging::add_console_log(std::clog, keywords::format = "%TimeStamp%: %Message%");
|
||||
|
||||
// One can also use lambda expressions to setup filters and formatters
|
||||
logging::add_file_log
|
||||
(
|
||||
"sample.log",
|
||||
keywords::filter = expr::attr< severity_level >("Severity") >= warning,
|
||||
keywords::format = expr::stream
|
||||
<< expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d, %H:%M:%S.%f")
|
||||
<< " [" << expr::format_date_time< attrs::timer::value_type >("Uptime", "%O:%M:%S")
|
||||
<< "] [" << expr::format_named_scope("Scope", keywords::format = "%n (%f:%l)")
|
||||
<< "] <" << expr::attr< severity_level >("Severity")
|
||||
<< "> " << expr::message
|
||||
/*
|
||||
keywords::format = expr::format("%1% [%2%] [%3%] <%4%> %5%")
|
||||
% expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d, %H:%M:%S.%f")
|
||||
% expr::format_date_time< attrs::timer::value_type >("Uptime", "%O:%M:%S")
|
||||
% expr::format_named_scope("Scope", keywords::format = "%n (%f:%l)")
|
||||
% expr::attr< severity_level >("Severity")
|
||||
% expr::message
|
||||
*/
|
||||
);
|
||||
|
||||
// Also let's add some commonly used attributes, like timestamp and record counter.
|
||||
logging::add_common_attributes();
|
||||
logging::core::get()->add_thread_attribute("Scope", attrs::named_scope());
|
||||
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
// 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!";
|
||||
|
||||
// Now, let's try logging with severity
|
||||
src::severity_logger< severity_level > slg;
|
||||
|
||||
// Let's pretend we also want to profile our code, so add a special timer attribute.
|
||||
slg.add_attribute("Uptime", attrs::timer());
|
||||
|
||||
BOOST_LOG_SEV(slg, normal) << "A normal severity message, will not pass to the file";
|
||||
BOOST_LOG_SEV(slg, warning) << "A warning severity message, will pass to the file";
|
||||
BOOST_LOG_SEV(slg, error) << "An error severity message, will pass to the file";
|
||||
|
||||
return 0;
|
||||
}
|
32
example/bounded_async_log/Jamfile.v2
Normal file
32
example/bounded_async_log/Jamfile.v2
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<library>/boost/thread//boost_thread
|
||||
<threading>multi
|
||||
;
|
||||
|
||||
exe bounded_async_log
|
||||
: main.cpp
|
||||
;
|
132
example/bounded_async_log/main.cpp
Normal file
132
example/bounded_async_log/main.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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 30.08.2009
|
||||
*
|
||||
* \brief An example of asynchronous logging with bounded log record queue in multiple threads.
|
||||
*/
|
||||
|
||||
// #define BOOST_LOG_DYN_LINK 1
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <boost/ref.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/thread/barrier.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/empty_deleter.hpp>
|
||||
#include <boost/log/utility/record_ordering.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
using boost::shared_ptr;
|
||||
|
||||
enum
|
||||
{
|
||||
LOG_RECORDS_TO_WRITE = 10000,
|
||||
THREAD_COUNT = 2
|
||||
};
|
||||
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::logger_mt)
|
||||
|
||||
//! This function is executed in multiple threads
|
||||
void thread_fun(boost::barrier& bar)
|
||||
{
|
||||
// Wait until all threads are created
|
||||
bar.wait();
|
||||
|
||||
// Here we go. First, identfy the thread.
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id());
|
||||
|
||||
// Now, do some logging
|
||||
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
|
||||
{
|
||||
BOOST_LOG(test_lg::get()) << "Log record " << i;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Open a rotating text file
|
||||
shared_ptr< std::ostream > strm(new std::ofstream("test.log"));
|
||||
if (!strm->good())
|
||||
throw std::runtime_error("Failed to open a text log file");
|
||||
|
||||
// Create a text file sink
|
||||
typedef sinks::text_ostream_backend backend_t;
|
||||
typedef sinks::asynchronous_sink<
|
||||
backend_t,
|
||||
sinks::bounded_ordering_queue<
|
||||
logging::attribute_value_ordering< unsigned int, std::less< unsigned int > >,
|
||||
128, // queue no more than 128 log records
|
||||
sinks::block_on_overflow // wait until records are processed
|
||||
>
|
||||
> sink_t;
|
||||
shared_ptr< sink_t > sink(new sink_t(
|
||||
boost::make_shared< backend_t >(),
|
||||
// We'll apply record ordering to ensure that records from different threads go sequentially in the file
|
||||
keywords::order = logging::make_attr_ordering("RecordID", std::less< unsigned int >())));
|
||||
|
||||
sink->locked_backend()->add_stream(strm);
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::format("%1%: [%2%] [%3%] - %4%")
|
||||
% expr::attr< unsigned int >("RecordID")
|
||||
% expr::attr< boost::posix_time::ptime >("TimeStamp")
|
||||
% expr::attr< boost::thread::id >("ThreadID")
|
||||
% expr::smessage
|
||||
);
|
||||
|
||||
// Add it to the core
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add some attributes too
|
||||
logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock());
|
||||
logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >());
|
||||
|
||||
// Create logging threads
|
||||
boost::barrier bar(THREAD_COUNT);
|
||||
boost::thread_group threads;
|
||||
for (unsigned int i = 0; i < THREAD_COUNT; ++i)
|
||||
threads.create_thread(boost::bind(&thread_fun, boost::ref(bar)));
|
||||
|
||||
// Wait until all action ends
|
||||
threads.join_all();
|
||||
|
||||
// Flush all buffered records
|
||||
sink->stop();
|
||||
sink->flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "FAILURE: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
49
example/doc/Jamfile.v2
Normal file
49
example/doc/Jamfile.v2
Normal file
@ -0,0 +1,49 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
import path ;
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/log//boost_log_setup
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<threading>single:<define>BOOST_LOG_NO_THREADS
|
||||
<threading>multi:<library>/boost/thread//boost_thread
|
||||
;
|
||||
|
||||
# Compiles each .cpp file in this directory into a separate executable
|
||||
rule compile_all
|
||||
{
|
||||
#ECHO executing compile_all rule ;
|
||||
local all_rules = ;
|
||||
for local file in [ glob *.cpp ]
|
||||
{
|
||||
local exename = [ MATCH "([^.]*).cpp$" : [ path.basename $(file) ] ] ;
|
||||
#ECHO "exename = $(exename)" ;
|
||||
all_rules += [ exe $(exename) : $(file) ] ;
|
||||
}
|
||||
|
||||
#ECHO $(all_rules) ;
|
||||
return $(all_rules) ;
|
||||
}
|
||||
|
||||
compile_all ;
|
134
example/doc/attr_value_extraction.cpp
Normal file
134
example/doc/attr_value_extraction.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include <boost/mpl/vector.hpp>
|
||||
#include <boost/log/attributes/value_extraction.hpp>
|
||||
#include <boost/log/attributes/attribute_value_impl.hpp>
|
||||
#include <boost/log/utility/value_ref.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
|
||||
//[ example_attr_value_extraction
|
||||
void print_value(logging::attribute_value const& attr)
|
||||
{
|
||||
// Extract a reference to the stored value
|
||||
logging::value_ref< int > val = logging::extract< int >(attr);
|
||||
|
||||
// Check the result
|
||||
if (val)
|
||||
std::cout << "Extraction succeeded: " << val.get() << std::endl;
|
||||
else
|
||||
std::cout << "Extraction failed" << std::endl;
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_attr_value_extraction_multiple_types
|
||||
void print_value_multiple_types(logging::attribute_value const& attr)
|
||||
{
|
||||
// Define the set of expected types of the stored value
|
||||
typedef boost::mpl::vector< int, std::string > types;
|
||||
|
||||
// Extract a reference to the stored value
|
||||
logging::value_ref< types > val = logging::extract< types >(attr);
|
||||
|
||||
// Check the result
|
||||
if (val)
|
||||
{
|
||||
std::cout << "Extraction succeeded" << std::endl;
|
||||
switch (val.which())
|
||||
{
|
||||
case 0:
|
||||
std::cout << "int: " << val.get< int >() << std::endl;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
std::cout << "string: " << val.get< std::string >() << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
std::cout << "Extraction failed" << std::endl;
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_attr_value_extraction_visitor
|
||||
struct hash_visitor
|
||||
{
|
||||
typedef std::size_t result_type;
|
||||
|
||||
result_type operator() (int val) const
|
||||
{
|
||||
std::size_t h = val;
|
||||
h = (h << 15) + h;
|
||||
h ^= (h >> 6) + (h << 7);
|
||||
return h;
|
||||
}
|
||||
|
||||
result_type operator() (std::string const& val) const
|
||||
{
|
||||
std::size_t h = 0;
|
||||
for (std::string::const_iterator it = val.begin(), end = val.end(); it != end; ++it)
|
||||
h += *it;
|
||||
|
||||
h = (h << 15) + h;
|
||||
h ^= (h >> 6) + (h << 7);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
void hash_value(logging::attribute_value const& attr)
|
||||
{
|
||||
// Define the set of expected types of the stored value
|
||||
typedef boost::mpl::vector< int, std::string > types;
|
||||
|
||||
// Extract the stored value
|
||||
logging::value_ref< types > val = logging::extract< types >(attr);
|
||||
|
||||
// Check the result
|
||||
if (val)
|
||||
std::cout << "Extraction succeeded, hash value: " << val.apply_visitor(hash_visitor()) << std::endl;
|
||||
else
|
||||
std::cout << "Extraction failed" << std::endl;
|
||||
}
|
||||
//]
|
||||
|
||||
#if 0
|
||||
//[ example_attr_value_extraction_visitor_rec
|
||||
void hash_value(logging::record_view const& rec, logging::attribute_name name)
|
||||
{
|
||||
// Define the set of expected types of the stored value
|
||||
typedef boost::mpl::vector< int, std::string > types;
|
||||
|
||||
// Extract the stored value
|
||||
logging::value_ref< types > val = logging::extract< types >(name, rec);
|
||||
|
||||
// Check the result
|
||||
if (val)
|
||||
std::cout << "Extraction succeeded, hash value: " << val.apply_visitor(hash_visitor()) << std::endl;
|
||||
else
|
||||
std::cout << "Extraction failed" << std::endl;
|
||||
}
|
||||
//]
|
||||
#endif
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
print_value(attrs::make_attribute_value(10));
|
||||
|
||||
print_value_multiple_types(attrs::make_attribute_value(10));
|
||||
print_value_multiple_types(attrs::make_attribute_value(std::string("Hello")));
|
||||
|
||||
hash_value(attrs::make_attribute_value(10));
|
||||
hash_value(attrs::make_attribute_value(std::string("Hello")));
|
||||
|
||||
return 0;
|
||||
}
|
123
example/doc/attr_value_visitation.cpp
Normal file
123
example/doc/attr_value_visitation.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/mpl/vector.hpp>
|
||||
#include <boost/log/attributes/value_visitation.hpp>
|
||||
#include <boost/log/attributes/attribute_value_impl.hpp>
|
||||
#include <boost/log/utility/functional/save_result.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
|
||||
//[ example_attr_value_visitation
|
||||
// Our attribute value visitor
|
||||
struct print_visitor
|
||||
{
|
||||
typedef void result_type;
|
||||
|
||||
result_type operator() (int val) const
|
||||
{
|
||||
std::cout << "Visited value is int: " << val << std::endl;
|
||||
}
|
||||
|
||||
result_type operator() (std::string const& val) const
|
||||
{
|
||||
std::cout << "Visited value is string: " << val << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
void print_value(logging::attribute_value const& attr)
|
||||
{
|
||||
// Define the set of expected types of the stored value
|
||||
typedef boost::mpl::vector< int, std::string > types;
|
||||
|
||||
// Apply our visitor
|
||||
logging::visitation_result result = logging::visit< types >(attr, print_visitor());
|
||||
|
||||
// Check the result
|
||||
if (result)
|
||||
std::cout << "Visitation succeeded" << std::endl;
|
||||
else
|
||||
std::cout << "Visitation failed" << std::endl;
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_attr_value_visitation_with_retval
|
||||
struct hash_visitor
|
||||
{
|
||||
typedef std::size_t result_type;
|
||||
|
||||
result_type operator() (int val) const
|
||||
{
|
||||
std::size_t h = val;
|
||||
h = (h << 15) + h;
|
||||
h ^= (h >> 6) + (h << 7);
|
||||
return h;
|
||||
}
|
||||
|
||||
result_type operator() (std::string const& val) const
|
||||
{
|
||||
std::size_t h = 0;
|
||||
for (std::string::const_iterator it = val.begin(), end = val.end(); it != end; ++it)
|
||||
h += *it;
|
||||
|
||||
h = (h << 15) + h;
|
||||
h ^= (h >> 6) + (h << 7);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
void hash_value(logging::attribute_value const& attr)
|
||||
{
|
||||
// Define the set of expected types of the stored value
|
||||
typedef boost::mpl::vector< int, std::string > types;
|
||||
|
||||
// Apply our visitor
|
||||
std::size_t h = 0;
|
||||
logging::visitation_result result = logging::visit< types >(attr, logging::save_result(hash_visitor(), h));
|
||||
|
||||
// Check the result
|
||||
if (result)
|
||||
std::cout << "Visitation succeeded, hash value: " << h << std::endl;
|
||||
else
|
||||
std::cout << "Visitation failed" << std::endl;
|
||||
}
|
||||
//]
|
||||
|
||||
#if 0
|
||||
//[ example_attr_value_visitation_with_retval_rec
|
||||
void hash_value(logging::record_view const& rec, logging::attribute_name name)
|
||||
{
|
||||
// Define the set of expected types of the stored value
|
||||
typedef boost::mpl::vector< int, std::string > types;
|
||||
|
||||
// Apply our visitor
|
||||
std::size_t h = 0;
|
||||
logging::visitation_result result = logging::visit< types >(name, rec, logging::save_result(hash_visitor(), h));
|
||||
|
||||
// Check the result
|
||||
if (result)
|
||||
std::cout << "Visitation succeeded, hash value: " << h << std::endl;
|
||||
else
|
||||
std::cout << "Visitation failed" << std::endl;
|
||||
}
|
||||
//]
|
||||
#endif
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
print_value(attrs::make_attribute_value(10));
|
||||
print_value(attrs::make_attribute_value(std::string("Hello")));
|
||||
|
||||
hash_value(attrs::make_attribute_value(10));
|
||||
hash_value(attrs::make_attribute_value(std::string("Hello")));
|
||||
|
||||
return 0;
|
||||
}
|
40
example/doc/core_core_manual.cpp
Normal file
40
example/doc/core_core_manual.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/move/utility.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
|
||||
//[ example_core_core_manual_logging
|
||||
void logging_function(logging::attribute_set const& attrs)
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Attempt to open a log record
|
||||
logging::record rec = core->open_record(attrs);
|
||||
if (rec)
|
||||
{
|
||||
// Ok, the record is accepted. Compose the message now.
|
||||
logging::record_ostream strm(rec);
|
||||
strm << "Hello, World!";
|
||||
strm.flush();
|
||||
|
||||
// Deliver the record to the sinks.
|
||||
core->push_record(boost::move(rec));
|
||||
}
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
logging_function(logging::attribute_set());
|
||||
|
||||
return 0;
|
||||
}
|
122
example/doc/core_record.cpp
Normal file
122
example/doc/core_record.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes.hpp>
|
||||
#include <boost/log/utility/value_ref.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
// We define our own severity levels
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
notification,
|
||||
warning,
|
||||
error,
|
||||
critical
|
||||
};
|
||||
|
||||
// The operator puts a human-friendly representation of the severity level to the stream
|
||||
std::ostream& operator<< (std::ostream& strm, severity_level level)
|
||||
{
|
||||
static const char* strings[] =
|
||||
{
|
||||
"normal",
|
||||
"notification",
|
||||
"warning",
|
||||
"error",
|
||||
"critical"
|
||||
};
|
||||
|
||||
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
|
||||
strm << strings[level];
|
||||
else
|
||||
strm << static_cast< int >(level);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
//[ example_core_record_visitation_extraction
|
||||
//=enum severity_level { ... };
|
||||
//=std::ostream& operator<< (std::ostream& strm, severity_level level);
|
||||
|
||||
struct print_visitor
|
||||
{
|
||||
typedef void result_type;
|
||||
result_type operator() (severity_level level) const
|
||||
{
|
||||
std::cout << level << std::endl;
|
||||
};
|
||||
};
|
||||
|
||||
// Prints severity level through visitation API
|
||||
void print_severity_visitation(logging::record const& rec)
|
||||
{
|
||||
logging::visit< severity_level >("Severity", rec, print_visitor());
|
||||
}
|
||||
|
||||
// Prints severity level through extraction API
|
||||
void print_severity_extraction(logging::record const& rec)
|
||||
{
|
||||
logging::value_ref< severity_level > level = logging::extract< severity_level >("Severity", rec);
|
||||
std::cout << level << std::endl;
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_core_record_attr_value_lookup
|
||||
// Prints severity level by searching the attribute values
|
||||
void print_severity_lookup(logging::record const& rec)
|
||||
{
|
||||
logging::attribute_value_set const& values = rec.attribute_values();
|
||||
logging::attribute_value_set::const_iterator it = values.find("Severity");
|
||||
if (it != values.end())
|
||||
{
|
||||
logging::attribute_value const& value = it->second;
|
||||
|
||||
// A single attribute value can also be visited or extracted
|
||||
std::cout << value.extract< severity_level >() << std::endl;
|
||||
}
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_core_record_subscript
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
|
||||
|
||||
// Prints severity level by using the subscript opereator
|
||||
void print_severity_subscript(logging::record const& rec)
|
||||
{
|
||||
// Use the attribute keyword to communicate the name and type of the value
|
||||
logging::value_ref< severity_level, tag::severity > level = rec[severity];
|
||||
std::cout << level << std::endl;
|
||||
}
|
||||
//]
|
||||
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
logging::attribute_set attrs;
|
||||
attrs.insert("Severity", attrs::make_constant(notification));
|
||||
|
||||
logging::record rec = logging::core::get()->open_record(attrs);
|
||||
if (rec)
|
||||
{
|
||||
print_severity_visitation(rec);
|
||||
print_severity_extraction(rec);
|
||||
print_severity_lookup(rec);
|
||||
print_severity_subscript(rec);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
163
example/doc/exception_handling.cpp
Normal file
163
example/doc/exception_handling.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/thread/shared_mutex.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sources/basic_logger.hpp>
|
||||
#include <boost/log/sources/severity_feature.hpp>
|
||||
#include <boost/log/sources/exception_handler_feature.hpp>
|
||||
#include <boost/log/sources/features.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sources/global_logger_storage.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/attributes/scoped_attribute.hpp>
|
||||
#include <boost/log/utility/exception_handler.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sources_exception_handler
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
warning,
|
||||
error
|
||||
};
|
||||
|
||||
// A logger class that allows to intercept exceptions and supports severity level
|
||||
class my_logger_mt :
|
||||
public src::basic_composite_logger<
|
||||
char,
|
||||
my_logger_mt,
|
||||
src::multi_thread_model< boost::shared_mutex >,
|
||||
src::features<
|
||||
src::severity< severity_level >,
|
||||
src::exception_handler
|
||||
>
|
||||
>
|
||||
{
|
||||
BOOST_LOG_FORWARD_LOGGER_MEMBERS(my_logger_mt)
|
||||
};
|
||||
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(my_logger, my_logger_mt)
|
||||
{
|
||||
my_logger_mt lg;
|
||||
|
||||
// Set up exception handler: all exceptions that occur while
|
||||
// logging through this logger, will be suppressed
|
||||
lg.set_exception_handler(logging::make_exception_suppressor());
|
||||
|
||||
return lg;
|
||||
}
|
||||
|
||||
void logging_function()
|
||||
{
|
||||
// This will not throw
|
||||
BOOST_LOG_SEV(my_logger::get(), normal) << "Hello, world";
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_utility_exception_handler
|
||||
struct my_handler
|
||||
{
|
||||
typedef void result_type;
|
||||
|
||||
void operator() (std::runtime_error const& e) const
|
||||
{
|
||||
std::cout << "std::runtime_error: " << e.what() << std::endl;
|
||||
}
|
||||
void operator() (std::logic_error const& e) const
|
||||
{
|
||||
std::cout << "std::logic_error: " << e.what() << std::endl;
|
||||
throw;
|
||||
}
|
||||
};
|
||||
|
||||
void init_exception_handler()
|
||||
{
|
||||
// Setup a global exception handler that will call my_handler::operator()
|
||||
// for the specified exception types
|
||||
logging::core::get()->set_exception_handler(logging::make_exception_handler<
|
||||
std::runtime_error,
|
||||
std::logic_error
|
||||
>(my_handler()));
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_utility_exception_handler_nothrow
|
||||
struct my_handler_nothrow
|
||||
{
|
||||
typedef void result_type;
|
||||
|
||||
void operator() (std::runtime_error const& e) const
|
||||
{
|
||||
std::cout << "std::runtime_error: " << e.what() << std::endl;
|
||||
}
|
||||
void operator() (std::logic_error const& e) const
|
||||
{
|
||||
std::cout << "std::logic_error: " << e.what() << std::endl;
|
||||
throw;
|
||||
}
|
||||
void operator() () const
|
||||
{
|
||||
std::cout << "unknown exception" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
void init_exception_handler_nothrow()
|
||||
{
|
||||
// Setup a global exception handler that will call my_handler::operator()
|
||||
// for the specified exception types. Note the std::nothrow argument that
|
||||
// specifies that all other exceptions should also be passed to the functor.
|
||||
logging::core::get()->set_exception_handler(logging::make_exception_handler<
|
||||
std::runtime_error,
|
||||
std::logic_error
|
||||
>(my_handler_nothrow(), std::nothrow));
|
||||
}
|
||||
//]
|
||||
|
||||
void init()
|
||||
{
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("sample.log"));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< expr::attr< unsigned int >("LineID")
|
||||
<< ": <" << expr::attr< severity_level >("Severity")
|
||||
<< "> " << expr::smessage
|
||||
);
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
init_exception_handler();
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging_function();
|
||||
|
||||
return 0;
|
||||
}
|
119
example/doc/expressions_attr_fmt_tag.cpp
Normal file
119
example/doc/expressions_attr_fmt_tag.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/formatting_ostream.hpp>
|
||||
#include <boost/log/utility/manipulators/to_log.hpp>
|
||||
#include <boost/log/utility/setup/console.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_expressions_attr_formatter_stream_tag
|
||||
// We define our own severity levels
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
notification,
|
||||
warning,
|
||||
error,
|
||||
critical
|
||||
};
|
||||
|
||||
// The operator is used for regular stream formatting
|
||||
std::ostream& operator<< (std::ostream& strm, severity_level level)
|
||||
{
|
||||
static const char* strings[] =
|
||||
{
|
||||
"normal",
|
||||
"notification",
|
||||
"warning",
|
||||
"error",
|
||||
"critical"
|
||||
};
|
||||
|
||||
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
|
||||
strm << strings[level];
|
||||
else
|
||||
strm << static_cast< int >(level);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
// Attribute value tag type
|
||||
struct severity_tag;
|
||||
|
||||
// The operator is used when putting the severity level to log
|
||||
logging::formatting_ostream& operator<<
|
||||
(
|
||||
logging::formatting_ostream& strm,
|
||||
logging::to_log_manip< severity_level, severity_tag > const& manip
|
||||
)
|
||||
{
|
||||
static const char* strings[] =
|
||||
{
|
||||
"NORM",
|
||||
"NTFY",
|
||||
"WARN",
|
||||
"ERRR",
|
||||
"CRIT"
|
||||
};
|
||||
|
||||
severity_level level = manip.get();
|
||||
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
|
||||
strm << strings[level];
|
||||
else
|
||||
strm << static_cast< int >(level);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
logging::add_console_log
|
||||
(
|
||||
std::clog,
|
||||
// This makes the sink to write log records that look like this:
|
||||
// 1: <NORM> A normal severity message
|
||||
// 2: <ERRR> An error severity message
|
||||
keywords::format =
|
||||
(
|
||||
expr::stream
|
||||
<< expr::attr< unsigned int >("LineID")
|
||||
<< ": <" << expr::attr< severity_level, severity_tag >("Severity")
|
||||
<< "> " << expr::smessage
|
||||
)
|
||||
);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging::add_common_attributes();
|
||||
|
||||
src::severity_logger< severity_level > lg;
|
||||
|
||||
// These messages will be written with CAPS severity levels
|
||||
BOOST_LOG_SEV(lg, normal) << "A normal severity message";
|
||||
BOOST_LOG_SEV(lg, notification) << "A notification severity message";
|
||||
BOOST_LOG_SEV(lg, warning) << "A warning severity message";
|
||||
BOOST_LOG_SEV(lg, error) << "An error severity message";
|
||||
BOOST_LOG_SEV(lg, critical) << "A critical severity message";
|
||||
|
||||
// This line will still use lower-case severity levels
|
||||
std::cout << "The regular output still uses lower-case formatting: " << normal << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
109
example/doc/expressions_channel_severity_filter.cpp
Normal file
109
example/doc/expressions_channel_severity_filter.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/setup/console.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_expressions_channel_severity_filter
|
||||
// We define our own severity levels
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
notification,
|
||||
warning,
|
||||
error,
|
||||
critical
|
||||
};
|
||||
|
||||
// Define the attribute keywords
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)
|
||||
|
||||
//<-
|
||||
std::ostream& operator<< (std::ostream& strm, severity_level level)
|
||||
{
|
||||
static const char* strings[] =
|
||||
{
|
||||
"normal",
|
||||
"notification",
|
||||
"warning",
|
||||
"error",
|
||||
"critical"
|
||||
};
|
||||
|
||||
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
|
||||
strm << strings[level];
|
||||
else
|
||||
strm << static_cast< int >(level);
|
||||
|
||||
return strm;
|
||||
}
|
||||
//->
|
||||
|
||||
void init()
|
||||
{
|
||||
// Create a minimal severity table filter
|
||||
typedef expr::channel_severity_filter_actor< std::string, severity_level > min_severity_filter;
|
||||
min_severity_filter min_severity = expr::channel_severity_filter(channel, severity);
|
||||
|
||||
// Set up the minimum severity levels for different channels
|
||||
min_severity["general"] = notification;
|
||||
min_severity["network"] = warning;
|
||||
min_severity["gui"] = error;
|
||||
|
||||
logging::add_console_log
|
||||
(
|
||||
std::clog,
|
||||
keywords::filter = min_severity || severity >= critical,
|
||||
keywords::format =
|
||||
(
|
||||
expr::stream
|
||||
<< line_id
|
||||
<< ": <" << severity
|
||||
<< "> [" << channel << "] "
|
||||
<< expr::smessage
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Define our logger type
|
||||
typedef src::severity_channel_logger< severity_level, std::string > logger_type;
|
||||
|
||||
void test_logging(logger_type& lg, std::string const& channel_name)
|
||||
{
|
||||
BOOST_LOG_CHANNEL_SEV(lg, channel_name, normal) << "A normal severity level message";
|
||||
BOOST_LOG_CHANNEL_SEV(lg, channel_name, notification) << "A notification severity level message";
|
||||
BOOST_LOG_CHANNEL_SEV(lg, channel_name, warning) << "A warning severity level message";
|
||||
BOOST_LOG_CHANNEL_SEV(lg, channel_name, error) << "An error severity level message";
|
||||
BOOST_LOG_CHANNEL_SEV(lg, channel_name, critical) << "A critical severity level message";
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging::add_common_attributes();
|
||||
|
||||
logger_type lg;
|
||||
test_logging(lg, "general");
|
||||
test_logging(lg, "network");
|
||||
test_logging(lg, "gui");
|
||||
test_logging(lg, "filesystem");
|
||||
|
||||
return 0;
|
||||
}
|
133
example/doc/expressions_has_attr_stat_accum.cpp
Normal file
133
example/doc/expressions_has_attr_stat_accum.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/log/sinks.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes/scoped_attribute.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/value_ref.hpp>
|
||||
#include <boost/log/utility/manipulators/add_value.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_expressions_has_attr_stat_accumulator
|
||||
// Declare attribute keywords
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(stat_stream, "StatisticStream", std::string)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(change, "Change", int)
|
||||
|
||||
// A simple sink backend to accumulate statistic information
|
||||
class my_stat_accumulator :
|
||||
public sinks::basic_sink_backend< sinks::synchronized_feeding >
|
||||
{
|
||||
// A map of accumulated statistic values,
|
||||
// ordered by the statistic information stream name
|
||||
typedef std::map< std::string, int > stat_info_map;
|
||||
stat_info_map m_stat_info;
|
||||
|
||||
public:
|
||||
// Destructor
|
||||
~my_stat_accumulator()
|
||||
{
|
||||
// Display the accumulated data
|
||||
stat_info_map::const_iterator it = m_stat_info.begin(), end = m_stat_info.end();
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
std::cout << "Statistic stream: " << it->first
|
||||
<< ", accumulated value: " << it->second << "\n";
|
||||
}
|
||||
std::cout.flush();
|
||||
}
|
||||
|
||||
// The method is called for every log record being put into the sink backend
|
||||
void consume(logging::record_view const& rec)
|
||||
{
|
||||
// First, acquire statistic information stream name
|
||||
logging::value_ref< std::string, tag::stat_stream > name = rec[stat_stream];
|
||||
if (name)
|
||||
{
|
||||
// Next, get the statistic value change
|
||||
logging::value_ref< int, tag::change > change_amount = rec[change];
|
||||
if (change_amount)
|
||||
{
|
||||
// Accumulate the statistic data
|
||||
m_stat_info[name.get()] += change_amount.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The function registers two sinks - one for statistic information,
|
||||
// and another one for other records
|
||||
void init()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Create a backend and attach a stream to it
|
||||
boost::shared_ptr< sinks::text_ostream_backend > backend =
|
||||
boost::make_shared< sinks::text_ostream_backend >();
|
||||
backend->add_stream(
|
||||
boost::shared_ptr< std::ostream >(new std::ofstream("test.log")));
|
||||
|
||||
// Create a frontend and setup filtering
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > log_sink_type;
|
||||
boost::shared_ptr< log_sink_type > log_sink(new log_sink_type(backend));
|
||||
// All records that don't have a "StatisticStream" attribute attached
|
||||
// will go to the "test.log" file
|
||||
log_sink->set_filter(!expr::has_attr(stat_stream));
|
||||
|
||||
core->add_sink(log_sink);
|
||||
|
||||
// Create another sink that will receive all statistic data
|
||||
typedef sinks::synchronous_sink< my_stat_accumulator > stat_sink_type;
|
||||
boost::shared_ptr< stat_sink_type > stat_sink(new stat_sink_type());
|
||||
// All records with a "StatisticStream" string attribute attached
|
||||
// will go to the my_stat_accumulator sink
|
||||
stat_sink->set_filter(expr::has_attr(stat_stream));
|
||||
|
||||
core->add_sink(stat_sink);
|
||||
}
|
||||
|
||||
// This simple macro will simplify putting statistic data into a logger
|
||||
#define PUT_STAT(lg, stat_stream_name, change)\
|
||||
if (true) {\
|
||||
BOOST_LOG_SCOPED_LOGGER_TAG(lg, "StatisticStream", stat_stream_name);\
|
||||
BOOST_LOG(lg) << logging::add_value("Change", (int)(change));\
|
||||
} else ((void)0)
|
||||
|
||||
void logging_function()
|
||||
{
|
||||
src::logger lg;
|
||||
|
||||
// Put a regular log record, it will go to the "test.log" file
|
||||
BOOST_LOG(lg) << "A regular log record";
|
||||
|
||||
// Put some statistic data
|
||||
PUT_STAT(lg, "StreamOne", 10);
|
||||
PUT_STAT(lg, "StreamTwo", 20);
|
||||
PUT_STAT(lg, "StreamOne", -5);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging_function();
|
||||
|
||||
return 0;
|
||||
}
|
129
example/doc/expressions_keyword_fmt_tag.cpp
Normal file
129
example/doc/expressions_keyword_fmt_tag.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/formatting_ostream.hpp>
|
||||
#include <boost/log/utility/manipulators/to_log.hpp>
|
||||
#include <boost/log/utility/setup/console.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_expressions_keyword_formatter_stream_tag
|
||||
// We define our own severity levels
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
notification,
|
||||
warning,
|
||||
error,
|
||||
critical
|
||||
};
|
||||
|
||||
// Define the attribute keywords
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
|
||||
|
||||
// The operator is used for regular stream formatting
|
||||
std::ostream& operator<< (std::ostream& strm, severity_level level)
|
||||
{
|
||||
static const char* strings[] =
|
||||
{
|
||||
"normal",
|
||||
"notification",
|
||||
"warning",
|
||||
"error",
|
||||
"critical"
|
||||
};
|
||||
|
||||
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
|
||||
strm << strings[level];
|
||||
else
|
||||
strm << static_cast< int >(level);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
// The operator is used when putting the severity level to log
|
||||
logging::formatting_ostream& operator<<
|
||||
(
|
||||
logging::formatting_ostream& strm,
|
||||
logging::to_log_manip< severity_level, tag::severity > const& manip
|
||||
)
|
||||
{
|
||||
static const char* strings[] =
|
||||
{
|
||||
"NORM",
|
||||
"NTFY",
|
||||
"WARN",
|
||||
"ERRR",
|
||||
"CRIT"
|
||||
};
|
||||
|
||||
severity_level level = manip.get();
|
||||
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
|
||||
strm << strings[level];
|
||||
else
|
||||
strm << static_cast< int >(level);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
logging::add_console_log
|
||||
(
|
||||
std::clog,
|
||||
// This makes the sink to write log records that look like this:
|
||||
// 1: <NORM> A normal severity message
|
||||
// 2: <ERRR> An error severity message
|
||||
keywords::format =
|
||||
(
|
||||
expr::stream
|
||||
<< line_id
|
||||
<< ": <" << severity
|
||||
<< "> " << expr::smessage
|
||||
)
|
||||
);
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_expressions_keyword_lookup
|
||||
void print_severity(logging::record_view const& rec)
|
||||
{
|
||||
logging::value_ref< severity_level, tag::severity > level = rec[severity];
|
||||
std::cout << level << std::endl;
|
||||
}
|
||||
//]
|
||||
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging::add_common_attributes();
|
||||
|
||||
src::severity_logger< severity_level > lg;
|
||||
|
||||
// These messages will be written with CAPS severity levels
|
||||
BOOST_LOG_SEV(lg, normal) << "A normal severity message";
|
||||
BOOST_LOG_SEV(lg, notification) << "A notification severity message";
|
||||
BOOST_LOG_SEV(lg, warning) << "A warning severity message";
|
||||
BOOST_LOG_SEV(lg, error) << "An error severity message";
|
||||
BOOST_LOG_SEV(lg, critical) << "A critical severity message";
|
||||
|
||||
// This line will still use lower-case severity levels
|
||||
std::cout << "The regular output still uses lower-case formatting: " << normal << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
123
example/doc/extension_app_launcher.cpp
Normal file
123
example/doc/extension_app_launcher.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/phoenix.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes/current_process_name.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/basic_sink_backend.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/value_ref.hpp>
|
||||
#include <boost/log/utility/formatting_ostream.hpp>
|
||||
#include <boost/log/utility/manipulators/add_value.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_extension_app_launcher_definition
|
||||
// The backend starts an external application to display notifications
|
||||
class app_launcher :
|
||||
public sinks::basic_formatted_sink_backend<
|
||||
char, /*< target character type >*/
|
||||
sinks::synchronized_feeding /*< in order not to spawn too many application instances we require records to be processed serial >*/
|
||||
>
|
||||
{
|
||||
public:
|
||||
// The function consumes the log records that come from the frontend
|
||||
void consume(logging::record_view const& rec, string_type const& command_line);
|
||||
};
|
||||
//]
|
||||
|
||||
//[ example_extension_app_launcher_consume
|
||||
// The function consumes the log records that come from the frontend
|
||||
void app_launcher::consume(logging::record_view const& rec, string_type const& command_line)
|
||||
{
|
||||
std::system(command_line.c_str());
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_extension_app_launcher_formatting
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(process_name, "ProcessName", std::string)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(caption, "Caption", std::string)
|
||||
|
||||
// Custom severity level formatting function
|
||||
std::string severity_level_as_urgency(
|
||||
logging::value_ref< logging::trivial::severity_level, logging::trivial::tag::severity > const& level)
|
||||
{
|
||||
if (!level || level.get() == logging::trivial::info)
|
||||
return "normal";
|
||||
logging::trivial::severity_level lvl = level.get();
|
||||
if (lvl < logging::trivial::info)
|
||||
return "low";
|
||||
else
|
||||
return "critical";
|
||||
}
|
||||
|
||||
// The function initializes the logging library
|
||||
void init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
typedef sinks::synchronous_sink< app_launcher > sink_t;
|
||||
boost::shared_ptr< sink_t > sink(new sink_t());
|
||||
|
||||
const std::pair< const char*, const char* > shell_decorations[] =
|
||||
{
|
||||
std::pair< const char*, const char* >("\"", "\\\""),
|
||||
std::pair< const char*, const char* >("$", "\\$"),
|
||||
std::pair< const char*, const char* >("!", "\\!")
|
||||
};
|
||||
|
||||
// Make the formatter generate the command line for notify-send
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream << "notify-send -t 2000 -u "
|
||||
<< boost::phoenix::bind(&severity_level_as_urgency, logging::trivial::severity.or_none())
|
||||
<< expr::if_(expr::has_attr(process_name))
|
||||
[
|
||||
expr::stream << " -a '" << process_name << "'"
|
||||
]
|
||||
<< expr::if_(expr::has_attr(caption))
|
||||
[
|
||||
expr::stream << " \"" << expr::char_decor(shell_decorations)[ expr::stream << caption ] << "\""
|
||||
]
|
||||
<< " \"" << expr::char_decor(shell_decorations)[ expr::stream << expr::message ] << "\""
|
||||
);
|
||||
|
||||
core->add_sink(sink);
|
||||
|
||||
// Add attributes that we will use
|
||||
core->add_global_attribute("ProcessName", attrs::current_process_name());
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_extension_app_launcher_logging
|
||||
void test_notifications()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "Hello, it's a simple notification";
|
||||
BOOST_LOG_TRIVIAL(info) << logging::add_value(caption, "Caption text") << "And this notification has caption as well";
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
test_notifications();
|
||||
|
||||
return 0;
|
||||
}
|
162
example/doc/extension_filter_parser.cpp
Normal file
162
example/doc/extension_filter_parser.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/phoenix.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes/attribute_name.hpp>
|
||||
#include <boost/log/attributes/scoped_attribute.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/value_ref.hpp>
|
||||
#include <boost/log/utility/formatting_ostream.hpp>
|
||||
#include <boost/log/utility/manipulators/add_value.hpp>
|
||||
#include <boost/log/utility/setup/filter_parser.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/utility/setup/console.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_extension_filter_parser_point_definition
|
||||
struct point
|
||||
{
|
||||
float m_x, m_y;
|
||||
|
||||
point() : m_x(0.0f), m_y(0.0f) {}
|
||||
point(float x, float y) : m_x(x), m_y(y) {}
|
||||
};
|
||||
|
||||
bool operator== (point const& left, point const& right);
|
||||
bool operator!= (point const& left, point const& right);
|
||||
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, point const& p);
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_istream< CharT, TraitsT >& operator>> (std::basic_istream< CharT, TraitsT >& strm, point& p);
|
||||
//]
|
||||
|
||||
const float epsilon = 0.0001f;
|
||||
|
||||
bool operator== (point const& left, point const& right)
|
||||
{
|
||||
return (left.m_x - epsilon <= right.m_x && left.m_x + epsilon >= right.m_x) &&
|
||||
(left.m_y - epsilon <= right.m_y && left.m_y + epsilon >= right.m_y);
|
||||
}
|
||||
|
||||
bool operator!= (point const& left, point const& right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, point const& p)
|
||||
{
|
||||
if (strm.good())
|
||||
strm << "(" << p.m_x << ", " << p.m_y << ")";
|
||||
return strm;
|
||||
}
|
||||
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_istream< CharT, TraitsT >& operator>> (std::basic_istream< CharT, TraitsT >& strm, point& p)
|
||||
{
|
||||
if (strm.good())
|
||||
{
|
||||
CharT left_brace = static_cast< CharT >(0), comma = static_cast< CharT >(0), right_brace = static_cast< CharT >(0);
|
||||
strm.setf(std::ios_base::skipws);
|
||||
strm >> left_brace >> p.m_x >> comma >> p.m_y >> right_brace;
|
||||
if (left_brace != '(' || comma != ',' || right_brace != ')')
|
||||
strm.setstate(std::ios_base::failbit);
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//[ example_extension_simple_filter_factory
|
||||
void init_factories()
|
||||
{
|
||||
//<-
|
||||
logging::register_simple_formatter_factory< point, char >("Coordinates");
|
||||
//->
|
||||
logging::register_simple_filter_factory< point, char >("Coordinates");
|
||||
}
|
||||
//]
|
||||
#endif
|
||||
|
||||
//[ example_extension_custom_filter_factory
|
||||
// Custom point filter factory
|
||||
class point_filter_factory :
|
||||
public logging::filter_factory< char >
|
||||
{
|
||||
public:
|
||||
logging::filter on_exists_test(logging::attribute_name const& name)
|
||||
{
|
||||
return expr::has_attr< point >(name);
|
||||
}
|
||||
|
||||
logging::filter on_equality_relation(logging::attribute_name const& name, string_type const& arg)
|
||||
{
|
||||
return expr::attr< point >(name) == boost::lexical_cast< point >(arg);
|
||||
}
|
||||
|
||||
logging::filter on_inequality_relation(logging::attribute_name const& name, string_type const& arg)
|
||||
{
|
||||
return expr::attr< point >(name) != boost::lexical_cast< point >(arg);
|
||||
}
|
||||
};
|
||||
|
||||
void init_factories()
|
||||
{
|
||||
//<-
|
||||
logging::register_simple_formatter_factory< point, char >("Coordinates");
|
||||
//->
|
||||
logging::register_filter_factory("Coordinates", boost::make_shared< point_filter_factory >());
|
||||
}
|
||||
//]
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
init_factories();
|
||||
|
||||
logging::add_console_log
|
||||
(
|
||||
std::clog,
|
||||
keywords::filter = "%Coordinates% = \"(10, 10)\"",
|
||||
keywords::format = "%TimeStamp% %Coordinates% %Message%"
|
||||
);
|
||||
|
||||
logging::add_common_attributes();
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
src::logger lg;
|
||||
|
||||
// We have to use scoped attributes in order coordinates to be passed to filters
|
||||
{
|
||||
BOOST_LOG_SCOPED_LOGGER_TAG(lg, "Coordinates", point(10, 10));
|
||||
BOOST_LOG(lg) << "Hello, world with coordinates (10, 10)!";
|
||||
}
|
||||
{
|
||||
BOOST_LOG_SCOPED_LOGGER_TAG(lg, "Coordinates", point(20, 20));
|
||||
BOOST_LOG(lg) << "Hello, world with coordinates (20, 20)!"; // this message will be suppressed by filter
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
203
example/doc/extension_filter_parser_custom_rel.cpp
Normal file
203
example/doc/extension_filter_parser_custom_rel.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/phoenix.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes/attribute_name.hpp>
|
||||
#include <boost/log/attributes/scoped_attribute.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/value_ref.hpp>
|
||||
#include <boost/log/utility/formatting_ostream.hpp>
|
||||
#include <boost/log/utility/manipulators/add_value.hpp>
|
||||
#include <boost/log/utility/setup/filter_parser.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/utility/setup/console.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
struct point
|
||||
{
|
||||
float m_x, m_y;
|
||||
|
||||
point() : m_x(0.0f), m_y(0.0f) {}
|
||||
point(float x, float y) : m_x(x), m_y(y) {}
|
||||
};
|
||||
|
||||
bool operator== (point const& left, point const& right);
|
||||
bool operator!= (point const& left, point const& right);
|
||||
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, point const& p);
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_istream< CharT, TraitsT >& operator>> (std::basic_istream< CharT, TraitsT >& strm, point& p);
|
||||
|
||||
const float epsilon = 0.0001f;
|
||||
|
||||
bool operator== (point const& left, point const& right)
|
||||
{
|
||||
return (left.m_x - epsilon <= right.m_x && left.m_x + epsilon >= right.m_x) &&
|
||||
(left.m_y - epsilon <= right.m_y && left.m_y + epsilon >= right.m_y);
|
||||
}
|
||||
|
||||
bool operator!= (point const& left, point const& right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, point const& p)
|
||||
{
|
||||
if (strm.good())
|
||||
strm << "(" << p.m_x << ", " << p.m_y << ")";
|
||||
return strm;
|
||||
}
|
||||
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_istream< CharT, TraitsT >& operator>> (std::basic_istream< CharT, TraitsT >& strm, point& p)
|
||||
{
|
||||
if (strm.good())
|
||||
{
|
||||
CharT left_brace = static_cast< CharT >(0), comma = static_cast< CharT >(0), right_brace = static_cast< CharT >(0);
|
||||
strm.setf(std::ios_base::skipws);
|
||||
strm >> left_brace >> p.m_x >> comma >> p.m_y >> right_brace;
|
||||
if (left_brace != '(' || comma != ',' || right_brace != ')')
|
||||
strm.setstate(std::ios_base::failbit);
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
//[ example_extension_filter_parser_rectangle_definition
|
||||
struct rectangle
|
||||
{
|
||||
point m_top_left, m_bottom_right;
|
||||
};
|
||||
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, rectangle const& r);
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_istream< CharT, TraitsT >& operator>> (std::basic_istream< CharT, TraitsT >& strm, rectangle& r);
|
||||
//]
|
||||
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, rectangle const& r)
|
||||
{
|
||||
if (strm.good())
|
||||
strm << "{" << r.m_top_left << " - " << r.m_bottom_right << "}";
|
||||
return strm;
|
||||
}
|
||||
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_istream< CharT, TraitsT >& operator>> (std::basic_istream< CharT, TraitsT >& strm, rectangle& r)
|
||||
{
|
||||
if (strm.good())
|
||||
{
|
||||
CharT left_brace = static_cast< CharT >(0), dash = static_cast< CharT >(0), right_brace = static_cast< CharT >(0);
|
||||
strm.setf(std::ios_base::skipws);
|
||||
strm >> left_brace >> r.m_top_left >> dash >> r.m_bottom_right >> right_brace;
|
||||
if (left_brace != '{' || dash != '-' || right_brace != '}')
|
||||
strm.setstate(std::ios_base::failbit);
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
|
||||
//[ example_extension_custom_filter_factory_with_custom_rel
|
||||
// The function checks if the point is inside the rectangle
|
||||
bool is_in_rect(logging::value_ref< point > const& p, rectangle const& r)
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
return p->m_x >= r.m_top_left.m_x && p->m_x <= r.m_bottom_right.m_x &&
|
||||
p->m_y >= r.m_top_left.m_y && p->m_y <= r.m_bottom_right.m_y;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Custom point filter factory
|
||||
class point_filter_factory :
|
||||
public logging::filter_factory< char >
|
||||
{
|
||||
public:
|
||||
logging::filter on_exists_test(logging::attribute_name const& name)
|
||||
{
|
||||
return expr::has_attr< point >(name);
|
||||
}
|
||||
|
||||
logging::filter on_equality_relation(logging::attribute_name const& name, string_type const& arg)
|
||||
{
|
||||
return expr::attr< point >(name) == boost::lexical_cast< point >(arg);
|
||||
}
|
||||
|
||||
logging::filter on_inequality_relation(logging::attribute_name const& name, string_type const& arg)
|
||||
{
|
||||
return expr::attr< point >(name) != boost::lexical_cast< point >(arg);
|
||||
}
|
||||
|
||||
logging::filter on_custom_relation(logging::attribute_name const& name, string_type const& rel, string_type const& arg)
|
||||
{
|
||||
if (rel == "is_in_rect")
|
||||
{
|
||||
return boost::phoenix::bind(&is_in_rect, expr::attr< point >(name), boost::lexical_cast< rectangle >(arg));
|
||||
}
|
||||
throw std::runtime_error("Unsupported filter relation: " + rel);
|
||||
}
|
||||
};
|
||||
|
||||
void init_factories()
|
||||
{
|
||||
//<-
|
||||
logging::register_simple_formatter_factory< point, char >("Coordinates");
|
||||
//->
|
||||
logging::register_filter_factory("Coordinates", boost::make_shared< point_filter_factory >());
|
||||
}
|
||||
//]
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
init_factories();
|
||||
|
||||
logging::add_console_log
|
||||
(
|
||||
std::clog,
|
||||
keywords::filter = "%Coordinates% is_in_rect \"{(0, 0) - (20, 20)}\"",
|
||||
keywords::format = "%TimeStamp% %Coordinates% %Message%"
|
||||
);
|
||||
|
||||
logging::add_common_attributes();
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
src::logger lg;
|
||||
|
||||
// We have to use scoped attributes in order coordinates to be passed to filters
|
||||
{
|
||||
BOOST_LOG_SCOPED_LOGGER_TAG(lg, "Coordinates", point(10, 10));
|
||||
BOOST_LOG(lg) << "Hello, world with coordinates (10, 10)!";
|
||||
}
|
||||
{
|
||||
BOOST_LOG_SCOPED_LOGGER_TAG(lg, "Coordinates", point(50, 50));
|
||||
BOOST_LOG(lg) << "Hello, world with coordinates (50, 50)!"; // this message will be suppressed by filter
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
125
example/doc/extension_formatter_parser.cpp
Normal file
125
example/doc/extension_formatter_parser.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/phoenix.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes/attribute_name.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/value_ref.hpp>
|
||||
#include <boost/log/utility/formatting_ostream.hpp>
|
||||
#include <boost/log/utility/manipulators/add_value.hpp>
|
||||
#include <boost/log/utility/setup/formatter_parser.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/utility/setup/console.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_extension_formatter_parser_point_definition
|
||||
struct point
|
||||
{
|
||||
float m_x, m_y;
|
||||
|
||||
point() : m_x(0.0f), m_y(0.0f) {}
|
||||
point(float x, float y) : m_x(x), m_y(y) {}
|
||||
};
|
||||
|
||||
template< typename CharT, typename TraitsT >
|
||||
std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, point const& p)
|
||||
{
|
||||
strm << "(" << p.m_x << ", " << p.m_y << ")";
|
||||
return strm;
|
||||
}
|
||||
//]
|
||||
|
||||
#if 0
|
||||
//[ example_extension_simple_formatter_factory
|
||||
void init_factories()
|
||||
{
|
||||
logging::register_simple_formatter_factory< point, char >("Coordinates");
|
||||
}
|
||||
//]
|
||||
#endif
|
||||
|
||||
//[ example_extension_custom_formatter_factory
|
||||
// Custom point formatter
|
||||
class point_formatter
|
||||
{
|
||||
public:
|
||||
typedef void result_type;
|
||||
|
||||
public:
|
||||
explicit point_formatter(std::string const& fmt) : m_format(fmt)
|
||||
{
|
||||
}
|
||||
|
||||
void operator() (logging::formatting_ostream& strm, logging::value_ref< point > const& value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
point const& p = value.get();
|
||||
m_format % p.m_x % p.m_y;
|
||||
strm << m_format;
|
||||
m_format.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
boost::format m_format;
|
||||
};
|
||||
|
||||
// Custom point formatter factory
|
||||
class point_formatter_factory :
|
||||
public logging::basic_formatter_factory< char, point >
|
||||
{
|
||||
public:
|
||||
formatter_type create_formatter(logging::attribute_name const& name, args_map const& args)
|
||||
{
|
||||
args_map::const_iterator it = args.find("format");
|
||||
if (it != args.end())
|
||||
return boost::phoenix::bind(point_formatter(it->second), expr::stream, expr::attr< point >(name));
|
||||
else
|
||||
return expr::stream << expr::attr< point >(name);
|
||||
}
|
||||
};
|
||||
|
||||
void init_factories()
|
||||
{
|
||||
logging::register_formatter_factory("Coordinates", boost::make_shared< point_formatter_factory >());
|
||||
}
|
||||
//]
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
init_factories();
|
||||
|
||||
logging::add_console_log(std::clog, keywords::format = "%TimeStamp% %Coordinates(format=\"{%0.3f; %0.3f}\")% %Message%");
|
||||
|
||||
logging::add_common_attributes();
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
src::logger lg;
|
||||
BOOST_LOG(lg) << logging::add_value("Coordinates", point(10.5f, 20.2f)) << "Hello, world with coordinates!";
|
||||
|
||||
return 0;
|
||||
}
|
247
example/doc/extension_record_tagger.cpp
Normal file
247
example/doc/extension_record_tagger.cpp
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/scope_exit.hpp>
|
||||
#include <boost/mpl/quote.hpp>
|
||||
#include <boost/parameter/keyword.hpp>
|
||||
#include <boost/thread/locks.hpp>
|
||||
#include <boost/move/utility.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes/constant.hpp>
|
||||
#include <boost/log/sources/features.hpp>
|
||||
#include <boost/log/sources/basic_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/utility/strictest_lock.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_extension_record_tagger_keyword
|
||||
namespace my_keywords {
|
||||
|
||||
BOOST_PARAMETER_KEYWORD(tag_ns, tag)
|
||||
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_extension_record_tagger_declaration
|
||||
template< typename BaseT >
|
||||
class record_tagger_feature :
|
||||
public BaseT /*< the feature should derive from other features or the basic_logger class >*/
|
||||
{
|
||||
public:
|
||||
// Let's import some types that we will need. These imports should be public,
|
||||
// in order to allow other features that may derive from record_tagger to do the same.
|
||||
typedef typename BaseT::char_type char_type;
|
||||
typedef typename BaseT::threading_model threading_model;
|
||||
|
||||
public:
|
||||
// Default constructor. Initializes m_Tag to an invalid value.
|
||||
record_tagger_feature();
|
||||
// Copy constructor. Initializes m_Tag to a value, equivalent to that.m_Tag.
|
||||
record_tagger_feature(record_tagger_feature const& that);
|
||||
// Forwarding constructor with named parameters
|
||||
template< typename ArgsT >
|
||||
record_tagger_feature(ArgsT const& args);
|
||||
|
||||
// The method will require locking, so we have to define locking requirements for it.
|
||||
// We use the strictest_lock trait in order to choose the most restricting lock type.
|
||||
typedef typename logging::strictest_lock<
|
||||
boost::lock_guard< threading_model >,
|
||||
typename BaseT::open_record_lock,
|
||||
typename BaseT::add_attribute_lock,
|
||||
typename BaseT::remove_attribute_lock
|
||||
>::type open_record_lock;
|
||||
|
||||
protected:
|
||||
// Lock-less implementation of operations
|
||||
template< typename ArgsT >
|
||||
logging::record open_record_unlocked(ArgsT const& args);
|
||||
};
|
||||
|
||||
// A convenience metafunction to specify the feature
|
||||
// in the list of features of the final logger later
|
||||
struct record_tagger :
|
||||
public boost::mpl::quote1< record_tagger_feature >
|
||||
{
|
||||
};
|
||||
//]
|
||||
|
||||
//[ example_extension_record_tagger_structors
|
||||
template< typename BaseT >
|
||||
record_tagger_feature< BaseT >::record_tagger_feature()
|
||||
{
|
||||
}
|
||||
|
||||
template< typename BaseT >
|
||||
record_tagger_feature< BaseT >::record_tagger_feature(record_tagger_feature const& that) :
|
||||
BaseT(static_cast< BaseT const& >(that))
|
||||
{
|
||||
}
|
||||
|
||||
template< typename BaseT >
|
||||
template< typename ArgsT >
|
||||
record_tagger_feature< BaseT >::record_tagger_feature(ArgsT const& args) : BaseT(args)
|
||||
{
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_extension_record_tagger_open_record
|
||||
template< typename BaseT >
|
||||
template< typename ArgsT >
|
||||
logging::record record_tagger_feature< BaseT >::open_record_unlocked(ArgsT const& args)
|
||||
{
|
||||
// Extract the named argument from the parameters pack
|
||||
std::string tag_value = args[my_keywords::tag | std::string()];
|
||||
|
||||
logging::attribute_set& attrs = BaseT::attributes();
|
||||
logging::attribute_set::iterator tag = attrs.end();
|
||||
if (!tag_value.empty())
|
||||
{
|
||||
// Add the tag as a new attribute
|
||||
std::pair<
|
||||
logging::attribute_set::iterator,
|
||||
bool
|
||||
> res = BaseT::add_attribute_unlocked("Tag",
|
||||
attrs::constant< std::string >(tag_value));
|
||||
if (res.second)
|
||||
tag = res.first;
|
||||
}
|
||||
|
||||
// In any case, after opening a record remove the tag from the attributes
|
||||
BOOST_SCOPE_EXIT_TPL((&tag)(&attrs))
|
||||
{
|
||||
if (tag != attrs.end())
|
||||
attrs.erase(tag);
|
||||
}
|
||||
BOOST_SCOPE_EXIT_END
|
||||
|
||||
// Forward the call to the base feature
|
||||
return BaseT::open_record_unlocked(args);
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_extension_record_tagger_my_logger
|
||||
template< typename LevelT = int >
|
||||
class my_logger :
|
||||
public src::basic_composite_logger<
|
||||
char, /*< character type for the logger >*/
|
||||
my_logger< LevelT >, /*< final logger type >*/
|
||||
src::single_thread_model, /*< the logger does not perform thread synchronization; use `multi_thread_model` to declare a thread-safe logger >*/
|
||||
src::features< /*< the list of features we want to combine >*/
|
||||
src::severity< LevelT >,
|
||||
record_tagger
|
||||
>
|
||||
>
|
||||
{
|
||||
// The following line will automatically generate forwarding constructors that
|
||||
// will call to the corresponding constructors of the base class
|
||||
BOOST_LOG_FORWARD_LOGGER_MEMBERS_TEMPLATE(my_logger)
|
||||
};
|
||||
//]
|
||||
|
||||
//[ example_extension_record_tagger_severity
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
warning,
|
||||
error
|
||||
};
|
||||
//]
|
||||
|
||||
inline std::ostream& operator<< (std::ostream& strm, severity_level level)
|
||||
{
|
||||
const char* levels[] =
|
||||
{
|
||||
"normal",
|
||||
"warning",
|
||||
"error"
|
||||
};
|
||||
|
||||
if (static_cast< std::size_t >(level) < sizeof(levels) / sizeof(*levels))
|
||||
strm << levels[level];
|
||||
else
|
||||
strm << static_cast< int >(level);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
//[ example_extension_record_tagger_manual_logging
|
||||
void manual_logging()
|
||||
{
|
||||
my_logger< severity_level > logger;
|
||||
|
||||
logging::record rec = logger.open_record((keywords::severity = normal, my_keywords::tag = "GUI"));
|
||||
if (rec)
|
||||
{
|
||||
logging::record_ostream strm(rec);
|
||||
strm << "The user has confirmed his choice";
|
||||
strm.flush();
|
||||
logger.push_record(boost::move(rec));
|
||||
}
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_extension_record_tagger_macro_logging
|
||||
#define LOG_WITH_TAG(lg, sev, tg) \
|
||||
BOOST_LOG_WITH_PARAMS((lg), (keywords::severity = (sev))(my_keywords::tag = (tg)))
|
||||
|
||||
void logging_function()
|
||||
{
|
||||
my_logger< severity_level > logger;
|
||||
|
||||
LOG_WITH_TAG(logger, normal, "GUI") << "The user has confirmed his choice";
|
||||
}
|
||||
//]
|
||||
|
||||
void init()
|
||||
{
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("sample.log"));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< expr::attr< unsigned int >("LineID")
|
||||
<< ": <" << expr::attr< severity_level >("Severity")
|
||||
<< "> [" << expr::attr< std::string >("Tag") << "]\t"
|
||||
<< expr::smessage
|
||||
);
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add attributes
|
||||
logging::add_common_attributes();
|
||||
}
|
||||
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
|
||||
logging_function();
|
||||
manual_logging();
|
||||
|
||||
return 0;
|
||||
}
|
171
example/doc/extension_stat_collector.cpp
Normal file
171
example/doc/extension_stat_collector.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/phoenix.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/basic_sink_backend.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/attributes/value_visitation.hpp>
|
||||
#include <boost/log/utility/manipulators/add_value.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_extension_stat_collector_definition
|
||||
// The backend collects statistical information about network activity of the application
|
||||
class stat_collector :
|
||||
public sinks::basic_sink_backend<
|
||||
sinks::combine_requirements<
|
||||
sinks::synchronized_feeding, /*< we will have to store internal data, so let's require frontend to synchronize feeding calls to the backend >*/
|
||||
sinks::flushing /*< also enable flushing support >*/
|
||||
>::type
|
||||
>
|
||||
{
|
||||
private:
|
||||
// The file to write the collected information to
|
||||
std::ofstream m_csv_file;
|
||||
|
||||
// Here goes the data collected so far:
|
||||
// Active connections
|
||||
unsigned int m_active_connections;
|
||||
// Sent bytes
|
||||
unsigned int m_sent_bytes;
|
||||
// Received bytes
|
||||
unsigned int m_received_bytes;
|
||||
|
||||
// The number of collected records since the last write to the file
|
||||
unsigned int m_collected_count;
|
||||
// The time when the collected data has been written to the file last time
|
||||
boost::posix_time::ptime m_last_store_time;
|
||||
|
||||
public:
|
||||
// The constructor initializes the internal data
|
||||
explicit stat_collector(const char* file_name);
|
||||
|
||||
// The function consumes the log records that come from the frontend
|
||||
void consume(logging::record_view const& rec);
|
||||
// The function flushes the file
|
||||
void flush();
|
||||
|
||||
private:
|
||||
// The function resets statistical accumulators to initial values
|
||||
void reset_accumulators();
|
||||
// The function writes the collected data to the file
|
||||
void write_data();
|
||||
};
|
||||
//]
|
||||
|
||||
// The constructor initializes the internal data
|
||||
stat_collector::stat_collector(const char* file_name) :
|
||||
m_csv_file(file_name, std::ios::app),
|
||||
m_active_connections(0),
|
||||
m_last_store_time(boost::posix_time::microsec_clock::universal_time())
|
||||
{
|
||||
reset_accumulators();
|
||||
if (!m_csv_file.is_open())
|
||||
throw std::runtime_error("could not open the CSV file");
|
||||
}
|
||||
|
||||
//[ example_extension_stat_collector_consume
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(sent, "Sent", unsigned int)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(received, "Received", unsigned int)
|
||||
|
||||
// The function consumes the log records that come from the frontend
|
||||
void stat_collector::consume(logging::record_view const& rec)
|
||||
{
|
||||
// Accumulate statistical readings
|
||||
if (rec.attribute_values().count("Connected"))
|
||||
++m_active_connections;
|
||||
else if (rec.attribute_values().count("Disconnected"))
|
||||
--m_active_connections;
|
||||
else
|
||||
{
|
||||
namespace phoenix = boost::phoenix;
|
||||
logging::visit(sent, rec, phoenix::ref(m_sent_bytes) += phoenix::placeholders::_1);
|
||||
logging::visit(received, rec, phoenix::ref(m_received_bytes) += phoenix::placeholders::_1);
|
||||
}
|
||||
++m_collected_count;
|
||||
|
||||
// Check if it's time to write the accumulated data to the file
|
||||
boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
|
||||
if (now - m_last_store_time >= boost::posix_time::minutes(1))
|
||||
{
|
||||
write_data();
|
||||
m_last_store_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
// The function writes the collected data to the file
|
||||
void stat_collector::write_data()
|
||||
{
|
||||
m_csv_file << m_active_connections
|
||||
<< ',' << m_sent_bytes
|
||||
<< ',' << m_received_bytes
|
||||
<< std::endl;
|
||||
reset_accumulators();
|
||||
}
|
||||
|
||||
// The function resets statistical accumulators to initial values
|
||||
void stat_collector::reset_accumulators()
|
||||
{
|
||||
m_sent_bytes = m_received_bytes = 0;
|
||||
m_collected_count = 0;
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_extension_stat_collector_flush
|
||||
// The function flushes the file
|
||||
void stat_collector::flush()
|
||||
{
|
||||
// Store any data that may have been collected since the list write to the file
|
||||
if (m_collected_count > 0)
|
||||
{
|
||||
write_data();
|
||||
m_last_store_time = boost::posix_time::microsec_clock::universal_time();
|
||||
}
|
||||
|
||||
m_csv_file.flush();
|
||||
}
|
||||
//]
|
||||
|
||||
// Complete sink type
|
||||
typedef sinks::synchronous_sink< stat_collector > sink_t;
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
boost::shared_ptr< stat_collector > backend(new stat_collector("stat.csv"));
|
||||
boost::shared_ptr< sink_t > sink(new sink_t(backend));
|
||||
core->add_sink(sink);
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
src::logger lg;
|
||||
BOOST_LOG(lg) << logging::add_value("Connected", true);
|
||||
BOOST_LOG(lg) << logging::add_value("Sent", 100u);
|
||||
BOOST_LOG(lg) << logging::add_value("Received", 200u);
|
||||
|
||||
logging::core::get()->flush();
|
||||
|
||||
return 0;
|
||||
}
|
222
example/doc/extension_stat_collector_settings.cpp
Normal file
222
example/doc/extension_stat_collector_settings.cpp
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/phoenix.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/basic_sink_backend.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/attributes/value_visitation.hpp>
|
||||
#include <boost/log/utility/manipulators/add_value.hpp>
|
||||
#include <boost/log/utility/setup/filter_parser.hpp>
|
||||
#include <boost/log/utility/setup/from_stream.hpp>
|
||||
#include <boost/log/utility/setup/from_settings.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_extension_stat_collector_settings_definition
|
||||
// The backend collects statistical information about network activity of the application
|
||||
class stat_collector :
|
||||
public sinks::basic_sink_backend<
|
||||
sinks::combine_requirements<
|
||||
sinks::synchronized_feeding,
|
||||
sinks::flushing
|
||||
>::type
|
||||
>
|
||||
{
|
||||
private:
|
||||
// The file to write the collected information to
|
||||
std::ofstream m_csv_file;
|
||||
|
||||
// Here goes the data collected so far:
|
||||
// Active connections
|
||||
unsigned int m_active_connections;
|
||||
// Sent bytes
|
||||
unsigned int m_sent_bytes;
|
||||
// Received bytes
|
||||
unsigned int m_received_bytes;
|
||||
|
||||
// The number of collected records since the last write to the file
|
||||
unsigned int m_collected_count;
|
||||
// The time when the collected data has been written to the file last time
|
||||
boost::posix_time::ptime m_last_store_time;
|
||||
// The collected data writing interval
|
||||
boost::posix_time::time_duration m_write_interval;
|
||||
|
||||
public:
|
||||
// The constructor initializes the internal data
|
||||
stat_collector(const char* file_name, boost::posix_time::time_duration write_interval);
|
||||
|
||||
// The function consumes the log records that come from the frontend
|
||||
void consume(logging::record_view const& rec);
|
||||
// The function flushes the file
|
||||
void flush();
|
||||
|
||||
private:
|
||||
// The function resets statistical accumulators to initial values
|
||||
void reset_accumulators();
|
||||
// The function writes the collected data to the file
|
||||
void write_data();
|
||||
};
|
||||
//]
|
||||
|
||||
// The constructor initializes the internal data
|
||||
stat_collector::stat_collector(const char* file_name, boost::posix_time::time_duration write_interval) :
|
||||
m_csv_file(file_name, std::ios::app),
|
||||
m_active_connections(0),
|
||||
m_last_store_time(boost::posix_time::microsec_clock::universal_time()),
|
||||
m_write_interval(write_interval)
|
||||
{
|
||||
reset_accumulators();
|
||||
if (!m_csv_file.is_open())
|
||||
throw std::runtime_error("could not open the CSV file");
|
||||
}
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(sent, "Sent", unsigned int)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(received, "Received", unsigned int)
|
||||
|
||||
// The function consumes the log records that come from the frontend
|
||||
void stat_collector::consume(logging::record_view const& rec)
|
||||
{
|
||||
// Accumulate statistical readings
|
||||
if (rec.attribute_values().count("Connected"))
|
||||
++m_active_connections;
|
||||
else if (rec.attribute_values().count("Disconnected"))
|
||||
--m_active_connections;
|
||||
else
|
||||
{
|
||||
namespace phoenix = boost::phoenix;
|
||||
logging::visit(sent, rec, phoenix::ref(m_sent_bytes) += phoenix::placeholders::_1);
|
||||
logging::visit(received, rec, phoenix::ref(m_received_bytes) += phoenix::placeholders::_1);
|
||||
}
|
||||
++m_collected_count;
|
||||
|
||||
// Check if it's time to write the accumulated data to the file
|
||||
boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
|
||||
if (now - m_last_store_time >= m_write_interval)
|
||||
{
|
||||
write_data();
|
||||
m_last_store_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
// The function writes the collected data to the file
|
||||
void stat_collector::write_data()
|
||||
{
|
||||
m_csv_file << m_active_connections
|
||||
<< ',' << m_sent_bytes
|
||||
<< ',' << m_received_bytes
|
||||
<< std::endl;
|
||||
reset_accumulators();
|
||||
}
|
||||
|
||||
// The function resets statistical accumulators to initial values
|
||||
void stat_collector::reset_accumulators()
|
||||
{
|
||||
m_sent_bytes = m_received_bytes = 0;
|
||||
m_collected_count = 0;
|
||||
}
|
||||
|
||||
// The function flushes the file
|
||||
void stat_collector::flush()
|
||||
{
|
||||
// Store any data that may have been collected since the list write to the file
|
||||
if (m_collected_count > 0)
|
||||
{
|
||||
write_data();
|
||||
m_last_store_time = boost::posix_time::microsec_clock::universal_time();
|
||||
}
|
||||
|
||||
m_csv_file.flush();
|
||||
}
|
||||
|
||||
//[ example_extension_stat_collector_factory
|
||||
// Factory for the stat_collector sink
|
||||
class stat_collector_factory :
|
||||
public logging::sink_factory< char >
|
||||
{
|
||||
public:
|
||||
// Creates the sink with the provided parameters
|
||||
boost::shared_ptr< sinks::sink > create_sink(settings_section const& settings)
|
||||
{
|
||||
// Read sink parameters
|
||||
std::string file_name;
|
||||
if (boost::optional< std::string > param = settings["FileName"])
|
||||
file_name = param.get();
|
||||
else
|
||||
throw std::runtime_error("No target file name specified in settings");
|
||||
|
||||
boost::posix_time::time_duration write_interval = boost::posix_time::minutes(1);
|
||||
if (boost::optional< std::string > param = settings["WriteInterval"])
|
||||
{
|
||||
unsigned int sec = boost::lexical_cast< unsigned int >(param.get());
|
||||
write_interval = boost::posix_time::seconds(sec);
|
||||
}
|
||||
|
||||
// Create the sink
|
||||
boost::shared_ptr< stat_collector > backend = boost::make_shared< stat_collector >(file_name.c_str(), write_interval);
|
||||
boost::shared_ptr< sinks::synchronous_sink< stat_collector > > sink = boost::make_shared< sinks::synchronous_sink< stat_collector > >(backend);
|
||||
|
||||
if (boost::optional< std::string > param = settings["Filter"])
|
||||
{
|
||||
sink->set_filter(logging::parse_filter(param.get()));
|
||||
}
|
||||
|
||||
return sink;
|
||||
}
|
||||
};
|
||||
|
||||
void init_factories()
|
||||
{
|
||||
logging::register_sink_factory("StatCollector", boost::make_shared< stat_collector_factory >());
|
||||
}
|
||||
//]
|
||||
|
||||
const char settings[] =
|
||||
"[Sinks.MyStat]\n"
|
||||
"Destination=StatCollector\n"
|
||||
"FileName=stat.csv\n"
|
||||
"WriteInterval=30\n"
|
||||
;
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
init_factories();
|
||||
|
||||
std::istringstream strm(settings);
|
||||
logging::init_from_stream(strm);
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
src::logger lg;
|
||||
BOOST_LOG(lg) << logging::add_value("Connected", true);
|
||||
BOOST_LOG(lg) << logging::add_value("Sent", 100u);
|
||||
BOOST_LOG(lg) << logging::add_value("Received", 200u);
|
||||
|
||||
logging::core::get()->flush();
|
||||
|
||||
return 0;
|
||||
}
|
133
example/doc/extension_system_uptime_attr.cpp
Normal file
133
example/doc/extension_system_uptime_attr.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/attributes/attribute.hpp>
|
||||
#include <boost/log/attributes/attribute_value.hpp>
|
||||
#include <boost/log/attributes/attribute_value_impl.hpp>
|
||||
#include <boost/log/attributes/attribute_cast.hpp>
|
||||
#include <boost/log/utility/empty_deleter.hpp>
|
||||
|
||||
// Includes for get_uptime()
|
||||
#include <boost/throw_exception.hpp>
|
||||
#if defined(BOOST_WINDOWS)
|
||||
#include <windows.h>
|
||||
#elif defined(__linux__) || defined(__linux) || defined(linux)
|
||||
#include <sys/sysinfo.h>
|
||||
#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <sys/sysctl.h>
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_extension_system_uptime_attr_impl
|
||||
// The function returns the system uptime, in seconds
|
||||
unsigned int get_uptime();
|
||||
|
||||
// Attribute implementation class
|
||||
class system_uptime_impl :
|
||||
public logging::attribute::impl
|
||||
{
|
||||
public:
|
||||
// The method generates a new attribute value
|
||||
logging::attribute_value get_value()
|
||||
{
|
||||
return attrs::make_attribute_value(get_uptime());
|
||||
}
|
||||
};
|
||||
//]
|
||||
|
||||
//[ example_extension_system_uptime_attr
|
||||
// Attribute interface class
|
||||
class system_uptime :
|
||||
public logging::attribute
|
||||
{
|
||||
public:
|
||||
system_uptime() : logging::attribute(new system_uptime_impl())
|
||||
{
|
||||
}
|
||||
// Attribute casting support
|
||||
explicit system_uptime(attrs::cast_source const& source) : logging::attribute(source.as< system_uptime_impl >())
|
||||
{
|
||||
}
|
||||
};
|
||||
//]
|
||||
|
||||
//[ example_extension_system_uptime_use
|
||||
void init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
//<-
|
||||
// Initialize the sink
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > sink_t;
|
||||
boost::shared_ptr< sink_t > sink(new sink_t());
|
||||
sink->locked_backend()->add_stream(boost::shared_ptr< std::ostream >(&std::clog, logging::empty_deleter()));
|
||||
sink->set_formatter(expr::stream << expr::attr< unsigned int >("SystemUptime") << ": " << expr::smessage);
|
||||
core->add_sink(sink);
|
||||
//->
|
||||
// ...
|
||||
|
||||
// Add the uptime attribute to the core
|
||||
core->add_global_attribute("SystemUptime", system_uptime());
|
||||
}
|
||||
//]
|
||||
|
||||
unsigned int get_uptime()
|
||||
{
|
||||
#if defined(BOOST_WINDOWS)
|
||||
return GetTickCount() / 1000u;
|
||||
#elif defined(__linux__) || defined(__linux) || defined(linux)
|
||||
struct sysinfo info;
|
||||
if (sysinfo(&info) != 0)
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Could not acquire uptime"));
|
||||
return info.uptime;
|
||||
#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
|
||||
struct timeval boottime;
|
||||
std::size_t len = sizeof(boottime);
|
||||
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
|
||||
if (sysctl(mib, 2, &boottime, &len, NULL, 0) < 0)
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Could not acquire uptime"));
|
||||
return time(NULL) - boottime.tv_sec;
|
||||
#elif (defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) && defined(CLOCK_UPTIME)
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_UPTIME, &ts) != 0)
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Could not acquire uptime"));
|
||||
return ts.tv_sec;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
src::logger lg;
|
||||
BOOST_LOG(lg) << "Hello, world with uptime!";
|
||||
|
||||
return 0;
|
||||
}
|
98
example/doc/sinks_async.cpp
Normal file
98
example/doc/sinks_async.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/async_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/empty_deleter.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sinks_async_init
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
warning,
|
||||
error
|
||||
};
|
||||
|
||||
// Complete sink type
|
||||
typedef sinks::asynchronous_sink< sinks::text_ostream_backend > sink_t;
|
||||
|
||||
boost::shared_ptr< sink_t > init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Create a backend and initialize it with a stream
|
||||
boost::shared_ptr< sinks::text_ostream_backend > backend =
|
||||
boost::make_shared< sinks::text_ostream_backend >();
|
||||
backend->add_stream(
|
||||
boost::shared_ptr< std::ostream >(&std::clog, logging::empty_deleter()));
|
||||
|
||||
// Wrap it into the frontend and register in the core
|
||||
boost::shared_ptr< sink_t > sink(new sink_t(backend));
|
||||
core->add_sink(sink);
|
||||
|
||||
// You can manage filtering and formatting through the sink interface
|
||||
sink->set_filter(expr::attr< severity_level >("Severity") >= warning);
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< "Level: " << expr::attr< severity_level >("Severity")
|
||||
<< " Message: " << expr::message
|
||||
);
|
||||
|
||||
// You can also manage backend in a thread-safe manner
|
||||
{
|
||||
sink_t::locked_backend_ptr p = sink->locked_backend();
|
||||
p->add_stream(boost::make_shared< std::ofstream >("sample.log"));
|
||||
} // the backend gets released here
|
||||
|
||||
return sink;
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_sinks_async_stop
|
||||
void stop_logging(boost::shared_ptr< sink_t >& sink)
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Remove the sink from the core, so that no records are passed to it
|
||||
core->remove_sink(sink);
|
||||
|
||||
// Break the feeding loop
|
||||
sink->stop();
|
||||
|
||||
// Flush all log records that may have left buffered
|
||||
sink->flush();
|
||||
|
||||
sink.reset();
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
boost::shared_ptr< sink_t > sink = init_logging();
|
||||
|
||||
src::severity_channel_logger< severity_level > lg(keywords::channel = "net");
|
||||
BOOST_LOG_SEV(lg, warning) << "Hello world!";
|
||||
|
||||
stop_logging(sink);
|
||||
|
||||
return 0;
|
||||
}
|
107
example/doc/sinks_async_bounded.cpp
Normal file
107
example/doc/sinks_async_bounded.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/async_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/sinks/bounded_fifo_queue.hpp>
|
||||
#include <boost/log/sinks/drop_on_overflow.hpp>
|
||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/empty_deleter.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
warning,
|
||||
error
|
||||
};
|
||||
|
||||
//[ example_sinks_bounded_async_init
|
||||
// Complete sink type
|
||||
typedef sinks::asynchronous_sink<
|
||||
sinks::text_ostream_backend,
|
||||
sinks::bounded_fifo_queue< /*< log record queueing strategy >*/
|
||||
100, /*< record queue capacity >*/
|
||||
sinks::drop_on_overflow /*< overflow handling policy >*/
|
||||
>
|
||||
> sink_t;
|
||||
|
||||
boost::shared_ptr< sink_t > init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Create a backend and initialize it with a stream
|
||||
boost::shared_ptr< sinks::text_ostream_backend > backend =
|
||||
boost::make_shared< sinks::text_ostream_backend >();
|
||||
backend->add_stream(
|
||||
boost::shared_ptr< std::ostream >(&std::clog, logging::empty_deleter()));
|
||||
|
||||
// Wrap it into the frontend and register in the core
|
||||
boost::shared_ptr< sink_t > sink(new sink_t(backend));
|
||||
core->add_sink(sink);
|
||||
|
||||
// ...
|
||||
//<-
|
||||
// You can manage filtering and formatting through the sink interface
|
||||
sink->set_filter(expr::attr< severity_level >("Severity") >= warning);
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< "Level: " << expr::attr< severity_level >("Severity")
|
||||
<< " Message: " << expr::message
|
||||
);
|
||||
|
||||
// You can also manage backend in a thread-safe manner
|
||||
{
|
||||
sink_t::locked_backend_ptr p = sink->locked_backend();
|
||||
p->add_stream(boost::make_shared< std::ofstream >("sample.log"));
|
||||
}
|
||||
//->
|
||||
|
||||
return sink;
|
||||
}
|
||||
//]
|
||||
|
||||
void stop_logging(boost::shared_ptr< sink_t >& sink)
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Remove the sink from the core, so that no records are passed to it
|
||||
core->remove_sink(sink);
|
||||
|
||||
// Break the feeding loop
|
||||
sink->stop();
|
||||
|
||||
// Flush all log records that may have left buffered
|
||||
sink->flush();
|
||||
|
||||
sink.reset();
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
boost::shared_ptr< sink_t > sink = init_logging();
|
||||
|
||||
src::severity_channel_logger< severity_level > lg(keywords::channel = "net");
|
||||
BOOST_LOG_SEV(lg, warning) << "Hello world!";
|
||||
|
||||
stop_logging(sink);
|
||||
|
||||
return 0;
|
||||
}
|
117
example/doc/sinks_async_ordering.cpp
Normal file
117
example/doc/sinks_async_ordering.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/async_frontend.hpp>
|
||||
#include <boost/log/sinks/unbounded_ordering_queue.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/empty_deleter.hpp>
|
||||
#include <boost/log/utility/record_ordering.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
warning,
|
||||
error
|
||||
};
|
||||
|
||||
//[ example_sinks_ordering_async_init
|
||||
// Complete sink type
|
||||
typedef sinks::asynchronous_sink<
|
||||
sinks::text_ostream_backend,
|
||||
sinks::unbounded_ordering_queue< /*< log record queueing strategy >*/
|
||||
logging::attribute_value_ordering< /*< log record ordering predicate type >*/
|
||||
unsigned int, /*< attribute value type >*/
|
||||
std::less< unsigned int > /*< optional, attribute value comparison predicate; `std::less` equivalent is used by default >*/
|
||||
>
|
||||
>
|
||||
> sink_t;
|
||||
|
||||
boost::shared_ptr< sink_t > init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Create a backend and initialize it with a stream
|
||||
boost::shared_ptr< sinks::text_ostream_backend > backend =
|
||||
boost::make_shared< sinks::text_ostream_backend >();
|
||||
backend->add_stream(
|
||||
boost::shared_ptr< std::ostream >(&std::clog, logging::empty_deleter()));
|
||||
|
||||
// Wrap it into the frontend and register in the core
|
||||
boost::shared_ptr< sink_t > sink(new sink_t(
|
||||
backend, /*< pointer to the pre-initialized backend >*/
|
||||
keywords::order =
|
||||
logging::make_attr_ordering("LineID", std::less< unsigned int >()), /*< log record ordering predicate >*/
|
||||
keywords::ordering_window = boost::posix_time::seconds(1) /*< latency of log record processing >*/
|
||||
));
|
||||
core->add_sink(sink);
|
||||
|
||||
// You can manage filtering and formatting through the sink interface
|
||||
sink->set_filter(expr::attr< severity_level >("Severity") >= warning);
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< "Level: " << expr::attr< severity_level >("Severity")
|
||||
<< " Message: " << expr::smessage
|
||||
);
|
||||
|
||||
// You can also manage backend in a thread-safe manner
|
||||
{
|
||||
sink_t::locked_backend_ptr p = sink->locked_backend();
|
||||
p->add_stream(boost::make_shared< std::ofstream >("sample.log"));
|
||||
} // the backend gets released here
|
||||
|
||||
return sink;
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_sinks_ordering_async_stop
|
||||
void stop_logging(boost::shared_ptr< sink_t >& sink)
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Remove the sink from the core, so that no records are passed to it
|
||||
core->remove_sink(sink);
|
||||
|
||||
// Break the feeding loop
|
||||
sink->stop();
|
||||
|
||||
// Flush all log records that may have left buffered
|
||||
sink->flush();
|
||||
|
||||
sink.reset();
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
boost::shared_ptr< sink_t > sink = init_logging();
|
||||
logging::add_common_attributes();
|
||||
|
||||
src::severity_channel_logger< severity_level > lg(keywords::channel = "net");
|
||||
BOOST_LOG_SEV(lg, warning) << "Hello world!";
|
||||
|
||||
stop_logging(sink);
|
||||
|
||||
return 0;
|
||||
}
|
59
example/doc/sinks_debugger.cpp
Normal file
59
example/doc/sinks_debugger.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions/predicates/is_debugger_present.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/debug_output_backend.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
|
||||
#if defined(BOOST_WINDOWS)
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
|
||||
//[ example_sinks_debugger
|
||||
// Complete sink type
|
||||
typedef sinks::synchronous_sink< sinks::debug_output_backend > sink_t;
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Create the sink. The backend requires synchronization in the frontend.
|
||||
boost::shared_ptr< sink_t > sink(new sink_t());
|
||||
|
||||
// Set the special filter to the frontend
|
||||
// in order to skip the sink when no debugger is available
|
||||
sink->set_filter(expr::is_debugger_present());
|
||||
|
||||
core->add_sink(sink);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
src::logger lg;
|
||||
BOOST_LOG(lg) << "Hello world!";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
53
example/doc/sinks_file.cpp
Normal file
53
example/doc/sinks_file.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_file_backend.hpp>
|
||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/empty_deleter.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sinks_file
|
||||
void init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
boost::shared_ptr< sinks::text_file_backend > backend =
|
||||
boost::make_shared< sinks::text_file_backend >(
|
||||
keywords::file_name = "file_%5N.log", /*< file name pattern >*/
|
||||
keywords::rotation_size = 5 * 1024 * 1024, /*< rotate the file upon reaching 5 MiB size... >*/
|
||||
keywords::time_based_rotation = sinks::file::rotation_at_time_point(12, 0, 0) /*< ...or every day, at noon, whichever comes first >*/
|
||||
);
|
||||
|
||||
// Wrap it into the frontend and register in the core.
|
||||
// The backend requires synchronization in the frontend.
|
||||
typedef sinks::synchronous_sink< sinks::text_file_backend > sink_t;
|
||||
boost::shared_ptr< sink_t > sink(new sink_t(backend));
|
||||
|
||||
core->add_sink(sink);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
src::severity_channel_logger< > lg(keywords::channel = "net");
|
||||
BOOST_LOG_SEV(lg, 3) << "Hello world!";
|
||||
|
||||
return 0;
|
||||
}
|
77
example/doc/sinks_multifile.cpp
Normal file
77
example/doc/sinks_multifile.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_multifile_backend.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/attributes/scoped_attribute.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sinks_multifile
|
||||
void init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
boost::shared_ptr< sinks::text_multifile_backend > backend =
|
||||
boost::make_shared< sinks::text_multifile_backend >();
|
||||
|
||||
// Set up the file naming pattern
|
||||
backend->set_file_name_composer
|
||||
(
|
||||
sinks::file::as_file_name_composer(expr::stream << "logs/" << expr::attr< std::string >("RequestID") << ".log")
|
||||
);
|
||||
|
||||
// Wrap it into the frontend and register in the core.
|
||||
// The backend requires synchronization in the frontend.
|
||||
typedef sinks::synchronous_sink< sinks::text_multifile_backend > sink_t;
|
||||
boost::shared_ptr< sink_t > sink(new sink_t(backend));
|
||||
|
||||
// Set the formatter
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< "[RequestID: " << expr::attr< std::string >("RequestID")
|
||||
<< "] " << expr::smessage
|
||||
);
|
||||
|
||||
core->add_sink(sink);
|
||||
}
|
||||
//]
|
||||
|
||||
void logging_function()
|
||||
{
|
||||
src::logger lg;
|
||||
BOOST_LOG(lg) << "Hello, world!";
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
{
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("RequestID", "Request1");
|
||||
logging_function();
|
||||
}
|
||||
{
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("RequestID", "Request2");
|
||||
logging_function();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
56
example/doc/sinks_ostream.cpp
Normal file
56
example/doc/sinks_ostream.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/empty_deleter.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sinks_ostream
|
||||
void init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Create a backend and attach a couple of streams to it
|
||||
boost::shared_ptr< sinks::text_ostream_backend > backend =
|
||||
boost::make_shared< sinks::text_ostream_backend >();
|
||||
backend->add_stream(
|
||||
boost::shared_ptr< std::ostream >(&std::clog, logging::empty_deleter()));
|
||||
backend->add_stream(
|
||||
boost::shared_ptr< std::ostream >(new std::ofstream("sample.log")));
|
||||
|
||||
// Enable auto-flushing after each log record written
|
||||
backend->auto_flush(true);
|
||||
|
||||
// Wrap it into the frontend and register in the core.
|
||||
// The backend requires synchronization in the frontend.
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > sink_t;
|
||||
boost::shared_ptr< sink_t > sink(new sink_t(backend));
|
||||
core->add_sink(sink);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
src::severity_channel_logger< > lg(keywords::channel = "net");
|
||||
BOOST_LOG_SEV(lg, 3) << "Hello world!";
|
||||
|
||||
return 0;
|
||||
}
|
100
example/doc/sinks_simple_event_log.cpp
Normal file
100
example/doc/sinks_simple_event_log.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/attributes.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/event_log_backend.hpp>
|
||||
|
||||
#if defined(BOOST_WINDOWS)
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sinks_simple_event_log
|
||||
// Complete sink type
|
||||
typedef sinks::synchronous_sink< sinks::simple_event_log_backend > sink_t;
|
||||
|
||||
// Define application-specific severity levels
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
warning,
|
||||
error
|
||||
};
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
// Create an event log sink
|
||||
boost::shared_ptr< sink_t > sink(new sink_t());
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::format("%1%: [%2%] - %3%")
|
||||
% expr::attr< unsigned int >("LineID")
|
||||
% expr::attr< boost::posix_time::ptime >("TimeStamp")
|
||||
% expr::smessage
|
||||
);
|
||||
|
||||
// We'll have to map our custom levels to the event log event types
|
||||
sinks::event_log::custom_event_type_mapping< severity_level > mapping("Severity");
|
||||
mapping[normal] = sinks::event_log::info;
|
||||
mapping[warning] = sinks::event_log::warning;
|
||||
mapping[error] = sinks::event_log::error;
|
||||
|
||||
sink->locked_backend()->set_event_type_mapper(mapping);
|
||||
|
||||
// Add the sink to the core
|
||||
logging::core::get()->add_sink(sink);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Initialize logging library
|
||||
init_logging();
|
||||
|
||||
// Add some attributes too
|
||||
logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock());
|
||||
logging::core::get()->add_global_attribute("LineID", attrs::counter< unsigned int >());
|
||||
|
||||
// Do some logging
|
||||
src::severity_logger< severity_level > lg(keywords::severity = normal);
|
||||
BOOST_LOG_SEV(lg, normal) << "Some record for NT event log with normal level";
|
||||
BOOST_LOG_SEV(lg, warning) << "Some record for NT event log with warning level";
|
||||
BOOST_LOG_SEV(lg, error) << "Some record for NT event log with error level";
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "FAILURE: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
76
example/doc/sinks_sync.cpp
Normal file
76
example/doc/sinks_sync.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/empty_deleter.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sinks_sync
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
warning,
|
||||
error
|
||||
};
|
||||
|
||||
// Complete sink type
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > sink_t;
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Create a backend and initialize it with a stream
|
||||
boost::shared_ptr< sinks::text_ostream_backend > backend =
|
||||
boost::make_shared< sinks::text_ostream_backend >();
|
||||
backend->add_stream(
|
||||
boost::shared_ptr< std::ostream >(&std::clog, logging::empty_deleter()));
|
||||
|
||||
// Wrap it into the frontend and register in the core
|
||||
boost::shared_ptr< sink_t > sink(new sink_t(backend));
|
||||
core->add_sink(sink);
|
||||
|
||||
// You can manage filtering and formatting through the sink interface
|
||||
sink->set_filter(expr::attr< severity_level >("Severity") >= warning);
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< "Level: " << expr::attr< severity_level >("Severity")
|
||||
<< " Message: " << expr::smessage
|
||||
);
|
||||
|
||||
// You can also manage backend in a thread-safe manner
|
||||
{
|
||||
sink_t::locked_backend_ptr p = sink->locked_backend();
|
||||
p->add_stream(boost::make_shared< std::ofstream >("sample.log"));
|
||||
} // the backend gets released here
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
src::severity_channel_logger< severity_level > lg(keywords::channel = "net");
|
||||
BOOST_LOG_SEV(lg, warning) << "Hello world!";
|
||||
|
||||
return 0;
|
||||
}
|
96
example/doc/sinks_syslog.cpp
Normal file
96
example/doc/sinks_syslog.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/syslog_backend.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/attributes/scoped_attribute.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sinks_syslog
|
||||
// Complete sink type
|
||||
typedef sinks::synchronous_sink< sinks::syslog_backend > sink_t;
|
||||
|
||||
//<-
|
||||
#if defined(BOOST_LOG_USE_NATIVE_SYSLOG)
|
||||
//->
|
||||
void init_native_syslog()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Create a backend
|
||||
boost::shared_ptr< sinks::syslog_backend > backend(new sinks::syslog_backend(
|
||||
keywords::facility = sinks::syslog::user, /*< the logging facility >*/
|
||||
keywords::use_impl = sinks::syslog::native /*< the native syslog API should be used >*/
|
||||
));
|
||||
|
||||
// Set the straightforward level translator for the "Severity" attribute of type int
|
||||
backend->set_severity_mapper(sinks::syslog::direct_severity_mapping< int >("Severity"));
|
||||
|
||||
// Wrap it into the frontend and register in the core.
|
||||
// The backend requires synchronization in the frontend.
|
||||
core->add_sink(boost::make_shared< sink_t >(backend));
|
||||
}
|
||||
//<-
|
||||
#endif
|
||||
//->
|
||||
|
||||
//<-
|
||||
#if !defined(BOOST_LOG_NO_ASIO)
|
||||
//->
|
||||
void init_builtin_syslog()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// Create a new backend
|
||||
boost::shared_ptr< sinks::syslog_backend > backend(new sinks::syslog_backend(
|
||||
keywords::facility = sinks::syslog::local0, /*< the logging facility >*/
|
||||
keywords::use_impl = sinks::syslog::udp_socket_based /*< the built-in socket-based implementation should be used >*/
|
||||
));
|
||||
|
||||
// Setup the target address and port to send syslog messages to
|
||||
backend->set_target_address("192.164.1.10", 514);
|
||||
|
||||
// Create and fill in another level translator for "MyLevel" attribute of type string
|
||||
sinks::syslog::custom_severity_mapping< std::string > mapping("MyLevel");
|
||||
mapping["debug"] = sinks::syslog::debug;
|
||||
mapping["normal"] = sinks::syslog::info;
|
||||
mapping["warning"] = sinks::syslog::warning;
|
||||
mapping["failure"] = sinks::syslog::critical;
|
||||
backend->set_severity_mapper(mapping);
|
||||
|
||||
// Wrap it into the frontend and register in the core.
|
||||
core->add_sink(boost::make_shared< sink_t >(backend));
|
||||
}
|
||||
//<-
|
||||
#endif
|
||||
//->
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
#if defined(BOOST_LOG_USE_NATIVE_SYSLOG)
|
||||
init_native_syslog();
|
||||
#elif !defined(BOOST_LOG_NO_ASIO)
|
||||
init_builtin_syslog();
|
||||
#endif
|
||||
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("MyLevel", "warning");
|
||||
src::severity_logger< > lg;
|
||||
BOOST_LOG_SEV(lg, 3) << "Hello world!";
|
||||
|
||||
return 0;
|
||||
}
|
76
example/doc/sinks_unlocked.cpp
Normal file
76
example/doc/sinks_unlocked.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/unlocked_frontend.hpp>
|
||||
#include <boost/log/sinks/basic_sink_backend.hpp>
|
||||
#include <boost/log/sinks/frontend_requirements.hpp>
|
||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sinks_unlocked
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
warning,
|
||||
error
|
||||
};
|
||||
|
||||
// A trivial sink backend that requires no thread synchronization
|
||||
class my_backend :
|
||||
public sinks::basic_sink_backend< sinks::concurrent_feeding >
|
||||
{
|
||||
public:
|
||||
// The function is called for every log record to be written to log
|
||||
void consume(logging::record_view const& rec)
|
||||
{
|
||||
// We skip the actual synchronization code for brevity
|
||||
std::cout << rec[expr::smessage] << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
// Complete sink type
|
||||
typedef sinks::unlocked_sink< my_backend > sink_t;
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
boost::shared_ptr< logging::core > core = logging::core::get();
|
||||
|
||||
// The simplest way, the backend is default-constructed
|
||||
boost::shared_ptr< sink_t > sink1(new sink_t());
|
||||
core->add_sink(sink1);
|
||||
|
||||
// One can construct backend separately and pass it to the frontend
|
||||
boost::shared_ptr< my_backend > backend(new my_backend());
|
||||
boost::shared_ptr< sink_t > sink2(new sink_t(backend));
|
||||
core->add_sink(sink2);
|
||||
|
||||
// You can manage filtering through the sink interface
|
||||
sink1->set_filter(expr::attr< severity_level >("Severity") >= warning);
|
||||
sink2->set_filter(expr::attr< std::string >("Channel") == "net");
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_logging();
|
||||
|
||||
src::severity_channel_logger< severity_level > lg(keywords::channel = "net");
|
||||
BOOST_LOG_SEV(lg, normal) << "Hello world!";
|
||||
|
||||
return 0;
|
||||
}
|
152
example/doc/sinks_xml_file.cpp
Normal file
152
example/doc/sinks_xml_file.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/lambda/lambda.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>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
typedef sinks::synchronous_sink< sinks::text_file_backend > file_sink;
|
||||
|
||||
//[ example_sinks_xml_file_collecting
|
||||
void init_file_collecting(boost::shared_ptr< file_sink > sink)
|
||||
{
|
||||
sink->locked_backend()->set_file_collector(sinks::file::make_collector(
|
||||
keywords::target = "logs", /*< the target directory >*/
|
||||
keywords::max_size = 16 * 1024 * 1024, /*< maximum total size of the stored files, in bytes >*/
|
||||
keywords::min_free_space = 100 * 1024 * 1024 /*< minimum free space on the drive, in bytes >*/
|
||||
));
|
||||
}
|
||||
//]
|
||||
|
||||
#if 0
|
||||
//[ example_sinks_xml_file
|
||||
// Complete file sink type
|
||||
typedef sinks::synchronous_sink< sinks::text_file_backend > file_sink;
|
||||
|
||||
void write_header(sinks::text_file_backend::stream_type& file)
|
||||
{
|
||||
file << "<?xml version=\"1.0\"?>\n<log>\n";
|
||||
}
|
||||
|
||||
void write_footer(sinks::text_file_backend::stream_type& file)
|
||||
{
|
||||
file << "</log>\n";
|
||||
}
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
// Create a text file sink
|
||||
boost::shared_ptr< file_sink > sink(new file_sink(
|
||||
keywords::file_name = "%Y%m%d_%H%M%S_%5N.xml", /*< the resulting file name pattern >*/
|
||||
keywords::rotation_size = 16384 /*< rotation size, in characters >*/
|
||||
));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::format("\t<record id=\"%1%\" timestamp=\"%2%\">%3%</record>")
|
||||
% expr::attr< unsigned int >("RecordID")
|
||||
% expr::attr< boost::posix_time::ptime >("TimeStamp")
|
||||
% expr::xml_decor[ expr::stream << expr::smessage ] /*< the log message has to be decorated, if it contains special characters >*/
|
||||
);
|
||||
|
||||
// Set header and footer writing functors
|
||||
sink->locked_backend()->set_open_handler(&write_header);
|
||||
sink->locked_backend()->set_close_handler(&write_footer);
|
||||
|
||||
// Add the sink to the core
|
||||
logging::core::get()->add_sink(sink);
|
||||
}
|
||||
//]
|
||||
#endif
|
||||
|
||||
//[ example_sinks_xml_file_final
|
||||
void init_logging()
|
||||
{
|
||||
// Create a text file sink
|
||||
boost::shared_ptr< file_sink > sink(new file_sink(
|
||||
keywords::file_name = "%Y%m%d_%H%M%S_%5N.xml",
|
||||
keywords::rotation_size = 16384
|
||||
));
|
||||
|
||||
// Set up where the rotated files will be stored
|
||||
init_file_collecting(sink);
|
||||
|
||||
// Upon restart, scan the directory for files matching the file_name pattern
|
||||
sink->locked_backend()->scan_for_files();
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::format("\t<record id=\"%1%\" timestamp=\"%2%\">%3%</record>")
|
||||
% expr::attr< unsigned int >("RecordID")
|
||||
% expr::attr< boost::posix_time::ptime >("TimeStamp")
|
||||
% expr::xml_decor[ expr::stream << expr::smessage ]
|
||||
);
|
||||
|
||||
// Set header and footer writing functors
|
||||
namespace bll = boost::lambda;
|
||||
|
||||
sink->locked_backend()->set_open_handler
|
||||
(
|
||||
bll::_1 << "<?xml version=\"1.0\"?>\n<log>\n"
|
||||
);
|
||||
sink->locked_backend()->set_close_handler
|
||||
(
|
||||
bll::_1 << "</log>\n"
|
||||
);
|
||||
|
||||
// Add the sink to the core
|
||||
logging::core::get()->add_sink(sink);
|
||||
}
|
||||
//]
|
||||
|
||||
enum { LOG_RECORDS_TO_WRITE = 2000 };
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Initialize logging library
|
||||
init_logging();
|
||||
|
||||
// And also add some attributes
|
||||
logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock());
|
||||
logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >());
|
||||
|
||||
// Do some logging
|
||||
src::logger lg;
|
||||
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
|
||||
{
|
||||
BOOST_LOG(lg) << "XML log record " << i;
|
||||
}
|
||||
|
||||
// Test that XML character decoration works
|
||||
BOOST_LOG(lg) << "Special XML characters: &, <, >, '";
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "FAILURE: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
123
example/doc/sources_net_connection.cpp
Normal file
123
example/doc/sources_net_connection.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/move/utility.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes/constant.hpp>
|
||||
#include <boost/log/attributes/scoped_attribute.hpp>
|
||||
#include <boost/log/attributes/attribute_value_impl.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/utility/manipulators/add_value.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(remote_address, "RemoteAddress", std::string)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(received_size, "ReceivedSize", std::size_t)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(sent_size, "SentSize", std::size_t)
|
||||
|
||||
//[ example_sources_network_connection
|
||||
class network_connection
|
||||
{
|
||||
src::logger m_logger;
|
||||
logging::attribute_set::iterator m_remote_addr;
|
||||
|
||||
public:
|
||||
void on_connected(std::string const& remote_addr)
|
||||
{
|
||||
// Put the remote address into the logger to automatically attach it
|
||||
// to every log record written through the logger
|
||||
m_remote_addr = m_logger.add_attribute("RemoteAddress",
|
||||
attrs::constant< std::string >(remote_addr)).first;
|
||||
|
||||
// The straightforward way of logging
|
||||
if (logging::record rec = m_logger.open_record())
|
||||
{
|
||||
rec.attribute_values().insert("Message",
|
||||
attrs::make_attribute_value(std::string("Connection established")));
|
||||
m_logger.push_record(boost::move(rec));
|
||||
}
|
||||
}
|
||||
void on_disconnected()
|
||||
{
|
||||
// The simpler way of logging: the above "if" condition is wrapped into a neat macro
|
||||
BOOST_LOG(m_logger) << "Connection shut down";
|
||||
|
||||
// Remove the attribute with the remote address
|
||||
m_logger.remove_attribute(m_remote_addr);
|
||||
}
|
||||
void on_data_received(std::size_t size)
|
||||
{
|
||||
// Put the size as an additional attribute
|
||||
// so it can be collected and accumulated later if needed.
|
||||
// The attribute will be attached only to this log record.
|
||||
BOOST_LOG(m_logger) << logging::add_value("ReceivedSize", size) << "Some data received";
|
||||
}
|
||||
void on_data_sent(std::size_t size)
|
||||
{
|
||||
BOOST_LOG(m_logger) << logging::add_value("SentSize", size) << "Some data sent";
|
||||
}
|
||||
};
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
// Construct the sink
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
// Add a stream to write log to
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("sample.log"));
|
||||
|
||||
// Set the formatter
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< line_id
|
||||
<< ": [" << remote_address << "] "
|
||||
<< expr::if_(expr::has_attr(received_size))
|
||||
[
|
||||
expr::stream << "[Received: " << received_size << "] "
|
||||
]
|
||||
<< expr::if_(expr::has_attr(sent_size))
|
||||
[
|
||||
expr::stream << "[Sent: " << sent_size << "] "
|
||||
]
|
||||
<< expr::smessage
|
||||
);
|
||||
|
||||
// Register the sink in the logging core
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Register other common attributes, such as time stamp and record counter
|
||||
logging::add_common_attributes();
|
||||
|
||||
// Emulate network activity
|
||||
network_connection conn;
|
||||
|
||||
conn.on_connected("11.22.33.44");
|
||||
conn.on_data_received(123);
|
||||
conn.on_data_sent(321);
|
||||
conn.on_disconnected();
|
||||
|
||||
return 0;
|
||||
}
|
142
example/doc/sources_net_connection_chan.cpp
Normal file
142
example/doc/sources_net_connection_chan.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes/constant.hpp>
|
||||
#include <boost/log/attributes/scoped_attribute.hpp>
|
||||
#include <boost/log/sources/channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/utility/manipulators/add_value.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(remote_address, "RemoteAddress", std::string)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(received_size, "ReceivedSize", std::size_t)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(sent_size, "SentSize", std::size_t)
|
||||
|
||||
//[ example_sources_network_connection_channels
|
||||
class network_connection
|
||||
{
|
||||
src::channel_logger< > m_net, m_stat;
|
||||
logging::attribute_set::iterator m_net_remote_addr, m_stat_remote_addr;
|
||||
|
||||
public:
|
||||
network_connection() :
|
||||
// We can dump network-related messages through this logger
|
||||
// and be able to filter them later
|
||||
m_net(keywords::channel = "net"),
|
||||
// We also can separate statistic records in a different channel
|
||||
// in order to route them to a different sink
|
||||
m_stat(keywords::channel = "stat")
|
||||
{
|
||||
}
|
||||
|
||||
void on_connected(std::string const& remote_addr)
|
||||
{
|
||||
// Add the remote address to both channels
|
||||
attrs::constant< std::string > addr(remote_addr);
|
||||
m_net_remote_addr = m_net.add_attribute("RemoteAddress", addr).first;
|
||||
m_stat_remote_addr = m_stat.add_attribute("RemoteAddress", addr).first;
|
||||
|
||||
// Put message to the "net" channel
|
||||
BOOST_LOG(m_net) << "Connection established";
|
||||
}
|
||||
|
||||
void on_disconnected()
|
||||
{
|
||||
// Put message to the "net" channel
|
||||
BOOST_LOG(m_net) << "Connection shut down";
|
||||
|
||||
// Remove the attribute with the remote address
|
||||
m_net.remove_attribute(m_net_remote_addr);
|
||||
m_stat.remove_attribute(m_stat_remote_addr);
|
||||
}
|
||||
|
||||
void on_data_received(std::size_t size)
|
||||
{
|
||||
BOOST_LOG(m_stat) << logging::add_value("ReceivedSize", size) << "Some data received";
|
||||
}
|
||||
|
||||
void on_data_sent(std::size_t size)
|
||||
{
|
||||
BOOST_LOG(m_stat) << logging::add_value("SentSize", size) << "Some data sent";
|
||||
}
|
||||
};
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
// Construct the sink for the "net" channel
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("net.log"));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream << line_id << ": [" << remote_address << "] " << expr::smessage
|
||||
);
|
||||
|
||||
sink->set_filter(channel == "net");
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Construct the sink for the "stat" channel
|
||||
sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("stat.log"));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< remote_address
|
||||
<< expr::if_(expr::has_attr(received_size))
|
||||
[
|
||||
expr::stream << " -> " << received_size << " bytes: "
|
||||
]
|
||||
<< expr::if_(expr::has_attr(sent_size))
|
||||
[
|
||||
expr::stream << " <- " << sent_size << " bytes: "
|
||||
]
|
||||
<< expr::smessage
|
||||
);
|
||||
|
||||
sink->set_filter(channel == "stat");
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Register other common attributes, such as time stamp and record counter
|
||||
logging::add_common_attributes();
|
||||
|
||||
// Emulate network activity
|
||||
network_connection conn;
|
||||
|
||||
conn.on_connected("11.22.33.44");
|
||||
conn.on_data_received(123);
|
||||
conn.on_data_sent(321);
|
||||
conn.on_disconnected();
|
||||
|
||||
return 0;
|
||||
}
|
140
example/doc/sources_net_connection_dynamic_chan.cpp
Normal file
140
example/doc/sources_net_connection_dynamic_chan.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes/constant.hpp>
|
||||
#include <boost/log/attributes/scoped_attribute.hpp>
|
||||
#include <boost/log/sources/channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sources/global_logger_storage.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/utility/manipulators/add_value.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(remote_address, "RemoteAddress", std::string)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(received_size, "ReceivedSize", std::size_t)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(sent_size, "SentSize", std::size_t)
|
||||
|
||||
//[ example_sources_network_connection_dynamic_channels
|
||||
// Define a global logger
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_CTOR_ARGS(my_logger, src::channel_logger_mt< >, (keywords::channel = "general"))
|
||||
|
||||
class network_connection
|
||||
{
|
||||
std::string m_remote_addr;
|
||||
|
||||
public:
|
||||
void on_connected(std::string const& remote_addr)
|
||||
{
|
||||
m_remote_addr = remote_addr;
|
||||
|
||||
// Put message to the "net" channel
|
||||
BOOST_LOG_CHANNEL(my_logger::get(), "net")
|
||||
<< logging::add_value("RemoteAddress", m_remote_addr)
|
||||
<< "Connection established";
|
||||
}
|
||||
|
||||
void on_disconnected()
|
||||
{
|
||||
// Put message to the "net" channel
|
||||
BOOST_LOG_CHANNEL(my_logger::get(), "net")
|
||||
<< logging::add_value("RemoteAddress", m_remote_addr)
|
||||
<< "Connection shut down";
|
||||
|
||||
m_remote_addr.clear();
|
||||
}
|
||||
|
||||
void on_data_received(std::size_t size)
|
||||
{
|
||||
BOOST_LOG_CHANNEL(my_logger::get(), "stat")
|
||||
<< logging::add_value("RemoteAddress", m_remote_addr)
|
||||
<< logging::add_value("ReceivedSize", size)
|
||||
<< "Some data received";
|
||||
}
|
||||
|
||||
void on_data_sent(std::size_t size)
|
||||
{
|
||||
BOOST_LOG_CHANNEL(my_logger::get(), "stat")
|
||||
<< logging::add_value("RemoteAddress", m_remote_addr)
|
||||
<< logging::add_value("SentSize", size)
|
||||
<< "Some data sent";
|
||||
}
|
||||
};
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
// Construct the sink for the "net" channel
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("net.log"));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream << line_id << ": [" << remote_address << "] " << expr::smessage
|
||||
);
|
||||
|
||||
sink->set_filter(channel == "net");
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Construct the sink for the "stat" channel
|
||||
sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("stat.log"));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< remote_address
|
||||
<< expr::if_(expr::has_attr(received_size))
|
||||
[
|
||||
expr::stream << " -> " << received_size << " bytes: "
|
||||
]
|
||||
<< expr::if_(expr::has_attr(sent_size))
|
||||
[
|
||||
expr::stream << " <- " << sent_size << " bytes: "
|
||||
]
|
||||
<< expr::smessage
|
||||
);
|
||||
|
||||
sink->set_filter(channel == "stat");
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Register other common attributes, such as time stamp and record counter
|
||||
logging::add_common_attributes();
|
||||
|
||||
// Emulate network activity
|
||||
network_connection conn;
|
||||
|
||||
conn.on_connected("11.22.33.44");
|
||||
conn.on_data_received(123);
|
||||
conn.on_data_sent(321);
|
||||
conn.on_disconnected();
|
||||
|
||||
return 0;
|
||||
}
|
134
example/doc/sources_severity.cpp
Normal file
134
example/doc/sources_severity.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <boost/move/utility.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sources_severity
|
||||
// We define our own severity levels
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
notification,
|
||||
warning,
|
||||
error,
|
||||
critical
|
||||
};
|
||||
|
||||
void logging_function()
|
||||
{
|
||||
// The logger implicitly adds a source-specific attribute 'Severity'
|
||||
// of type 'severity_level' on construction
|
||||
src::severity_logger< severity_level > slg;
|
||||
|
||||
BOOST_LOG_SEV(slg, normal) << "A regular message";
|
||||
BOOST_LOG_SEV(slg, warning) << "Something bad is going on but I can handle it";
|
||||
BOOST_LOG_SEV(slg, critical) << "Everything crumbles, shoot me now!";
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_sources_default_severity
|
||||
void default_severity()
|
||||
{
|
||||
// The default severity can be specified in constructor.
|
||||
src::severity_logger< severity_level > error_lg(keywords::severity = error);
|
||||
|
||||
BOOST_LOG(error_lg) << "An error level log record (by default)";
|
||||
|
||||
// The explicitly specified level overrides the default
|
||||
BOOST_LOG_SEV(error_lg, warning) << "A warning level log record (overrode the default)";
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_sources_severity_manual
|
||||
void manual_logging()
|
||||
{
|
||||
src::severity_logger< severity_level > slg;
|
||||
|
||||
logging::record rec = slg.open_record(keywords::severity = normal);
|
||||
if (rec)
|
||||
{
|
||||
logging::record_ostream strm(rec);
|
||||
strm << "A regular message";
|
||||
strm.flush();
|
||||
slg.push_record(boost::move(rec));
|
||||
}
|
||||
}
|
||||
//]
|
||||
|
||||
// The operator puts a human-friendly representation of the severity level to the stream
|
||||
std::ostream& operator<< (std::ostream& strm, severity_level level)
|
||||
{
|
||||
static const char* strings[] =
|
||||
{
|
||||
"normal",
|
||||
"notification",
|
||||
"warning",
|
||||
"error",
|
||||
"critical"
|
||||
};
|
||||
|
||||
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
|
||||
strm << strings[level];
|
||||
else
|
||||
strm << static_cast< int >(level);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("sample.log"));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< expr::attr< unsigned int >("LineID")
|
||||
<< ": <" << expr::attr< severity_level >("Severity")
|
||||
<< ">\t"
|
||||
<< expr::smessage
|
||||
);
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add attributes
|
||||
logging::add_common_attributes();
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
|
||||
logging_function();
|
||||
default_severity();
|
||||
manual_logging();
|
||||
|
||||
return 0;
|
||||
}
|
110
example/doc/sources_severity_channel.cpp
Normal file
110
example/doc/sources_severity_channel.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sources/global_logger_storage.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sources_severity_channel
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
notification,
|
||||
warning,
|
||||
error,
|
||||
critical
|
||||
};
|
||||
|
||||
typedef src::severity_channel_logger_mt<
|
||||
severity_level, // the type of the severity level
|
||||
std::string // the type of the channel name
|
||||
> my_logger_mt;
|
||||
|
||||
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_INIT(my_logger, my_logger_mt)
|
||||
{
|
||||
// Specify the channel name on construction, similarly as with the channel_logger
|
||||
return my_logger_mt(keywords::channel = "my_logger");
|
||||
}
|
||||
|
||||
void logging_function()
|
||||
{
|
||||
// Do logging with the severity level. The record will have both
|
||||
// the severity level and the channel name attached.
|
||||
BOOST_LOG_SEV(my_logger::get(), normal) << "Hello, world!";
|
||||
}
|
||||
//]
|
||||
|
||||
// The operator puts a human-friendly representation of the severity level to the stream
|
||||
std::ostream& operator<< (std::ostream& strm, severity_level level)
|
||||
{
|
||||
static const char* strings[] =
|
||||
{
|
||||
"normal",
|
||||
"notification",
|
||||
"warning",
|
||||
"error",
|
||||
"critical"
|
||||
};
|
||||
|
||||
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
|
||||
strm << strings[level];
|
||||
else
|
||||
strm << static_cast< int >(level);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("sample.log"));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< expr::attr< unsigned int >("LineID")
|
||||
<< ": <" << expr::attr< severity_level >("Severity")
|
||||
<< ">\t"
|
||||
<< "[" << expr::attr< std::string >("Channel") << "] "
|
||||
<< expr::smessage
|
||||
);
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add attributes
|
||||
logging::add_common_attributes();
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging_function();
|
||||
|
||||
return 0;
|
||||
}
|
155
example/doc/tutorial_attributes.cpp
Normal file
155
example/doc/tutorial_attributes.cpp
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes.hpp>
|
||||
#include <boost/log/sources/basic_logger.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/attributes/scoped_attribute.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
// We define our own severity levels
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
notification,
|
||||
warning,
|
||||
error,
|
||||
critical
|
||||
};
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(scope, "Scope", attrs::named_scope::value_type)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(timeline, "Timeline", attrs::timer::value_type)
|
||||
|
||||
void logging_function()
|
||||
{
|
||||
src::severity_logger< severity_level > slg;
|
||||
|
||||
BOOST_LOG_SEV(slg, normal) << "A regular message";
|
||||
BOOST_LOG_SEV(slg, warning) << "Something bad is going on but I can handle it";
|
||||
BOOST_LOG_SEV(slg, critical) << "Everything crumbles, shoot me now!";
|
||||
}
|
||||
|
||||
//[ example_tutorial_attributes_named_scope
|
||||
void named_scope_logging()
|
||||
{
|
||||
BOOST_LOG_NAMED_SCOPE("named_scope_logging");
|
||||
|
||||
src::severity_logger< severity_level > slg;
|
||||
|
||||
BOOST_LOG_SEV(slg, normal) << "Hello from the function named_scope_logging!";
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_tutorial_attributes_tagged_logging
|
||||
void tagged_logging()
|
||||
{
|
||||
src::severity_logger< severity_level > slg;
|
||||
slg.add_attribute("Tag", attrs::constant< std::string >("My tag value"));
|
||||
|
||||
BOOST_LOG_SEV(slg, normal) << "Here goes the tagged record";
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_tutorial_attributes_timed_logging
|
||||
void timed_logging()
|
||||
{
|
||||
BOOST_LOG_SCOPED_THREAD_ATTR("Timeline", attrs::timer());
|
||||
|
||||
src::severity_logger< severity_level > slg;
|
||||
BOOST_LOG_SEV(slg, normal) << "Starting to time nested functions";
|
||||
|
||||
logging_function();
|
||||
|
||||
BOOST_LOG_SEV(slg, normal) << "Stopping to time nested functions";
|
||||
}
|
||||
//]
|
||||
|
||||
// The operator puts a human-friendly representation of the severity level to the stream
|
||||
std::ostream& operator<< (std::ostream& strm, severity_level level)
|
||||
{
|
||||
static const char* strings[] =
|
||||
{
|
||||
"normal",
|
||||
"notification",
|
||||
"warning",
|
||||
"error",
|
||||
"critical"
|
||||
};
|
||||
|
||||
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
|
||||
strm << strings[level];
|
||||
else
|
||||
strm << static_cast< int >(level);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("sample.log"));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
<< std::hex << std::setw(8) << std::setfill('0') << line_id << std::dec << std::setfill(' ')
|
||||
<< ": <" << severity << ">\t"
|
||||
<< "(" << scope << ") "
|
||||
<< expr::if_(expr::has_attr(tag_attr))
|
||||
[
|
||||
expr::stream << "[" << tag_attr << "] "
|
||||
]
|
||||
<< expr::if_(expr::has_attr(timeline))
|
||||
[
|
||||
expr::stream << "[" << timeline << "] "
|
||||
]
|
||||
<< expr::smessage
|
||||
);
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add attributes
|
||||
logging::add_common_attributes();
|
||||
logging::core::get()->add_global_attribute("Scope", attrs::named_scope());
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
|
||||
named_scope_logging();
|
||||
tagged_logging();
|
||||
timed_logging();
|
||||
|
||||
return 0;
|
||||
}
|
93
example/doc/tutorial_file.cpp
Normal file
93
example/doc/tutorial_file.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/text_file_backend.hpp>
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
#if 0
|
||||
|
||||
//[ example_tutorial_file_simple
|
||||
void init()
|
||||
{
|
||||
logging::add_file_log("sample.log");
|
||||
|
||||
logging::core::get()->set_filter
|
||||
(
|
||||
logging::trivial::severity >= logging::trivial::info
|
||||
);
|
||||
}
|
||||
//]
|
||||
|
||||
// We need this due to this bug: https://svn.boost.org/trac/boost/ticket/4416
|
||||
//[ example_tutorial_file_advanced_no_callouts
|
||||
void init()
|
||||
{
|
||||
logging::add_file_log
|
||||
(
|
||||
keywords::file_name = "sample_%N.log",
|
||||
keywords::rotation_size = 10 * 1024 * 1024,
|
||||
keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
|
||||
keywords::format = "[%TimeStamp%]: %Message%"
|
||||
);
|
||||
|
||||
logging::core::get()->set_filter
|
||||
(
|
||||
logging::trivial::severity >= logging::trivial::info
|
||||
);
|
||||
}
|
||||
//]
|
||||
|
||||
#else
|
||||
|
||||
//[ example_tutorial_file_advanced
|
||||
void init()
|
||||
{
|
||||
logging::add_file_log
|
||||
(
|
||||
keywords::file_name = "sample_%N.log", /*< file name pattern >*/
|
||||
keywords::rotation_size = 10 * 1024 * 1024, /*< rotate files every 10 MiB... >*/
|
||||
keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0), /*< ...or at midnight >*/
|
||||
keywords::format = "[%TimeStamp%]: %Message%" /*< log record format >*/
|
||||
);
|
||||
|
||||
logging::core::get()->set_filter
|
||||
(
|
||||
logging::trivial::severity >= logging::trivial::info
|
||||
);
|
||||
}
|
||||
//]
|
||||
|
||||
#endif
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging::add_common_attributes();
|
||||
|
||||
using namespace logging::trivial;
|
||||
src::severity_logger< severity_level > lg;
|
||||
|
||||
BOOST_LOG_SEV(lg, trace) << "A trace severity message";
|
||||
BOOST_LOG_SEV(lg, debug) << "A debug severity message";
|
||||
BOOST_LOG_SEV(lg, info) << "An informational severity message";
|
||||
BOOST_LOG_SEV(lg, warning) << "A warning severity message";
|
||||
BOOST_LOG_SEV(lg, error) << "An error severity message";
|
||||
BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
|
||||
|
||||
return 0;
|
||||
}
|
46
example/doc/tutorial_file_manual.cpp
Normal file
46
example/doc/tutorial_file_manual.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
|
||||
//[ example_tutorial_file_manual
|
||||
void init()
|
||||
{
|
||||
// Construct the sink
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
// Add a stream to write log to
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("sample.log"));
|
||||
|
||||
// Register the sink in the logging core
|
||||
logging::core::get()->add_sink(sink);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
|
||||
src::logger lg;
|
||||
BOOST_LOG(lg) << "Hello world!";
|
||||
|
||||
return 0;
|
||||
}
|
192
example/doc/tutorial_filtering.cpp
Normal file
192
example/doc/tutorial_filtering.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/phoenix/bind.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes.hpp>
|
||||
#include <boost/log/sources/basic_logger.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/attributes/scoped_attribute.hpp>
|
||||
#include <boost/log/utility/value_ref.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
// We define our own severity levels
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
notification,
|
||||
warning,
|
||||
error,
|
||||
critical
|
||||
};
|
||||
|
||||
// The operator puts a human-friendly representation of the severity level to the stream
|
||||
std::ostream& operator<< (std::ostream& strm, severity_level level)
|
||||
{
|
||||
static const char* strings[] =
|
||||
{
|
||||
"normal",
|
||||
"notification",
|
||||
"warning",
|
||||
"error",
|
||||
"critical"
|
||||
};
|
||||
|
||||
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
|
||||
strm << strings[level];
|
||||
else
|
||||
strm << static_cast< int >(level);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
//[ example_tutorial_filtering
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(line_id, "LineID", unsigned int)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
|
||||
|
||||
void init()
|
||||
{
|
||||
// Setup the common formatter for all sinks
|
||||
logging::formatter fmt = expr::stream
|
||||
<< std::setw(6) << std::setfill('0') << line_id << std::setfill(' ')
|
||||
<< ": <" << severity << ">\t"
|
||||
<< expr::if_(expr::has_attr(tag_attr))
|
||||
[
|
||||
expr::stream << "[" << tag_attr << "] "
|
||||
]
|
||||
<< expr::smessage;
|
||||
|
||||
// Initialize sinks
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("full.log"));
|
||||
|
||||
sink->set_formatter(fmt);
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("important.log"));
|
||||
|
||||
sink->set_formatter(fmt);
|
||||
|
||||
sink->set_filter(severity >= warning || (expr::has_attr(tag_attr) && tag_attr == "IMPORTANT_MESSAGE"));
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add attributes
|
||||
logging::add_common_attributes();
|
||||
}
|
||||
//]
|
||||
|
||||
#if 0
|
||||
|
||||
//[ example_tutorial_filtering_bind
|
||||
bool my_filter(logging::value_ref< severity_level > const& level, logging::value_ref< std::string > const& tag)
|
||||
{
|
||||
return level >= warning || tag == "IMPORTANT_MESSAGE";
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
//<-
|
||||
|
||||
// Setup the common formatter for all sinks
|
||||
logging::formatter fmt = expr::stream
|
||||
<< std::setw(6) << std::setfill('0') << line_id << std::setfill(' ')
|
||||
<< ": <" << severity << ">\t"
|
||||
<< expr::if_(expr::has_attr(tag_attr))
|
||||
[
|
||||
expr::stream << "[" << tag_attr << "] "
|
||||
]
|
||||
<< expr::smessage;
|
||||
|
||||
// Initialize sinks
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("full.log"));
|
||||
|
||||
sink->set_formatter(fmt);
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("important.log"));
|
||||
|
||||
sink->set_formatter(fmt);
|
||||
|
||||
//->
|
||||
// ...
|
||||
|
||||
namespace phoenix = boost::phoenix;
|
||||
sink->set_filter(phoenix::bind(&my_filter, severity, tag_attr));
|
||||
|
||||
// ...
|
||||
//<-
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add attributes
|
||||
logging::add_common_attributes();
|
||||
|
||||
//->
|
||||
}
|
||||
//]
|
||||
|
||||
#endif
|
||||
|
||||
void logging_function()
|
||||
{
|
||||
src::severity_logger< severity_level > slg;
|
||||
|
||||
BOOST_LOG_SEV(slg, normal) << "A regular message";
|
||||
BOOST_LOG_SEV(slg, warning) << "Something bad is going on but I can handle it";
|
||||
BOOST_LOG_SEV(slg, critical) << "Everything crumbles, shoot me now!";
|
||||
|
||||
{
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("Tag", "IMPORTANT_MESSAGE");
|
||||
BOOST_LOG_SEV(slg, normal) << "An important message";
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
|
||||
logging_function();
|
||||
|
||||
return 0;
|
||||
}
|
73
example/doc/tutorial_fmt_custom.cpp
Normal file
73
example/doc/tutorial_fmt_custom.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/formatting_ostream.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/attributes/value_extraction.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
|
||||
//[ example_tutorial_formatters_custom
|
||||
void my_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
|
||||
{
|
||||
// Get the LineID attribute value and put it into the stream
|
||||
strm << logging::extract< unsigned int >("LineID", rec) << ": ";
|
||||
|
||||
// The same for the severity level.
|
||||
// The simplified syntax is possible if attribute keywords are used.
|
||||
strm << "<" << rec[logging::trivial::severity] << "> ";
|
||||
|
||||
// Finally, put the record message to the stream
|
||||
strm << rec[expr::smessage];
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("sample.log"));
|
||||
|
||||
sink->set_formatter(&my_formatter);
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging::add_common_attributes();
|
||||
|
||||
using namespace logging::trivial;
|
||||
src::severity_logger< severity_level > lg;
|
||||
|
||||
BOOST_LOG_SEV(lg, trace) << "A trace severity message";
|
||||
BOOST_LOG_SEV(lg, debug) << "A debug severity message";
|
||||
BOOST_LOG_SEV(lg, info) << "An informational severity message";
|
||||
BOOST_LOG_SEV(lg, warning) << "A warning severity message";
|
||||
BOOST_LOG_SEV(lg, error) << "An error severity message";
|
||||
BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
|
||||
|
||||
return 0;
|
||||
}
|
66
example/doc/tutorial_fmt_format.cpp
Normal file
66
example/doc/tutorial_fmt_format.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_tutorial_formatters_format
|
||||
void init()
|
||||
{
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("sample.log"));
|
||||
|
||||
// This makes the sink to write log records that look like this:
|
||||
// 1: <normal> A normal severity message
|
||||
// 2: <error> An error severity message
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::format("%1%: <%2%> %3%")
|
||||
% expr::attr< unsigned int >("LineID")
|
||||
% logging::trivial::severity
|
||||
% expr::smessage
|
||||
);
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging::add_common_attributes();
|
||||
|
||||
using namespace logging::trivial;
|
||||
src::severity_logger< severity_level > lg;
|
||||
|
||||
BOOST_LOG_SEV(lg, trace) << "A trace severity message";
|
||||
BOOST_LOG_SEV(lg, debug) << "A debug severity message";
|
||||
BOOST_LOG_SEV(lg, info) << "An informational severity message";
|
||||
BOOST_LOG_SEV(lg, warning) << "A warning severity message";
|
||||
BOOST_LOG_SEV(lg, error) << "An error severity message";
|
||||
BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
|
||||
|
||||
return 0;
|
||||
}
|
80
example/doc/tutorial_fmt_stream.cpp
Normal file
80
example/doc/tutorial_fmt_stream.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_tutorial_formatters_stream
|
||||
void init()
|
||||
{
|
||||
logging::add_file_log
|
||||
(
|
||||
keywords::file_name = "sample_%N.log",
|
||||
// This makes the sink to write log records that look like this:
|
||||
// 1: <normal> A normal severity message
|
||||
// 2: <error> An error severity message
|
||||
keywords::format =
|
||||
(
|
||||
expr::stream
|
||||
<< expr::attr< unsigned int >("LineID")
|
||||
<< ": <" << logging::trivial::severity
|
||||
<< "> " << expr::smessage
|
||||
)
|
||||
);
|
||||
}
|
||||
//]
|
||||
|
||||
#if 0
|
||||
|
||||
//[ example_tutorial_formatters_stream_date_time
|
||||
void init()
|
||||
{
|
||||
logging::add_file_log
|
||||
(
|
||||
keywords::file_name = "sample_%N.log",
|
||||
// This makes the sink to write log records that look like this:
|
||||
// YYYY-MM-DD HH:MI:SS: <normal> A normal severity message
|
||||
// YYYY-MM-DD HH:MI:SS: <error> An error severity message
|
||||
keywords::format =
|
||||
(
|
||||
expr::stream
|
||||
<< expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
|
||||
<< ": <" << logging::trivial::severity
|
||||
<< "> " << expr::smessage
|
||||
)
|
||||
);
|
||||
}
|
||||
//]
|
||||
|
||||
#endif
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging::add_common_attributes();
|
||||
|
||||
using namespace logging::trivial;
|
||||
src::severity_logger< severity_level > lg;
|
||||
|
||||
BOOST_LOG_SEV(lg, trace) << "A trace severity message";
|
||||
BOOST_LOG_SEV(lg, debug) << "A debug severity message";
|
||||
BOOST_LOG_SEV(lg, info) << "An informational severity message";
|
||||
BOOST_LOG_SEV(lg, warning) << "A warning severity message";
|
||||
BOOST_LOG_SEV(lg, error) << "An error severity message";
|
||||
BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
|
||||
|
||||
return 0;
|
||||
}
|
65
example/doc/tutorial_fmt_stream_manual.cpp
Normal file
65
example/doc/tutorial_fmt_stream_manual.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_ostream_backend.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_tutorial_formatters_stream_manual
|
||||
void init()
|
||||
{
|
||||
typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
|
||||
boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();
|
||||
|
||||
sink->locked_backend()->add_stream(
|
||||
boost::make_shared< std::ofstream >("sample.log"));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::stream
|
||||
// line id will be written in hex, 8-digits, zero-filled
|
||||
<< std::hex << std::setw(8) << std::setfill('0') << expr::attr< unsigned int >("LineID")
|
||||
<< ": <" << logging::trivial::severity
|
||||
<< "> " << expr::smessage
|
||||
);
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging::add_common_attributes();
|
||||
|
||||
using namespace logging::trivial;
|
||||
src::severity_logger< severity_level > lg;
|
||||
|
||||
BOOST_LOG_SEV(lg, trace) << "A trace severity message";
|
||||
BOOST_LOG_SEV(lg, debug) << "A debug severity message";
|
||||
BOOST_LOG_SEV(lg, info) << "An informational severity message";
|
||||
BOOST_LOG_SEV(lg, warning) << "A warning severity message";
|
||||
BOOST_LOG_SEV(lg, error) << "An error severity message";
|
||||
BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
|
||||
|
||||
return 0;
|
||||
}
|
45
example/doc/tutorial_fmt_string.cpp
Normal file
45
example/doc/tutorial_fmt_string.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_tutorial_formatters_string
|
||||
void init()
|
||||
{
|
||||
logging::add_file_log
|
||||
(
|
||||
keywords::file_name = "sample_%N.log",
|
||||
keywords::format = "[%TimeStamp%]: %Message%"
|
||||
);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
logging::add_common_attributes();
|
||||
|
||||
using namespace logging::trivial;
|
||||
src::severity_logger< severity_level > lg;
|
||||
|
||||
BOOST_LOG_SEV(lg, trace) << "A trace severity message";
|
||||
BOOST_LOG_SEV(lg, debug) << "A debug severity message";
|
||||
BOOST_LOG_SEV(lg, info) << "An informational severity message";
|
||||
BOOST_LOG_SEV(lg, warning) << "A warning severity message";
|
||||
BOOST_LOG_SEV(lg, error) << "An error severity message";
|
||||
BOOST_LOG_SEV(lg, fatal) << "A fatal severity message";
|
||||
|
||||
return 0;
|
||||
}
|
52
example/doc/tutorial_logging.cpp
Normal file
52
example/doc/tutorial_logging.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/move/utility.hpp>
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sources/global_logger_storage.hpp>
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace src = boost::log::sources;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::logger_mt)
|
||||
|
||||
void logging_function1()
|
||||
{
|
||||
src::logger lg;
|
||||
|
||||
//[ example_tutorial_logging_manual_logging
|
||||
logging::record rec = lg.open_record();
|
||||
if (rec)
|
||||
{
|
||||
logging::record_ostream strm(rec);
|
||||
strm << "Hello, World!";
|
||||
strm.flush();
|
||||
lg.push_record(boost::move(rec));
|
||||
}
|
||||
//]
|
||||
}
|
||||
|
||||
void logging_function2()
|
||||
{
|
||||
src::logger_mt& lg = my_logger::get();
|
||||
BOOST_LOG(lg) << "Greetings from the global logger!";
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
logging::add_file_log("sample.log");
|
||||
logging::add_common_attributes();
|
||||
|
||||
logging_function1();
|
||||
logging_function2();
|
||||
|
||||
return 0;
|
||||
}
|
22
example/doc/tutorial_trivial.cpp
Normal file
22
example/doc/tutorial_trivial.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
//[ example_tutorial_trivial
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
|
||||
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
|
||||
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
|
||||
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
|
||||
BOOST_LOG_TRIVIAL(error) << "An error severity message";
|
||||
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
|
||||
|
||||
return 0;
|
||||
}
|
||||
//]
|
36
example/doc/tutorial_trivial_flt.cpp
Normal file
36
example/doc/tutorial_trivial_flt.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
|
||||
//[ example_tutorial_trivial_with_filtering
|
||||
void init()
|
||||
{
|
||||
logging::core::get()->set_filter
|
||||
(
|
||||
logging::trivial::severity >= logging::trivial::info
|
||||
);
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init();
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
|
||||
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
|
||||
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
|
||||
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
|
||||
BOOST_LOG_TRIVIAL(error) << "An error severity message";
|
||||
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
|
||||
|
||||
return 0;
|
||||
}
|
||||
//]
|
97
example/doc/util_dynamic_type_disp.cpp
Normal file
97
example/doc/util_dynamic_type_disp.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/log/utility/type_dispatch/dynamic_type_dispatcher.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
|
||||
// Base interface for the custom opaque value
|
||||
struct my_value_base
|
||||
{
|
||||
virtual ~my_value_base() {}
|
||||
virtual bool dispatch(logging::type_dispatcher& dispatcher) const = 0;
|
||||
};
|
||||
|
||||
// A simple attribute value
|
||||
template< typename T >
|
||||
struct my_value :
|
||||
public my_value_base
|
||||
{
|
||||
T m_value;
|
||||
|
||||
explicit my_value(T const& value) : m_value(value) {}
|
||||
|
||||
// The function passes the contained type into the dispatcher
|
||||
bool dispatch(logging::type_dispatcher& dispatcher) const
|
||||
{
|
||||
logging::type_dispatcher::callback< T > cb = dispatcher.get_callback< T >();
|
||||
if (cb)
|
||||
{
|
||||
cb(m_value);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//[ example_util_dynamic_type_dispatcher
|
||||
// Visitor functions for the supported types
|
||||
void on_int(int const& value)
|
||||
{
|
||||
std::cout << "Received int value = " << value << std::endl;
|
||||
}
|
||||
|
||||
void on_double(double const& value)
|
||||
{
|
||||
std::cout << "Received double value = " << value << std::endl;
|
||||
}
|
||||
|
||||
void on_string(std::string const& value)
|
||||
{
|
||||
std::cout << "Received string value = " << value << std::endl;
|
||||
}
|
||||
|
||||
logging::dynamic_type_dispatcher disp;
|
||||
|
||||
// The function initializes the dispatcher object
|
||||
void init_disp()
|
||||
{
|
||||
// Register type visitors
|
||||
disp.register_type< int >(&on_int);
|
||||
disp.register_type< double >(&on_double);
|
||||
disp.register_type< std::string >(&on_string);
|
||||
}
|
||||
|
||||
// Prints the supplied value
|
||||
bool print(my_value_base const& val)
|
||||
{
|
||||
return val.dispatch(disp);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
init_disp();
|
||||
|
||||
// These two attributes are supported by the dispatcher
|
||||
bool res = print(my_value< std::string >("Hello world!"));
|
||||
assert(res);
|
||||
|
||||
res = print(my_value< double >(1.2));
|
||||
assert(res);
|
||||
|
||||
// This one is not
|
||||
res = print(my_value< float >(-4.3f));
|
||||
assert(!res);
|
||||
|
||||
return 0;
|
||||
}
|
73
example/doc/util_manip_to_log.cpp
Normal file
73
example/doc/util_manip_to_log.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <boost/log/utility/formatting_ostream.hpp>
|
||||
#include <boost/log/utility/manipulators/to_log.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
|
||||
//[ example_utility_manipulators_to_log
|
||||
std::ostream& operator<<
|
||||
(
|
||||
std::ostream& strm,
|
||||
logging::to_log_manip< int > const& manip
|
||||
)
|
||||
{
|
||||
strm << std::setw(4) << std::setfill('0') << std::hex << manip.get() << std::dec;
|
||||
return strm;
|
||||
}
|
||||
|
||||
void test_manip()
|
||||
{
|
||||
std::cout << "Regular output: " << 1010 << std::endl;
|
||||
std::cout << "Log output: " << logging::to_log(1010) << std::endl;
|
||||
}
|
||||
//]
|
||||
|
||||
//[ example_utility_manipulators_to_log_with_tag
|
||||
struct tag_A;
|
||||
struct tag_B;
|
||||
|
||||
std::ostream& operator<<
|
||||
(
|
||||
std::ostream& strm,
|
||||
logging::to_log_manip< int, tag_A > const& manip
|
||||
)
|
||||
{
|
||||
strm << "A[" << manip.get() << "]";
|
||||
return strm;
|
||||
}
|
||||
|
||||
std::ostream& operator<<
|
||||
(
|
||||
std::ostream& strm,
|
||||
logging::to_log_manip< int, tag_B > const& manip
|
||||
)
|
||||
{
|
||||
strm << "B[" << manip.get() << "]";
|
||||
return strm;
|
||||
}
|
||||
|
||||
void test_manip_with_tag()
|
||||
{
|
||||
std::cout << "Regular output: " << 1010 << std::endl;
|
||||
std::cout << "Log output A: " << logging::to_log< tag_A >(1010) << std::endl;
|
||||
std::cout << "Log output B: " << logging::to_log< tag_B >(1010) << std::endl;
|
||||
}
|
||||
//]
|
||||
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
test_manip();
|
||||
test_manip_with_tag();
|
||||
|
||||
return 0;
|
||||
}
|
94
example/doc/util_static_type_disp.cpp
Normal file
94
example/doc/util_static_type_disp.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/mpl/vector.hpp>
|
||||
#include <boost/log/utility/type_dispatch/static_type_dispatcher.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
|
||||
//[ example_util_static_type_dispatcher
|
||||
// Base interface for the custom opaque value
|
||||
struct my_value_base
|
||||
{
|
||||
virtual ~my_value_base() {}
|
||||
virtual bool dispatch(logging::type_dispatcher& dispatcher) const = 0;
|
||||
};
|
||||
|
||||
// A simple attribute value
|
||||
template< typename T >
|
||||
struct my_value :
|
||||
public my_value_base
|
||||
{
|
||||
T m_value;
|
||||
|
||||
explicit my_value(T const& value) : m_value(value) {}
|
||||
|
||||
// The function passes the contained type into the dispatcher
|
||||
bool dispatch(logging::type_dispatcher& dispatcher) const
|
||||
{
|
||||
logging::type_dispatcher::callback< T > cb = dispatcher.get_callback< T >();
|
||||
if (cb)
|
||||
{
|
||||
cb(m_value);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Value visitor for the supported types
|
||||
struct print_visitor
|
||||
{
|
||||
typedef void result_type;
|
||||
|
||||
// Implement visitation logic for all supported types
|
||||
void operator() (int const& value) const
|
||||
{
|
||||
std::cout << "Received int value = " << value << std::endl;
|
||||
}
|
||||
void operator() (double const& value) const
|
||||
{
|
||||
std::cout << "Received double value = " << value << std::endl;
|
||||
}
|
||||
void operator() (std::string const& value) const
|
||||
{
|
||||
std::cout << "Received string value = " << value << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
// Prints the supplied value
|
||||
bool print(my_value_base const& val)
|
||||
{
|
||||
typedef boost::mpl::vector< int, double, std::string > types;
|
||||
|
||||
print_visitor visitor;
|
||||
logging::static_type_dispatcher< types > disp(visitor);
|
||||
|
||||
return val.dispatch(disp);
|
||||
}
|
||||
//]
|
||||
|
||||
int main(int, char*[])
|
||||
{
|
||||
// These two attributes are supported by the dispatcher
|
||||
bool res = print(my_value< std::string >("Hello world!"));
|
||||
assert(res);
|
||||
|
||||
res = print(my_value< double >(1.2));
|
||||
assert(res);
|
||||
|
||||
// This one is not
|
||||
res = print(my_value< float >(-4.3f));
|
||||
assert(!res);
|
||||
|
||||
return 0;
|
||||
}
|
44
example/event_log/Jamfile.v2
Normal file
44
example/event_log/Jamfile.v2
Normal file
@ -0,0 +1,44 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
import os ;
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<library>/boost/thread//boost_thread
|
||||
<threading>multi
|
||||
;
|
||||
|
||||
if [ os.name ] = "NT"
|
||||
{
|
||||
lib event_log_messages
|
||||
: event_log_messages.mc
|
||||
: <linkflags>-noentry
|
||||
# <name>event_log_messages
|
||||
;
|
||||
|
||||
exe event_log
|
||||
: main.cpp
|
||||
: <implicit-dependency>event_log_messages
|
||||
;
|
||||
}
|
58
example/event_log/event_log_messages.mc
Normal file
58
example/event_log/event_log_messages.mc
Normal file
@ -0,0 +1,58 @@
|
||||
; /* --------------------------------------------------------
|
||||
; HEADER SECTION
|
||||
; */
|
||||
SeverityNames=(Debug=0x0:MY_SEVERITY_DEBUG
|
||||
Info=0x1:MY_SEVERITY_INFO
|
||||
Warning=0x2:MY_SEVERITY_WARNING
|
||||
Error=0x3:MY_SEVERITY_ERROR
|
||||
)
|
||||
|
||||
; /* --------------------------------------------------------
|
||||
; MESSAGE DEFINITION SECTION
|
||||
; */
|
||||
|
||||
MessageIdTypedef=WORD
|
||||
|
||||
MessageId=0x1
|
||||
SymbolicName=MY_CATEGORY_1
|
||||
Language=English
|
||||
Category 1
|
||||
.
|
||||
|
||||
MessageId=0x2
|
||||
SymbolicName=MY_CATEGORY_2
|
||||
Language=English
|
||||
Category 2
|
||||
.
|
||||
|
||||
MessageId=0x3
|
||||
SymbolicName=MY_CATEGORY_3
|
||||
Language=English
|
||||
Category 3
|
||||
.
|
||||
|
||||
MessageIdTypedef=DWORD
|
||||
|
||||
MessageId=0x100
|
||||
Severity=Warning
|
||||
Facility=Application
|
||||
SymbolicName=LOW_DISK_SPACE_MSG
|
||||
Language=English
|
||||
The drive %1 has low free disk space. At least %2 Mb of free space is recommended.
|
||||
.
|
||||
|
||||
MessageId=0x101
|
||||
Severity=Error
|
||||
Facility=Application
|
||||
SymbolicName=DEVICE_INACCESSIBLE_MSG
|
||||
Language=English
|
||||
The drive %1 is not accessible.
|
||||
.
|
||||
|
||||
MessageId=0x102
|
||||
Severity=Info
|
||||
Facility=Application
|
||||
SymbolicName=SUCCEEDED_MSG
|
||||
Language=English
|
||||
Operation finished successfully in %1 seconds.
|
||||
.
|
190
example/event_log/main.cpp
Normal file
190
example/event_log/main.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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 16.11.2008
|
||||
*
|
||||
* \brief An example of logging into Windows event log.
|
||||
*
|
||||
* The example shows the basic usage of the Windows NT event log backend.
|
||||
* The code defines custom severity levels, initializes the sink and a couple of
|
||||
* attributes to test with, and writes several records at different levels.
|
||||
* As a result the written records should appear in the Application log, and
|
||||
* should be displayed correctly with the Windows event log viewer.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/attributes.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/event_log_backend.hpp>
|
||||
|
||||
#if !defined(WIN32_LEAN_AND_MEAN)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include "event_log_messages.h"
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
//[ example_sinks_event_log_severity
|
||||
// Define application-specific severity levels
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
warning,
|
||||
error
|
||||
};
|
||||
//]
|
||||
|
||||
void init_logging()
|
||||
{
|
||||
//[ example_sinks_event_log_create_backend
|
||||
// Create an event log sink
|
||||
boost::shared_ptr< sinks::event_log_backend > backend(
|
||||
new sinks::event_log_backend((
|
||||
keywords::message_file = "%SystemDir%\\event_log_messages.dll",
|
||||
keywords::log_name = "My Application",
|
||||
keywords::log_source = "My Source"
|
||||
))
|
||||
);
|
||||
//]
|
||||
|
||||
//[ example_sinks_event_log_event_composer
|
||||
// Create an event composer. It is initialized with the event identifier mapping.
|
||||
sinks::event_log::event_composer composer(
|
||||
sinks::event_log::direct_event_id_mapping< int >("EventID"));
|
||||
|
||||
// For each event described in the message file, set up the insertion string formatters
|
||||
composer[LOW_DISK_SPACE_MSG]
|
||||
// the first placeholder in the message
|
||||
// will be replaced with contents of the "Drive" attribute
|
||||
% expr::attr< std::string >("Drive")
|
||||
// the second placeholder in the message
|
||||
// will be replaced with contents of the "Size" attribute
|
||||
% expr::attr< boost::uintmax_t >("Size");
|
||||
|
||||
composer[DEVICE_INACCESSIBLE_MSG]
|
||||
% expr::attr< std::string >("Drive");
|
||||
|
||||
composer[SUCCEEDED_MSG]
|
||||
% expr::attr< unsigned int >("Duration");
|
||||
|
||||
// Then put the composer to the backend
|
||||
backend->set_event_composer(composer);
|
||||
//]
|
||||
|
||||
//[ example_sinks_event_log_mappings
|
||||
// We'll have to map our custom levels to the event log event types
|
||||
sinks::event_log::custom_event_type_mapping< severity_level > type_mapping("Severity");
|
||||
type_mapping[normal] = sinks::event_log::make_event_type(MY_SEVERITY_INFO);
|
||||
type_mapping[warning] = sinks::event_log::make_event_type(MY_SEVERITY_WARNING);
|
||||
type_mapping[error] = sinks::event_log::make_event_type(MY_SEVERITY_ERROR);
|
||||
|
||||
backend->set_event_type_mapper(type_mapping);
|
||||
|
||||
// Same for event categories.
|
||||
// Usually event categories can be restored by the event identifier.
|
||||
sinks::event_log::custom_event_category_mapping< int > cat_mapping("EventID");
|
||||
cat_mapping[LOW_DISK_SPACE_MSG] = sinks::event_log::make_event_category(MY_CATEGORY_1);
|
||||
cat_mapping[DEVICE_INACCESSIBLE_MSG] = sinks::event_log::make_event_category(MY_CATEGORY_2);
|
||||
cat_mapping[SUCCEEDED_MSG] = sinks::event_log::make_event_category(MY_CATEGORY_3);
|
||||
|
||||
backend->set_event_category_mapper(cat_mapping);
|
||||
//]
|
||||
|
||||
//[ example_sinks_event_log_register_sink
|
||||
// Create the frontend for the sink
|
||||
boost::shared_ptr< sinks::synchronous_sink< sinks::event_log_backend > > sink(
|
||||
new sinks::synchronous_sink< sinks::event_log_backend >(backend));
|
||||
|
||||
// Set up filter to pass only records that have the necessary attribute
|
||||
sink->set_filter(expr::has_attr< int >("EventID"));
|
||||
|
||||
logging::core::get()->add_sink(sink);
|
||||
//]
|
||||
}
|
||||
|
||||
//[ example_sinks_event_log_facilities
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(event_logger, src::severity_logger_mt< severity_level >)
|
||||
|
||||
// The function raises an event of the disk space depletion
|
||||
void announce_low_disk_space(std::string const& drive, boost::uintmax_t size)
|
||||
{
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("EventID", (int)LOW_DISK_SPACE_MSG);
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("Drive", drive);
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("Size", size);
|
||||
// Since this record may get accepted by other sinks,
|
||||
// this message is not completely useless
|
||||
BOOST_LOG_SEV(event_logger::get(), warning) << "Low disk " << drive
|
||||
<< " space, " << size << " Mb is recommended";
|
||||
}
|
||||
|
||||
// The function raises an event of inaccessible disk drive
|
||||
void announce_device_inaccessible(std::string const& drive)
|
||||
{
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("EventID", (int)DEVICE_INACCESSIBLE_MSG);
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("Drive", drive);
|
||||
BOOST_LOG_SEV(event_logger::get(), error) << "Cannot access drive " << drive;
|
||||
}
|
||||
|
||||
// The structure is an activity guard that will emit an event upon the activity completion
|
||||
struct activity_guard
|
||||
{
|
||||
activity_guard()
|
||||
{
|
||||
// Add a stop watch attribute to measure the activity duration
|
||||
m_it = event_logger::get().add_attribute("Duration", attrs::timer()).first;
|
||||
}
|
||||
~activity_guard()
|
||||
{
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("EventID", (int)SUCCEEDED_MSG);
|
||||
BOOST_LOG_SEV(event_logger::get(), normal) << "Activity ended";
|
||||
event_logger::get().remove_attribute(m_it);
|
||||
}
|
||||
|
||||
private:
|
||||
logging::attribute_set::iterator m_it;
|
||||
};
|
||||
//]
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Initialize the library
|
||||
init_logging();
|
||||
|
||||
// Make some events
|
||||
{
|
||||
activity_guard activity;
|
||||
|
||||
announce_low_disk_space("C:", 2 * 1024 * 1024);
|
||||
announce_device_inaccessible("D:");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "FAILURE: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
33
example/keywords/Jamfile.v2
Normal file
33
example/keywords/Jamfile.v2
Normal file
@ -0,0 +1,33 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/log//boost_log_setup
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<threading>single:<define>BOOST_LOG_NO_THREADS
|
||||
<threading>multi:<library>/boost/thread//boost_thread
|
||||
;
|
||||
|
||||
exe keywords
|
||||
: main.cpp
|
||||
;
|
133
example/keywords/main.cpp
Normal file
133
example/keywords/main.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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 01.12.2012
|
||||
*
|
||||
* \brief An example of using attribute keywords.
|
||||
*/
|
||||
|
||||
// #define BOOST_LOG_USE_CHAR
|
||||
// #define BOOST_ALL_DYN_LINK 1
|
||||
// #define BOOST_LOG_DYN_LINK 1
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
#include <boost/log/utility/setup/console.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
|
||||
#include <boost/log/attributes/timer.hpp>
|
||||
#include <boost/log/attributes/named_scope.hpp>
|
||||
|
||||
#include <boost/log/sources/logger.hpp>
|
||||
|
||||
#include <boost/log/support/date_time.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace expr = boost::log::expressions;
|
||||
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;
|
||||
}
|
||||
|
||||
// Declare attribute keywords
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(_severity, "Severity", severity_level)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(_timestamp, "TimeStamp", boost::posix_time::ptime)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(_uptime, "Uptime", attrs::timer::value_type)
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(_scope, "Scope", attrs::named_scope::value_type)
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// This is a simple 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.
|
||||
logging::add_console_log(std::clog, keywords::format = "%TimeStamp%: %_%");
|
||||
|
||||
// One can also use lambda expressions to setup filters and formatters
|
||||
logging::add_file_log
|
||||
(
|
||||
"sample.log",
|
||||
keywords::filter = _severity >= warning,
|
||||
keywords::format = expr::stream
|
||||
<< expr::format_date_time(_timestamp, "%Y-%m-%d, %H:%M:%S.%f")
|
||||
<< " [" << expr::format_date_time(_uptime, "%O:%M:%S")
|
||||
<< "] [" << expr::format_named_scope(_scope, keywords::format = "%n (%f:%l)")
|
||||
<< "] <" << _severity
|
||||
<< "> " << expr::message
|
||||
/*
|
||||
keywords::format = expr::format("%1% [%2%] [%3%] <%4%> %5%")
|
||||
% expr::format_date_time(_timestamp, "%Y-%m-%d, %H:%M:%S.%f")
|
||||
% expr::format_date_time(_uptime, "%O:%M:%S")
|
||||
% expr::format_named_scope(_scope, keywords::format = "%n (%f:%l)")
|
||||
% _severity
|
||||
% expr::message
|
||||
*/
|
||||
);
|
||||
|
||||
// Also let's add some commonly used attributes, like timestamp and record counter.
|
||||
logging::add_common_attributes();
|
||||
logging::core::get()->add_thread_attribute("Scope", attrs::named_scope());
|
||||
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
// 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!";
|
||||
|
||||
// Now, let's try logging with severity
|
||||
src::severity_logger< severity_level > slg;
|
||||
|
||||
// Let's pretend we also want to profile our code, so add a special timer attribute.
|
||||
slg.add_attribute("Uptime", attrs::timer());
|
||||
|
||||
BOOST_LOG_SEV(slg, normal) << "A normal severity message, will not pass to the file";
|
||||
BOOST_LOG_SEV(slg, warning) << "A warning severity message, will pass to the file";
|
||||
BOOST_LOG_SEV(slg, error) << "An error severity message, will pass to the file";
|
||||
|
||||
return 0;
|
||||
}
|
32
example/multiple_files/Jamfile.v2
Normal file
32
example/multiple_files/Jamfile.v2
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<library>/boost/thread//boost_thread
|
||||
<threading>multi
|
||||
;
|
||||
|
||||
exe multiple_files
|
||||
: main.cpp
|
||||
;
|
105
example/multiple_files/main.cpp
Normal file
105
example/multiple_files/main.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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 26.04.2008
|
||||
*
|
||||
* \brief This example shows how to perform logging to several files simultaneously,
|
||||
* with files being created on an attribute value basis - thread identifier in this case.
|
||||
* In the example the application creates a number of threads and registers thread
|
||||
* identifiers as attributes. Every thread performs logging, and the sink separates
|
||||
* log records from different threads into different files.
|
||||
*/
|
||||
|
||||
// #define BOOST_LOG_DYN_LINK 1
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/thread/thread.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/sources/logger.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_multifile_backend.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
using boost::shared_ptr;
|
||||
|
||||
enum
|
||||
{
|
||||
THREAD_COUNT = 5,
|
||||
LOG_RECORDS_TO_WRITE = 10
|
||||
};
|
||||
|
||||
// Global logger declaration
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::logger_mt)
|
||||
|
||||
// This function is executed in a separate thread
|
||||
void thread_foo()
|
||||
{
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id());
|
||||
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
|
||||
{
|
||||
BOOST_LOG(my_logger::get()) << "Log record " << i;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a text file sink
|
||||
typedef sinks::synchronous_sink< sinks::text_multifile_backend > file_sink;
|
||||
shared_ptr< file_sink > sink(new file_sink);
|
||||
|
||||
// Set up how the file names will be generated
|
||||
sink->locked_backend()->set_file_name_composer(sinks::file::as_file_name_composer(
|
||||
expr::stream << "logs/" << expr::attr< boost::thread::id >("ThreadID") << ".log"));
|
||||
|
||||
// Set the log record formatter
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::format("%1%: [%2%] - %3%")
|
||||
% expr::attr< unsigned int >("RecordID")
|
||||
% expr::attr< boost::posix_time::ptime >("TimeStamp")
|
||||
% expr::smessage
|
||||
);
|
||||
|
||||
// Add it to the core
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add some attributes too
|
||||
logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock());
|
||||
logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >());
|
||||
|
||||
// Create threads and make some logs
|
||||
boost::thread_group threads;
|
||||
for (unsigned int i = 0; i < THREAD_COUNT; ++i)
|
||||
threads.create_thread(&thread_foo);
|
||||
|
||||
threads.join_all();
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "FAILURE: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
32
example/multiple_threads/Jamfile.v2
Normal file
32
example/multiple_threads/Jamfile.v2
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<library>/boost/thread//boost_thread
|
||||
<threading>multi
|
||||
;
|
||||
|
||||
exe multiple_threads
|
||||
: main.cpp
|
||||
;
|
116
example/multiple_threads/main.cpp
Normal file
116
example/multiple_threads/main.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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 10.06.2008
|
||||
*
|
||||
* \brief An example of logging in multiple threads.
|
||||
* 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_DYN_LINK 1
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <boost/ref.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/thread/barrier.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/empty_deleter.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
using boost::shared_ptr;
|
||||
|
||||
enum
|
||||
{
|
||||
LOG_RECORDS_TO_WRITE = 10000,
|
||||
THREAD_COUNT = 2
|
||||
};
|
||||
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::logger_mt)
|
||||
|
||||
//! This function is executed in multiple threads
|
||||
void thread_fun(boost::barrier& bar)
|
||||
{
|
||||
// Wait until all threads are created
|
||||
bar.wait();
|
||||
|
||||
// Now, do some logging
|
||||
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
|
||||
{
|
||||
BOOST_LOG(test_lg::get()) << "Log record " << i;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Open a rotating text file
|
||||
shared_ptr< std::ostream > strm(new std::ofstream("test.log"));
|
||||
if (!strm->good())
|
||||
throw std::runtime_error("Failed to open a text log file");
|
||||
|
||||
// Create a text file sink
|
||||
shared_ptr< sinks::synchronous_sink< sinks::text_ostream_backend > > sink(
|
||||
new sinks::synchronous_sink< sinks::text_ostream_backend >);
|
||||
|
||||
sink->locked_backend()->add_stream(strm);
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::format("%1%: [%2%] [%3%] - %4%")
|
||||
% expr::attr< unsigned int >("RecordID")
|
||||
% expr::attr< boost::posix_time::ptime >("TimeStamp")
|
||||
% expr::attr< attrs::current_thread_id::value_type >("ThreadID")
|
||||
% expr::smessage
|
||||
);
|
||||
|
||||
// Add it to the core
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add some attributes too
|
||||
logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock());
|
||||
logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >());
|
||||
logging::core::get()->add_global_attribute("ThreadID", attrs::current_thread_id());
|
||||
|
||||
// Create logging threads
|
||||
boost::barrier bar(THREAD_COUNT);
|
||||
boost::thread_group threads;
|
||||
for (unsigned int i = 0; i < THREAD_COUNT; ++i)
|
||||
threads.create_thread(boost::bind(&thread_fun, boost::ref(bar)));
|
||||
|
||||
// Wait until all action ends
|
||||
threads.join_all();
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "FAILURE: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
32
example/native_syslog/Jamfile.v2
Normal file
32
example/native_syslog/Jamfile.v2
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<library>/boost/thread//boost_thread
|
||||
<threading>multi
|
||||
;
|
||||
|
||||
exe native_syslog
|
||||
: main.cpp
|
||||
;
|
104
example/native_syslog/main.cpp
Normal file
104
example/native_syslog/main.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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 07.03.2009
|
||||
*
|
||||
* \brief An example of logging to a syslog server (syslogd, for example).
|
||||
*
|
||||
* The example shows how to initialize logging to a local syslog server.
|
||||
* The code creates a sink that will use native syslog API to emit messages.
|
||||
*/
|
||||
|
||||
// #define BOOST_LOG_DYN_LINK 1
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/attributes.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/syslog_backend.hpp>
|
||||
|
||||
#if defined(BOOST_LOG_USE_NATIVE_SYSLOG)
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
using boost::shared_ptr;
|
||||
|
||||
//! Define application-specific severity levels
|
||||
enum severity_levels
|
||||
{
|
||||
normal,
|
||||
warning,
|
||||
error
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a syslog sink
|
||||
shared_ptr< sinks::synchronous_sink< sinks::syslog_backend > > sink(
|
||||
new sinks::synchronous_sink< sinks::syslog_backend >(
|
||||
keywords::use_impl = sinks::syslog::native,
|
||||
keywords::facility = sinks::syslog::local7));
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::format("native_syslog: %1%: %2%")
|
||||
% expr::attr< unsigned int >("RecordID")
|
||||
% expr::smessage
|
||||
);
|
||||
|
||||
// We'll have to map our custom levels to the syslog levels
|
||||
sinks::syslog::custom_severity_mapping< severity_levels > mapping("Severity");
|
||||
mapping[normal] = sinks::syslog::info;
|
||||
mapping[warning] = sinks::syslog::warning;
|
||||
mapping[error] = sinks::syslog::critical;
|
||||
|
||||
sink->locked_backend()->set_severity_mapper(mapping);
|
||||
|
||||
// Add the sink to the core
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add some attributes too
|
||||
logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >());
|
||||
|
||||
// Do some logging
|
||||
src::severity_logger< severity_levels > lg(keywords::severity = normal);
|
||||
BOOST_LOG_SEV(lg, normal) << "A syslog record with normal level";
|
||||
BOOST_LOG_SEV(lg, warning) << "A syslog record with warning level";
|
||||
BOOST_LOG_SEV(lg, error) << "A syslog record with error level";
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "FAILURE: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
#else // defined(BOOST_LOG_USE_NATIVE_SYSLOG)
|
||||
|
||||
int main (int, char*[])
|
||||
{
|
||||
std::cout << "Native syslog API is not supported on this platform" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // defined(BOOST_LOG_USE_NATIVE_SYSLOG)
|
32
example/rotating_file/Jamfile.v2
Normal file
32
example/rotating_file/Jamfile.v2
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<library>/boost/thread//boost_thread
|
||||
<threading>multi
|
||||
;
|
||||
|
||||
exe rotating_file
|
||||
: main.cpp
|
||||
;
|
94
example/rotating_file/main.cpp
Normal file
94
example/rotating_file/main.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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 26.04.2008
|
||||
*
|
||||
* \brief An example of logging into a rotating text file.
|
||||
* 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_DYN_LINK 1
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.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/sources/logger.hpp>
|
||||
#include <boost/log/sinks/sync_frontend.hpp>
|
||||
#include <boost/log/sinks/text_file_backend.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace expr = boost::log::expressions;
|
||||
namespace keywords = boost::log::keywords;
|
||||
|
||||
using boost::shared_ptr;
|
||||
|
||||
enum { LOG_RECORDS_TO_WRITE = 10000 };
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a text file sink
|
||||
typedef sinks::synchronous_sink< sinks::text_file_backend > file_sink;
|
||||
shared_ptr< file_sink > sink(new file_sink(
|
||||
keywords::file_name = "%Y%m%d_%H%M%S_%5N.log", // file name pattern
|
||||
keywords::rotation_size = 16384 // rotation size, in characters
|
||||
));
|
||||
|
||||
// Set up where the rotated files will be stored
|
||||
sink->locked_backend()->set_file_collector(sinks::file::make_collector(
|
||||
keywords::target = "logs", // where to store rotated files
|
||||
keywords::max_size = 16 * 1024 * 1024, // maximum total size of the stored files, in bytes
|
||||
keywords::min_free_space = 100 * 1024 * 1024 // minimum free space on the drive, in bytes
|
||||
));
|
||||
|
||||
// Upon restart, scan the target directory for files matching the file_name pattern
|
||||
sink->locked_backend()->scan_for_files();
|
||||
|
||||
sink->set_formatter
|
||||
(
|
||||
expr::format("%1%: [%2%] - %3%")
|
||||
% expr::attr< unsigned int >("RecordID")
|
||||
% expr::attr< boost::posix_time::ptime >("TimeStamp")
|
||||
% expr::smessage
|
||||
);
|
||||
|
||||
// Add it to the core
|
||||
logging::core::get()->add_sink(sink);
|
||||
|
||||
// Add some attributes too
|
||||
logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock());
|
||||
logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >());
|
||||
|
||||
// Do some logging
|
||||
src::logger lg;
|
||||
for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i)
|
||||
{
|
||||
BOOST_LOG(lg) << "Some log record";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "FAILURE: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
33
example/settings_file/Jamfile.v2
Normal file
33
example/settings_file/Jamfile.v2
Normal file
@ -0,0 +1,33 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/log//boost_log_setup
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<threading>single:<define>BOOST_LOG_NO_THREADS
|
||||
<threading>multi:<library>/boost/thread//boost_thread
|
||||
;
|
||||
|
||||
exe settings_file
|
||||
: main.cpp
|
||||
;
|
90
example/settings_file/main.cpp
Normal file
90
example/settings_file/main.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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 26.04.2008
|
||||
*
|
||||
* \brief An example of initializing the library from a settings file.
|
||||
* 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_ALL_DYN_LINK 1
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/attributes.hpp>
|
||||
#include <boost/log/utility/setup/from_stream.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
|
||||
using boost::shared_ptr;
|
||||
|
||||
// Here we define our application severity levels.
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
notification,
|
||||
warning,
|
||||
error,
|
||||
critical
|
||||
};
|
||||
|
||||
// Global logger declaration
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::severity_logger< >)
|
||||
|
||||
void try_logging()
|
||||
{
|
||||
src::severity_logger< >& lg = test_lg::get();
|
||||
BOOST_LOG_SEV(lg, normal) << "This is a normal severity record";
|
||||
BOOST_LOG_SEV(lg, notification) << "This is a notification severity record";
|
||||
BOOST_LOG_SEV(lg, warning) << "This is a warning severity record";
|
||||
BOOST_LOG_SEV(lg, error) << "This is a error severity record";
|
||||
BOOST_LOG_SEV(lg, critical) << "This is a critical severity record";
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Open the file
|
||||
std::ifstream settings("settings.txt");
|
||||
if (!settings.is_open())
|
||||
{
|
||||
std::cout << "Could not open settings.txt file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read the settings and initialize logging library
|
||||
logging::init_from_stream(settings);
|
||||
|
||||
// Add some attributes
|
||||
logging::core::get()->add_global_attribute("TimeStamp", attrs::local_clock());
|
||||
|
||||
// Try logging
|
||||
try_logging();
|
||||
|
||||
// Now enable tagging and try again
|
||||
BOOST_LOG_SCOPED_THREAD_TAG("Tag", "TAGGED");
|
||||
try_logging();
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "FAILURE: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
25
example/settings_file/settings.txt
Normal file
25
example/settings_file/settings.txt
Normal file
@ -0,0 +1,25 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
[Core]
|
||||
|
||||
Filter="%Severity% >= 2"
|
||||
|
||||
|
||||
[Sinks.1]
|
||||
|
||||
Destination=Console
|
||||
Format="%TimeStamp% *** %Message%"
|
||||
Filter="%Tag% | %Severity% > 3"
|
||||
|
||||
|
||||
[Sinks.2]
|
||||
|
||||
Destination=TextFile
|
||||
FileName=test.log
|
||||
AutoFlush=true
|
||||
Format="[%TimeStamp%] %Tag% %Message%"
|
34
example/settings_file_formatter_factory/Jamfile.v2
Normal file
34
example/settings_file_formatter_factory/Jamfile.v2
Normal file
@ -0,0 +1,34 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
project
|
||||
: requirements
|
||||
<link>shared:<define>BOOST_ALL_DYN_LINK
|
||||
<logapi>unix:<define>BOOST_LOG_USE_NATIVE_SYSLOG=1
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_SCL_SECURE_NO_DEPRECATE
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_WARNINGS
|
||||
<toolset>intel-win:<define>_CRT_SECURE_NO_DEPRECATE
|
||||
<toolset>gcc:<cxxflags>-fno-strict-aliasing # avoids strict aliasing violations in other Boost components
|
||||
<toolset>gcc:<cxxflags>-ftemplate-depth-1024
|
||||
<library>/boost/log//boost_log
|
||||
<library>/boost/log//boost_log_setup
|
||||
<library>/boost/date_time//boost_date_time
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/system//boost_system
|
||||
<threading>single:<define>BOOST_LOG_NO_THREADS
|
||||
<threading>multi:<library>/boost/thread//boost_thread
|
||||
;
|
||||
|
||||
exe settings_file_formatter_factory
|
||||
: main.cpp
|
||||
;
|
||||
|
145
example/settings_file_formatter_factory/main.cpp
Normal file
145
example/settings_file_formatter_factory/main.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2007 - 2013.
|
||||
* 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 12.05.2010
|
||||
*
|
||||
* \brief An example of initializing the library from a settings file,
|
||||
* with a custom formatter for an attribute.
|
||||
*/
|
||||
|
||||
// #define BOOST_ALL_DYN_LINK 1
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <boost/ref.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/attributes.hpp>
|
||||
#include <boost/log/core/record.hpp>
|
||||
#include <boost/log/attributes/value_visitation.hpp>
|
||||
#include <boost/log/utility/setup/from_stream.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/utility/setup/formatter_parser.hpp>
|
||||
|
||||
namespace logging = boost::log;
|
||||
namespace attrs = boost::log::attributes;
|
||||
namespace src = boost::log::sources;
|
||||
|
||||
enum severity_level
|
||||
{
|
||||
normal,
|
||||
notification,
|
||||
warning,
|
||||
error,
|
||||
critical
|
||||
};
|
||||
|
||||
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::severity_logger< >)
|
||||
|
||||
//! Our custom formatter for the scope list
|
||||
struct scope_list_formatter
|
||||
{
|
||||
typedef void result_type;
|
||||
typedef attrs::named_scope::value_type scope_stack;
|
||||
|
||||
explicit scope_list_formatter(logging::attribute_name const& name) :
|
||||
name_(name)
|
||||
{
|
||||
}
|
||||
void operator()(logging::record_view const& rec, logging::formatting_ostream& strm) const
|
||||
{
|
||||
// We need to acquire the attribute value from the log record
|
||||
logging::visit< scope_stack >
|
||||
(
|
||||
name_,
|
||||
rec.attribute_values(),
|
||||
boost::bind(&scope_list_formatter::format, _1, boost::ref(strm))
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
//! This is where our custom formatting takes place
|
||||
static void format(scope_stack const& scopes, logging::formatting_ostream& strm)
|
||||
{
|
||||
scope_stack::const_iterator it = scopes.begin(), end = scopes.end();
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
strm << "\t" << it->scope_name << " [" << it->file_name << ":" << it->line << "]\n";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
logging::attribute_name name_;
|
||||
};
|
||||
|
||||
class my_scopes_formatter_factory :
|
||||
public logging::formatter_factory< char >
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* This function creates a formatter for the MyScopes attribute.
|
||||
* It effectively associates the attribute with the scope_list_formatter class
|
||||
*/
|
||||
formatter_type create_formatter(
|
||||
logging::attribute_name const& attr_name, args_map const& args)
|
||||
{
|
||||
return formatter_type(scope_list_formatter(attr_name));
|
||||
}
|
||||
};
|
||||
|
||||
//! The function initializes the logging library
|
||||
void init_logging()
|
||||
{
|
||||
// First thing - register the custom formatter for MyScopes
|
||||
logging::register_formatter_factory("MyScopes", boost::make_shared< my_scopes_formatter_factory >());
|
||||
|
||||
// Then load the settings from the file
|
||||
std::ifstream settings("settings.txt");
|
||||
if (!settings.is_open())
|
||||
throw std::runtime_error("Could not open settings.txt file");
|
||||
logging::init_from_stream(settings);
|
||||
|
||||
// Add some attributes
|
||||
logging::add_common_attributes();
|
||||
|
||||
logging::core::get()->add_global_attribute("MyScopes", attrs::named_scope());
|
||||
}
|
||||
|
||||
//! The function tests logging
|
||||
void try_logging()
|
||||
{
|
||||
BOOST_LOG_FUNCTION();
|
||||
|
||||
src::severity_logger< >& lg = test_lg::get();
|
||||
|
||||
BOOST_LOG_SEV(lg, critical) << "This is a critical severity record";
|
||||
|
||||
BOOST_LOG_NAMED_SCOPE("random name");
|
||||
BOOST_LOG_SEV(lg, error) << "This is a error severity record";
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
init_logging();
|
||||
try_logging();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "FAILURE: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
13
example/settings_file_formatter_factory/settings.txt
Normal file
13
example/settings_file_formatter_factory/settings.txt
Normal file
@ -0,0 +1,13 @@
|
||||
#
|
||||
# Copyright Andrey Semashev 2007 - 2013.
|
||||
# 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)
|
||||
#
|
||||
|
||||
[Sinks.TextFileSettings]
|
||||
Destination=TextFile
|
||||
FileName=test.log
|
||||
AutoFlush=true
|
||||
Format="[%TimeStamp%] [%Severity%]\n%MyScopes%\n\t:: %Message%"
|
||||
Asynchronous=false
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user