3 <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
4 <title>Extending the library</title>
5 <link rel="stylesheet" href="../../../../../doc/src/boostbook.css" type="text/css">
6 <meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
7 <link rel="home" href="../index.html" title="Chapter 1. Boost.Log v2">
8 <link rel="up" href="../index.html" title="Chapter 1. Boost.Log v2">
9 <link rel="prev" href="detailed/utilities.html" title="Utilities">
10 <link rel="next" href="extension/sources.html" title="Writing your own sources">
12 <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
13 <table cellpadding="2" width="100%"><tr><td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../boost.png"></td></tr></table>
15 <div class="spirit-nav">
16 <a accesskey="p" href="detailed/utilities.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../index.html"><img src="../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../index.html"><img src="../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="extension/sources.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a>
19 <div class="titlepage"><div><div><h2 class="title" style="clear: both">
20 <a name="log.extension"></a><a class="link" href="extension.html" title="Extending the library">Extending the library</a>
21 </h2></div></div></div>
22 <div class="toc"><dl class="toc">
23 <dt><span class="section"><a href="extension.html#log.extension.sinks">Writing your own sinks</a></span></dt>
24 <dt><span class="section"><a href="extension/sources.html">Writing your own sources</a></span></dt>
25 <dt><span class="section"><a href="extension/attributes.html">Writing your own attributes</a></span></dt>
26 <dt><span class="section"><a href="extension/settings.html">Extending library settings support</a></span></dt>
29 <div class="titlepage"><div><div><h3 class="title">
30 <a name="log.extension.sinks"></a><a class="link" href="extension.html#log.extension.sinks" title="Writing your own sinks">Writing your own sinks</a>
31 </h3></div></div></div>
32 <pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><code class="computeroutput"><a class="link" href="../sinks.html#header.boost.log.sinks.basic_sink_backend_hpp" title="Header <boost/log/sinks/basic_sink_backend.hpp>">boost/log/sinks/basic_sink_backend.hpp</a></code><span class="special">></span>
35 As was described in the <a class="link" href="design.html" title="Design overview">Design overview</a>
36 section, sinks consist of two parts: frontend and backend. Frontends are
37 provided by the library and usually do not need to be reimplemented. Thanks
38 to frontends, implementing backends is much easier than it could be: all
39 filtering, formatting and thread synchronization is done there.
42 In order to develop a sink backend, you derive your class from either <code class="computeroutput"><a class="link" href="../boost/log/sinks/basic_sink_backend.html" title="Struct template basic_sink_backend">basic_sink_backend</a></code>
43 or <code class="computeroutput"><a class="link" href="../boost/log/sinks/basic_fo_idm45711346315728.html" title="Struct template basic_formatted_sink_backend">basic_formatted_sink_backend</a></code>,
44 depending on whether your backend requires formatted log records or not.
45 Both base classes define a set of types that are required to interface with
46 sink frontends. One of these types is <code class="computeroutput"><span class="identifier">frontend_requirements</span></code>.
49 <a name="log.extension.sinks.h0"></a>
50 <span class="phrase"><a name="log.extension.sinks.frontend_requirements"></a></span><a class="link" href="extension.html#log.extension.sinks.frontend_requirements">Frontend
53 <pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><code class="computeroutput"><a class="link" href="../sinks.html#header.boost.log.sinks.frontend_requirements_hpp" title="Header <boost/log/sinks/frontend_requirements.hpp>">boost/log/sinks/frontend_requirements.hpp</a></code><span class="special">></span>
56 In order to work with sink backends, frontends use the <code class="computeroutput"><span class="identifier">frontend_requirements</span></code>
57 type defined by all backends. The type combines one or several requirement
60 <div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; ">
62 <code class="computeroutput"><a class="link" href="../boost/log/sinks/synchronized_feeding.html" title="Struct synchronized_feeding">synchronized_feeding</a></code>.
63 If the backend has this requirement, it expects log records to be passed
64 from frontend in synchronized manner (i.e. only one thread should be
65 feeding a record at a time). Note that different threads may be feeding
66 different records, the requirement merely states that there will be no
70 <code class="computeroutput"><a class="link" href="../boost/log/sinks/concurrent_feeding.html" title="Struct concurrent_feeding">concurrent_feeding</a></code>.
71 This requirement extends <code class="computeroutput"><a class="link" href="../boost/log/sinks/synchronized_feeding.html" title="Struct synchronized_feeding">synchronized_feeding</a></code>
72 by allowing different threads to feed records concurrently. The backend
73 implements all necessary thread synchronization in this case.
76 <code class="computeroutput"><a class="link" href="../boost/log/sinks/formatted_records.html" title="Struct formatted_records">formatted_records</a></code>.
77 The backend expects formatted log records. The frontend implements formatting
78 to a string with character type defined by the <code class="computeroutput"><span class="identifier">char_type</span></code>
79 typedef within the backend. The formatted string will be passed along
80 with the log record to the backend. The <code class="computeroutput"><a class="link" href="../boost/log/sinks/basic_fo_idm45711346315728.html" title="Struct template basic_formatted_sink_backend">basic_formatted_sink_backend</a></code>
81 base class automatically adds this requirement to the <code class="computeroutput"><span class="identifier">frontend_requirements</span></code>
85 <code class="computeroutput"><a class="link" href="../boost/log/sinks/flushing.html" title="Struct flushing">flushing</a></code>. The
86 backend supports flushing its internal buffers. If the backend indicates
87 this requirement it has to implement the <code class="computeroutput"><span class="identifier">flush</span></code>
88 method taking no arguments; this method will be called by the frontend
92 <div class="tip"><table border="0" summary="Tip">
94 <td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="../../../../../doc/src/images/tip.png"></td>
95 <th align="left">Tip</th>
97 <tr><td align="left" valign="top"><p>
98 By choosing either of the thread synchronization requirements you effectively
99 allow or prohibit certain <a class="link" href="detailed/sink_frontends.html" title="Sink frontends">sink
100 frontends</a> from being used with your backend.
104 Multiple requirements can be combined into <code class="computeroutput"><span class="identifier">frontend_requirements</span></code>
105 type with the <code class="computeroutput"><a class="link" href="../boost/log/sinks/combine_requirements.html" title="Struct template combine_requirements">combine_requirements</a></code>
108 <pre class="programlisting"><span class="keyword">typedef</span> <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">combine_requirements</span><span class="special"><</span>
109 <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">synchronized_feeding</span><span class="special">,</span>
110 <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">formatted_records</span><span class="special">,</span>
111 <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">flushing</span>
112 <span class="special">>::</span><span class="identifier">type</span> <span class="identifier">frontend_requirements</span><span class="special">;</span>
115 It must be noted that <code class="computeroutput"><a class="link" href="../boost/log/sinks/synchronized_feeding.html" title="Struct synchronized_feeding">synchronized_feeding</a></code>
116 and <code class="computeroutput"><a class="link" href="../boost/log/sinks/concurrent_feeding.html" title="Struct concurrent_feeding">concurrent_feeding</a></code>
117 should not be combined together as it would make the synchronization requirement
118 ambiguous. The <code class="computeroutput"><a class="link" href="../boost/log/sinks/synchronized_feeding.html" title="Struct synchronized_feeding">synchronized_feeding</a></code>
119 is a more strict requirement than <code class="computeroutput"><a class="link" href="../boost/log/sinks/concurrent_feeding.html" title="Struct concurrent_feeding">concurrent_feeding</a></code>,
120 so whenever the backend requires concurrent feeding it is also capable of
121 synchronized feeding.
124 The <code class="computeroutput"><a class="link" href="../boost/log/sinks/has_requirement.html" title="Struct template has_requirement">has_requirement</a></code>
125 metafunction can be used to test for a specific requirement in the <code class="computeroutput"><span class="identifier">frontend_requirements</span></code> typedef.
128 <a name="log.extension.sinks.h1"></a>
129 <span class="phrase"><a name="log.extension.sinks.minimalistic_sink_backend"></a></span><a class="link" href="extension.html#log.extension.sinks.minimalistic_sink_backend">Minimalistic
133 As an example of the <code class="computeroutput"><a class="link" href="../boost/log/sinks/basic_sink_backend.html" title="Struct template basic_sink_backend">basic_sink_backend</a></code>
134 class usage, let's implement a simple statistical information collector backend.
135 Assume we have a network server and we want to monitor how many incoming
136 connections are active and how much data was sent or received. The collected
137 information should be written to a CSV-file every minute. The backend definition
138 could look something like this:
142 <pre class="programlisting"><span class="comment">// The backend collects statistical information about network activity of the application</span>
143 <span class="keyword">class</span> <span class="identifier">stat_collector</span> <span class="special">:</span>
144 <span class="keyword">public</span> <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">basic_sink_backend</span><span class="special"><</span>
145 <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">combine_requirements</span><span class="special"><</span>
146 <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">synchronized_feeding</span><span class="special">,</span> <a class="co" name="log.extension.sinks.c0" href="extension.html#log.extension.sinks.c1"><img src="../../../../../doc/src/images/callouts/1.png" alt="1" border="0"></a>
147 <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">flushing</span> <a class="co" name="log.extension.sinks.c2" href="extension.html#log.extension.sinks.c3"><img src="../../../../../doc/src/images/callouts/2.png" alt="2" border="0"></a>
148 <span class="special">>::</span><span class="identifier">type</span>
149 <span class="special">></span>
150 <span class="special">{</span>
151 <span class="keyword">private</span><span class="special">:</span>
152 <span class="comment">// The file to write the collected information to</span>
153 <span class="identifier">std</span><span class="special">::</span><span class="identifier">ofstream</span> <span class="identifier">m_csv_file</span><span class="special">;</span>
155 <span class="comment">// Here goes the data collected so far:</span>
156 <span class="comment">// Active connections</span>
157 <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="identifier">m_active_connections</span><span class="special">;</span>
158 <span class="comment">// Sent bytes</span>
159 <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="identifier">m_sent_bytes</span><span class="special">;</span>
160 <span class="comment">// Received bytes</span>
161 <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="identifier">m_received_bytes</span><span class="special">;</span>
163 <span class="comment">// The number of collected records since the last write to the file</span>
164 <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="identifier">m_collected_count</span><span class="special">;</span>
165 <span class="comment">// The time when the collected data has been written to the file last time</span>
166 <span class="identifier">boost</span><span class="special">::</span><span class="identifier">posix_time</span><span class="special">::</span><span class="identifier">ptime</span> <span class="identifier">m_last_store_time</span><span class="special">;</span>
168 <span class="keyword">public</span><span class="special">:</span>
169 <span class="comment">// The constructor initializes the internal data</span>
170 <span class="keyword">explicit</span> <span class="identifier">stat_collector</span><span class="special">(</span><span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="identifier">file_name</span><span class="special">);</span>
172 <span class="comment">// The function consumes the log records that come from the frontend</span>
173 <span class="keyword">void</span> <span class="identifier">consume</span><span class="special">(</span><span class="identifier">logging</span><span class="special">::</span><span class="identifier">record_view</span> <span class="keyword">const</span><span class="special">&</span> <span class="identifier">rec</span><span class="special">);</span>
174 <span class="comment">// The function flushes the file</span>
175 <span class="keyword">void</span> <span class="identifier">flush</span><span class="special">();</span>
177 <span class="keyword">private</span><span class="special">:</span>
178 <span class="comment">// The function resets statistical accumulators to initial values</span>
179 <span class="keyword">void</span> <span class="identifier">reset_accumulators</span><span class="special">();</span>
180 <span class="comment">// The function writes the collected data to the file</span>
181 <span class="keyword">void</span> <span class="identifier">write_data</span><span class="special">();</span>
182 <span class="special">};</span>
186 <div class="calloutlist"><table border="0" summary="Callout list">
188 <td width="5%" valign="top" align="left"><p><a name="log.extension.sinks.c1"></a><a href="#log.extension.sinks.c0"><img src="../../../../../doc/src/images/callouts/1.png" alt="1" border="0"></a> </p></td>
189 <td valign="top" align="left"><p>
190 we will have to store internal data, so let's require frontend to synchronize
191 feeding calls to the backend
195 <td width="5%" valign="top" align="left"><p><a name="log.extension.sinks.c3"></a><a href="#log.extension.sinks.c2"><img src="../../../../../doc/src/images/callouts/2.png" alt="2" border="0"></a> </p></td>
196 <td valign="top" align="left"><p>
197 also enable flushing support
202 As you can see, the public interface of the backend is quite simple. Only
203 the <code class="computeroutput"><span class="identifier">consume</span></code> and <code class="computeroutput"><span class="identifier">flush</span></code> methods are called by frontends.
204 The <code class="computeroutput"><span class="identifier">consume</span></code> function is called
205 every time a logging record passes filtering in the frontend. The record,
206 as was stated before, contains a set of attribute values and the message
207 string. Since we have no need for the record message, we will ignore it for
208 now. But from the other attributes we can extract the statistical data to
209 accumulate and write to the file. We can use <a class="link" href="detailed/expressions.html#log.detailed.expressions.attr_keywords" title="Defining attribute keywords">attribute
210 keywords</a> and <a class="link" href="detailed/attributes.html#log.detailed.attributes.related_components.value_processing.visitation" title="Value visitation">value
211 visitation</a> to accomplish this.
215 <pre class="programlisting"><span class="identifier">BOOST_LOG_ATTRIBUTE_KEYWORD</span><span class="special">(</span><span class="identifier">sent</span><span class="special">,</span> <span class="string">"Sent"</span><span class="special">,</span> <span class="keyword">unsigned</span> <span class="keyword">int</span><span class="special">)</span>
216 <span class="identifier">BOOST_LOG_ATTRIBUTE_KEYWORD</span><span class="special">(</span><span class="identifier">received</span><span class="special">,</span> <span class="string">"Received"</span><span class="special">,</span> <span class="keyword">unsigned</span> <span class="keyword">int</span><span class="special">)</span>
218 <span class="comment">// The function consumes the log records that come from the frontend</span>
219 <span class="keyword">void</span> <span class="identifier">stat_collector</span><span class="special">::</span><span class="identifier">consume</span><span class="special">(</span><span class="identifier">logging</span><span class="special">::</span><span class="identifier">record_view</span> <span class="keyword">const</span><span class="special">&</span> <span class="identifier">rec</span><span class="special">)</span>
220 <span class="special">{</span>
221 <span class="comment">// Accumulate statistical readings</span>
222 <span class="keyword">if</span> <span class="special">(</span><span class="identifier">rec</span><span class="special">.</span><span class="identifier">attribute_values</span><span class="special">().</span><span class="identifier">count</span><span class="special">(</span><span class="string">"Connected"</span><span class="special">))</span>
223 <span class="special">++</span><span class="identifier">m_active_connections</span><span class="special">;</span>
224 <span class="keyword">else</span> <span class="keyword">if</span> <span class="special">(</span><span class="identifier">rec</span><span class="special">.</span><span class="identifier">attribute_values</span><span class="special">().</span><span class="identifier">count</span><span class="special">(</span><span class="string">"Disconnected"</span><span class="special">))</span>
225 <span class="special">--</span><span class="identifier">m_active_connections</span><span class="special">;</span>
226 <span class="keyword">else</span>
227 <span class="special">{</span>
228 <span class="keyword">namespace</span> <span class="identifier">phoenix</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">phoenix</span><span class="special">;</span>
229 <span class="identifier">logging</span><span class="special">::</span><span class="identifier">visit</span><span class="special">(</span><span class="identifier">sent</span><span class="special">,</span> <span class="identifier">rec</span><span class="special">,</span> <span class="identifier">phoenix</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">m_sent_bytes</span><span class="special">)</span> <span class="special">+=</span> <span class="identifier">phoenix</span><span class="special">::</span><span class="identifier">placeholders</span><span class="special">::</span><span class="identifier">_1</span><span class="special">);</span>
230 <span class="identifier">logging</span><span class="special">::</span><span class="identifier">visit</span><span class="special">(</span><span class="identifier">received</span><span class="special">,</span> <span class="identifier">rec</span><span class="special">,</span> <span class="identifier">phoenix</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span><span class="identifier">m_received_bytes</span><span class="special">)</span> <span class="special">+=</span> <span class="identifier">phoenix</span><span class="special">::</span><span class="identifier">placeholders</span><span class="special">::</span><span class="identifier">_1</span><span class="special">);</span>
231 <span class="special">}</span>
232 <span class="special">++</span><span class="identifier">m_collected_count</span><span class="special">;</span>
234 <span class="comment">// Check if it's time to write the accumulated data to the file</span>
235 <span class="identifier">boost</span><span class="special">::</span><span class="identifier">posix_time</span><span class="special">::</span><span class="identifier">ptime</span> <span class="identifier">now</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">posix_time</span><span class="special">::</span><span class="identifier">microsec_clock</span><span class="special">::</span><span class="identifier">universal_time</span><span class="special">();</span>
236 <span class="keyword">if</span> <span class="special">(</span><span class="identifier">now</span> <span class="special">-</span> <span class="identifier">m_last_store_time</span> <span class="special">>=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">posix_time</span><span class="special">::</span><span class="identifier">minutes</span><span class="special">(</span><span class="number">1</span><span class="special">))</span>
237 <span class="special">{</span>
238 <span class="identifier">write_data</span><span class="special">();</span>
239 <span class="identifier">m_last_store_time</span> <span class="special">=</span> <span class="identifier">now</span><span class="special">;</span>
240 <span class="special">}</span>
241 <span class="special">}</span>
243 <span class="comment">// The function writes the collected data to the file</span>
244 <span class="keyword">void</span> <span class="identifier">stat_collector</span><span class="special">::</span><span class="identifier">write_data</span><span class="special">()</span>
245 <span class="special">{</span>
246 <span class="identifier">m_csv_file</span> <span class="special"><<</span> <span class="identifier">m_active_connections</span>
247 <span class="special"><<</span> <span class="char">','</span> <span class="special"><<</span> <span class="identifier">m_sent_bytes</span>
248 <span class="special"><<</span> <span class="char">','</span> <span class="special"><<</span> <span class="identifier">m_received_bytes</span>
249 <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">endl</span><span class="special">;</span>
250 <span class="identifier">reset_accumulators</span><span class="special">();</span>
251 <span class="special">}</span>
253 <span class="comment">// The function resets statistical accumulators to initial values</span>
254 <span class="keyword">void</span> <span class="identifier">stat_collector</span><span class="special">::</span><span class="identifier">reset_accumulators</span><span class="special">()</span>
255 <span class="special">{</span>
256 <span class="identifier">m_sent_bytes</span> <span class="special">=</span> <span class="identifier">m_received_bytes</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span>
257 <span class="identifier">m_collected_count</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span>
258 <span class="special">}</span>
263 Note that we used <a href="http://www.boost.org/doc/libs/release/libs/phoenix/doc/html/index.html" target="_top">Boost.Phoenix</a>
264 to automatically generate visitor function objects for attribute values.
267 The last bit of implementation is the <code class="computeroutput"><span class="identifier">flush</span></code>
268 method. It is used to flush all buffered data to the external storage, which
269 is a file in our case. The method can be implemented in the following way:
273 <pre class="programlisting"><span class="comment">// The function flushes the file</span>
274 <span class="keyword">void</span> <span class="identifier">stat_collector</span><span class="special">::</span><span class="identifier">flush</span><span class="special">()</span>
275 <span class="special">{</span>
276 <span class="comment">// Store any data that may have been collected since the list write to the file</span>
277 <span class="keyword">if</span> <span class="special">(</span><span class="identifier">m_collected_count</span> <span class="special">></span> <span class="number">0</span><span class="special">)</span>
278 <span class="special">{</span>
279 <span class="identifier">write_data</span><span class="special">();</span>
280 <span class="identifier">m_last_store_time</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">posix_time</span><span class="special">::</span><span class="identifier">microsec_clock</span><span class="special">::</span><span class="identifier">universal_time</span><span class="special">();</span>
281 <span class="special">}</span>
283 <span class="identifier">m_csv_file</span><span class="special">.</span><span class="identifier">flush</span><span class="special">();</span>
284 <span class="special">}</span>
289 You can find the complete code of this example <a href="../../../../../libs/log/example/doc/extension_stat_collector.cpp" target="_top">here</a>.
292 <a name="log.extension.sinks.h2"></a>
293 <span class="phrase"><a name="log.extension.sinks.formatting_sink_backend"></a></span><a class="link" href="extension.html#log.extension.sinks.formatting_sink_backend">Formatting
297 As an example of a formatting sink backend, let's implement a sink that will
298 display text notifications for every log record passed to it.
300 <div class="tip"><table border="0" summary="Tip">
302 <td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="../../../../../doc/src/images/tip.png"></td>
303 <th align="left">Tip</th>
305 <tr><td align="left" valign="top"><p>
306 Real world applications would probably use some GUI toolkit API to display
307 notifications but GUI programming is out of scope of this documentation.
308 In order to display notifications we shall use an external program which
309 does just that. In this example we shall employ <code class="computeroutput"><span class="identifier">notify</span><span class="special">-</span><span class="identifier">send</span></code>
310 program which is available on Linux (Ubuntu/Debian users can install it
311 with the <code class="computeroutput"><span class="identifier">libnotify</span><span class="special">-</span><span class="identifier">bin</span></code> package; other distros should also
312 have it available in their package repositories). The program takes the
313 notification parameters in the command line, displays the notification
314 in the current desktop environment and then exits. Other platforms may
315 also have similar tools.
319 The definition of the backend is very similar to what we have seen in the
324 <pre class="programlisting"><span class="comment">// The backend starts an external application to display notifications</span>
325 <span class="keyword">class</span> <span class="identifier">app_launcher</span> <span class="special">:</span>
326 <span class="keyword">public</span> <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">basic_formatted_sink_backend</span><span class="special"><</span>
327 <span class="keyword">char</span><span class="special">,</span> <a class="co" name="log.extension.sinks.c4" href="extension.html#log.extension.sinks.c5"><img src="../../../../../doc/src/images/callouts/1.png" alt="1" border="0"></a>
328 <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">synchronized_feeding</span> <a class="co" name="log.extension.sinks.c6" href="extension.html#log.extension.sinks.c7"><img src="../../../../../doc/src/images/callouts/2.png" alt="2" border="0"></a>
329 <span class="special">></span>
330 <span class="special">{</span>
331 <span class="keyword">public</span><span class="special">:</span>
332 <span class="comment">// The function consumes the log records that come from the frontend</span>
333 <span class="keyword">void</span> <span class="identifier">consume</span><span class="special">(</span><span class="identifier">logging</span><span class="special">::</span><span class="identifier">record_view</span> <span class="keyword">const</span><span class="special">&</span> <span class="identifier">rec</span><span class="special">,</span> <span class="identifier">string_type</span> <span class="keyword">const</span><span class="special">&</span> <span class="identifier">command_line</span><span class="special">);</span>
334 <span class="special">};</span>
338 <div class="calloutlist"><table border="0" summary="Callout list">
340 <td width="5%" valign="top" align="left"><p><a name="log.extension.sinks.c5"></a><a href="#log.extension.sinks.c4"><img src="../../../../../doc/src/images/callouts/1.png" alt="1" border="0"></a> </p></td>
341 <td valign="top" align="left"><p>
342 target character type
346 <td width="5%" valign="top" align="left"><p><a name="log.extension.sinks.c7"></a><a href="#log.extension.sinks.c6"><img src="../../../../../doc/src/images/callouts/2.png" alt="2" border="0"></a> </p></td>
347 <td valign="top" align="left"><p>
348 in order not to spawn too many application instances we require records
349 to be processed serial
354 The first thing to notice is that the <code class="computeroutput"><span class="identifier">app_launcher</span></code>
355 backend derives from <code class="computeroutput"><a class="link" href="../boost/log/sinks/basic_fo_idm45711346315728.html" title="Struct template basic_formatted_sink_backend">basic_formatted_sink_backend</a></code>
356 rather than <code class="computeroutput"><a class="link" href="../boost/log/sinks/basic_sink_backend.html" title="Struct template basic_sink_backend">basic_sink_backend</a></code>.
357 This base class accepts the character type in addition to the requirements.
358 The specified character type defines the target string type the formatter
359 will compose in the frontend and it typically corresponds to the underlying
360 API the backend uses to process records. It must be mentioned that the character
361 type the backend requires is not related to the character types of string
362 attribute values, including the message text. The formatter will take care
363 of character code conversion when needed.
366 The second notable difference from the previous examples is that <code class="computeroutput"><span class="identifier">consume</span></code> method takes an additional string
367 parameter besides the log record. This is the result of formatting. The
368 <code class="computeroutput"><span class="identifier">string_type</span></code> type is defined
369 by the <code class="computeroutput"><a class="link" href="../boost/log/sinks/basic_fo_idm45711346315728.html" title="Struct template basic_formatted_sink_backend">basic_formatted_sink_backend</a></code>
370 base class and it corresponds to the requested character type.
373 We don't need to flush any buffers in this example, so we didn't specify
374 the <code class="computeroutput"><a class="link" href="../boost/log/sinks/flushing.html" title="Struct flushing">flushing</a></code> requirement
375 and omitted the <code class="computeroutput"><span class="identifier">flush</span></code> method
376 in the backend. Although we don't need any synchronization in our backend,
377 we specified <code class="computeroutput"><a class="link" href="../boost/log/sinks/synchronized_feeding.html" title="Struct synchronized_feeding">synchronized_feeding</a></code>
378 requirement so that we don't spawn multiple instances of <code class="computeroutput"><span class="identifier">notify</span><span class="special">-</span><span class="identifier">send</span></code> program
379 and cause a "fork bomb".
382 Now, the <code class="computeroutput"><span class="identifier">consume</span></code> implementation
387 <pre class="programlisting"><span class="comment">// The function consumes the log records that come from the frontend</span>
388 <span class="keyword">void</span> <span class="identifier">app_launcher</span><span class="special">::</span><span class="identifier">consume</span><span class="special">(</span><span class="identifier">logging</span><span class="special">::</span><span class="identifier">record_view</span> <span class="keyword">const</span><span class="special">&</span> <span class="identifier">rec</span><span class="special">,</span> <span class="identifier">string_type</span> <span class="keyword">const</span><span class="special">&</span> <span class="identifier">command_line</span><span class="special">)</span>
389 <span class="special">{</span>
390 <span class="identifier">std</span><span class="special">::</span><span class="identifier">system</span><span class="special">(</span><span class="identifier">command_line</span><span class="special">.</span><span class="identifier">c_str</span><span class="special">());</span>
391 <span class="special">}</span>
396 So the formatted string is expected to actually be a command line to start
397 the application. The exact application name and arguments are to be determined
398 by the formatter. This approach adds flexibility because the backend can
399 be used for different purposes and updating the command line is as easy as
400 updating the formatter.
403 The sink can be configured with the following code:
407 <pre class="programlisting"><span class="identifier">BOOST_LOG_ATTRIBUTE_KEYWORD</span><span class="special">(</span><span class="identifier">process_name</span><span class="special">,</span> <span class="string">"ProcessName"</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">)</span>
408 <span class="identifier">BOOST_LOG_ATTRIBUTE_KEYWORD</span><span class="special">(</span><span class="identifier">caption</span><span class="special">,</span> <span class="string">"Caption"</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">)</span>
410 <span class="comment">// Custom severity level formatting function</span>
411 <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">severity_level_as_urgency</span><span class="special">(</span>
412 <span class="identifier">logging</span><span class="special">::</span><span class="identifier">value_ref</span><span class="special"><</span> <span class="identifier">logging</span><span class="special">::</span><span class="identifier">trivial</span><span class="special">::</span><span class="identifier">severity_level</span><span class="special">,</span> <span class="identifier">logging</span><span class="special">::</span><span class="identifier">trivial</span><span class="special">::</span><span class="identifier">tag</span><span class="special">::</span><span class="identifier">severity</span> <span class="special">></span> <span class="keyword">const</span><span class="special">&</span> <span class="identifier">level</span><span class="special">)</span>
413 <span class="special">{</span>
414 <span class="keyword">if</span> <span class="special">(!</span><span class="identifier">level</span> <span class="special">||</span> <span class="identifier">level</span><span class="special">.</span><span class="identifier">get</span><span class="special">()</span> <span class="special">==</span> <span class="identifier">logging</span><span class="special">::</span><span class="identifier">trivial</span><span class="special">::</span><span class="identifier">info</span><span class="special">)</span>
415 <span class="keyword">return</span> <span class="string">"normal"</span><span class="special">;</span>
416 <span class="identifier">logging</span><span class="special">::</span><span class="identifier">trivial</span><span class="special">::</span><span class="identifier">severity_level</span> <span class="identifier">lvl</span> <span class="special">=</span> <span class="identifier">level</span><span class="special">.</span><span class="identifier">get</span><span class="special">();</span>
417 <span class="keyword">if</span> <span class="special">(</span><span class="identifier">lvl</span> <span class="special"><</span> <span class="identifier">logging</span><span class="special">::</span><span class="identifier">trivial</span><span class="special">::</span><span class="identifier">info</span><span class="special">)</span>
418 <span class="keyword">return</span> <span class="string">"low"</span><span class="special">;</span>
419 <span class="keyword">else</span>
420 <span class="keyword">return</span> <span class="string">"critical"</span><span class="special">;</span>
421 <span class="special">}</span>
423 <span class="comment">// The function initializes the logging library</span>
424 <span class="keyword">void</span> <span class="identifier">init_logging</span><span class="special">()</span>
425 <span class="special">{</span>
426 <span class="identifier">boost</span><span class="special">::</span><span class="identifier">shared_ptr</span><span class="special"><</span> <span class="identifier">logging</span><span class="special">::</span><span class="identifier">core</span> <span class="special">></span> <span class="identifier">core</span> <span class="special">=</span> <span class="identifier">logging</span><span class="special">::</span><span class="identifier">core</span><span class="special">::</span><span class="identifier">get</span><span class="special">();</span>
428 <span class="keyword">typedef</span> <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">synchronous_sink</span><span class="special"><</span> <span class="identifier">app_launcher</span> <span class="special">></span> <span class="identifier">sink_t</span><span class="special">;</span>
429 <span class="identifier">boost</span><span class="special">::</span><span class="identifier">shared_ptr</span><span class="special"><</span> <span class="identifier">sink_t</span> <span class="special">></span> <span class="identifier">sink</span><span class="special">(</span><span class="keyword">new</span> <span class="identifier">sink_t</span><span class="special">());</span>
431 <span class="keyword">const</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">pair</span><span class="special"><</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*,</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="special">></span> <span class="identifier">shell_decorations</span><span class="special">[]</span> <span class="special">=</span>
432 <span class="special">{</span>
433 <span class="identifier">std</span><span class="special">::</span><span class="identifier">pair</span><span class="special"><</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*,</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="special">>(</span><span class="string">"\""</span><span class="special">,</span> <span class="string">"\\\""</span><span class="special">),</span>
434 <span class="identifier">std</span><span class="special">::</span><span class="identifier">pair</span><span class="special"><</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*,</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="special">>(</span><span class="string">"$"</span><span class="special">,</span> <span class="string">"\\$"</span><span class="special">),</span>
435 <span class="identifier">std</span><span class="special">::</span><span class="identifier">pair</span><span class="special"><</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*,</span> <span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="special">>(</span><span class="string">"!"</span><span class="special">,</span> <span class="string">"\\!"</span><span class="special">)</span>
436 <span class="special">};</span>
438 <span class="comment">// Make the formatter generate the command line for notify-send</span>
439 <span class="identifier">sink</span><span class="special">-></span><span class="identifier">set_formatter</span>
440 <span class="special">(</span>
441 <span class="identifier">expr</span><span class="special">::</span><span class="identifier">stream</span> <span class="special"><<</span> <span class="string">"notify-send -t 2000 -u "</span>
442 <span class="special"><<</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">phoenix</span><span class="special">::</span><span class="identifier">bind</span><span class="special">(&</span><span class="identifier">severity_level_as_urgency</span><span class="special">,</span> <span class="identifier">logging</span><span class="special">::</span><span class="identifier">trivial</span><span class="special">::</span><span class="identifier">severity</span><span class="special">.</span><span class="identifier">or_none</span><span class="special">())</span>
443 <span class="special"><<</span> <span class="identifier">expr</span><span class="special">::</span><span class="identifier">if_</span><span class="special">(</span><span class="identifier">expr</span><span class="special">::</span><span class="identifier">has_attr</span><span class="special">(</span><span class="identifier">process_name</span><span class="special">))</span>
444 <span class="special">[</span>
445 <span class="identifier">expr</span><span class="special">::</span><span class="identifier">stream</span> <span class="special"><<</span> <span class="string">" -a '"</span> <span class="special"><<</span> <span class="identifier">process_name</span> <span class="special"><<</span> <span class="string">"'"</span>
446 <span class="special">]</span>
447 <span class="special"><<</span> <span class="identifier">expr</span><span class="special">::</span><span class="identifier">if_</span><span class="special">(</span><span class="identifier">expr</span><span class="special">::</span><span class="identifier">has_attr</span><span class="special">(</span><span class="identifier">caption</span><span class="special">))</span>
448 <span class="special">[</span>
449 <span class="identifier">expr</span><span class="special">::</span><span class="identifier">stream</span> <span class="special"><<</span> <span class="string">" \""</span> <span class="special"><<</span> <span class="identifier">expr</span><span class="special">::</span><span class="identifier">char_decor</span><span class="special">(</span><span class="identifier">shell_decorations</span><span class="special">)[</span> <span class="identifier">expr</span><span class="special">::</span><span class="identifier">stream</span> <span class="special"><<</span> <span class="identifier">caption</span> <span class="special">]</span> <span class="special"><<</span> <span class="string">"\""</span>
450 <span class="special">]</span>
451 <span class="special"><<</span> <span class="string">" \""</span> <span class="special"><<</span> <span class="identifier">expr</span><span class="special">::</span><span class="identifier">char_decor</span><span class="special">(</span><span class="identifier">shell_decorations</span><span class="special">)[</span> <span class="identifier">expr</span><span class="special">::</span><span class="identifier">stream</span> <span class="special"><<</span> <span class="identifier">expr</span><span class="special">::</span><span class="identifier">message</span> <span class="special">]</span> <span class="special"><<</span> <span class="string">"\""</span>
452 <span class="special">);</span>
454 <span class="identifier">core</span><span class="special">-></span><span class="identifier">add_sink</span><span class="special">(</span><span class="identifier">sink</span><span class="special">);</span>
456 <span class="comment">// Add attributes that we will use</span>
457 <span class="identifier">core</span><span class="special">-></span><span class="identifier">add_global_attribute</span><span class="special">(</span><span class="string">"ProcessName"</span><span class="special">,</span> <span class="identifier">attrs</span><span class="special">::</span><span class="identifier">current_process_name</span><span class="special">());</span>
458 <span class="special">}</span>
463 The most interesting part is the sink setup. The <code class="computeroutput"><a class="link" href="../boost/log/sinks/synchronous_sink.html" title="Class template synchronous_sink">synchronous_sink</a></code>
464 frontend (as well as any other frontend) will detect that the <code class="computeroutput"><span class="identifier">app_launcher</span></code> backend requires formatting
465 and enable the corresponding functionality. The <code class="computeroutput"><span class="identifier">set_formatter</span></code>
466 method becomes available and can be used to set the formatting expression
467 that composes the command line to start the <code class="computeroutput"><span class="identifier">notify</span><span class="special">-</span><span class="identifier">send</span></code> program.
468 We used <a class="link" href="detailed/expressions.html#log.detailed.expressions.attr_keywords" title="Defining attribute keywords">attribute
469 keywords</a> to identify particular attribute values in the formatter.
470 Notice that string attribute values have to be preprocessed so that special
471 characters interpreted by the shell are escaped in the command line. We achieve
472 that with the <code class="computeroutput"><a class="link" href="../boost/log/expressions/char_dec_idm45711347999168.html" title="Function template char_decor">char_decor</a></code> decorator with
473 our custom replacement map. After the sink is configured we also add the
474 <a class="link" href="detailed/attributes.html#log.detailed.attributes.process_name" title="Current process name">current process name</a>
475 attribute to the core so that we don't have to add it to every record.
478 After all this is done, we can finally display some notifications:
482 <pre class="programlisting"><span class="keyword">void</span> <span class="identifier">test_notifications</span><span class="special">()</span>
483 <span class="special">{</span>
484 <span class="identifier">BOOST_LOG_TRIVIAL</span><span class="special">(</span><span class="identifier">debug</span><span class="special">)</span> <span class="special"><<</span> <span class="string">"Hello, it's a simple notification"</span><span class="special">;</span>
485 <span class="identifier">BOOST_LOG_TRIVIAL</span><span class="special">(</span><span class="identifier">info</span><span class="special">)</span> <span class="special"><<</span> <span class="identifier">logging</span><span class="special">::</span><span class="identifier">add_value</span><span class="special">(</span><span class="identifier">caption</span><span class="special">,</span> <span class="string">"Caption text"</span><span class="special">)</span> <span class="special"><<</span> <span class="string">"And this notification has caption as well"</span><span class="special">;</span>
486 <span class="special">}</span>
491 The complete code of this example is available <a href="../../../../../libs/log/example/doc/extension_app_launcher.cpp" target="_top">here</a>.
495 <table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr>
496 <td align="left"></td>
497 <td align="right"><div class="copyright-footer">Copyright © 2007-2016 Andrey Semashev<p>
498 Distributed under the Boost Software License, Version 1.0. (See accompanying
499 file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>).
504 <div class="spirit-nav">
505 <a accesskey="p" href="detailed/utilities.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../index.html"><img src="../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../index.html"><img src="../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="extension/sources.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a>