| <html> |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> |
| <title>Extending the library</title> |
| <link rel="stylesheet" href="../../../../../doc/src/boostbook.css" type="text/css"> |
| <meta name="generator" content="DocBook XSL Stylesheets V1.78.1"> |
| <link rel="home" href="../index.html" title="Chapter 1. Boost.Log v2"> |
| <link rel="up" href="../index.html" title="Chapter 1. Boost.Log v2"> |
| <link rel="prev" href="detailed/utilities.html" title="Utilities"> |
| <link rel="next" href="extension/sources.html" title="Writing your own sources"> |
| </head> |
| <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"> |
| <table cellpadding="2" width="100%"><tr><td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../boost.png"></td></tr></table> |
| <hr> |
| <div class="spirit-nav"> |
| <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> |
| </div> |
| <div class="section"> |
| <div class="titlepage"><div><div><h2 class="title" style="clear: both"> |
| <a name="log.extension"></a><a class="link" href="extension.html" title="Extending the library">Extending the library</a> |
| </h2></div></div></div> |
| <div class="toc"><dl class="toc"> |
| <dt><span class="section"><a href="extension.html#log.extension.sinks">Writing your own sinks</a></span></dt> |
| <dt><span class="section"><a href="extension/sources.html">Writing your own sources</a></span></dt> |
| <dt><span class="section"><a href="extension/attributes.html">Writing your own attributes</a></span></dt> |
| <dt><span class="section"><a href="extension/settings.html">Extending library settings support</a></span></dt> |
| </dl></div> |
| <div class="section"> |
| <div class="titlepage"><div><div><h3 class="title"> |
| <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> |
| </h3></div></div></div> |
| <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> |
| </pre> |
| <p> |
| As was described in the <a class="link" href="design.html" title="Design overview">Design overview</a> |
| 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. |
| </p> |
| <p> |
| 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> |
| or <code class="computeroutput"><a class="link" href="../boost/log/sinks/basic_formatte_idp44219440.html" title="Struct template basic_formatted_sink_backend">basic_formatted_sink_backend</a></code>, |
| 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 <code class="computeroutput"><span class="identifier">frontend_requirements</span></code>. |
| </p> |
| <h5> |
| <a name="log.extension.sinks.h0"></a> |
| <span class="phrase"><a name="log.extension.sinks.frontend_requirements"></a></span><a class="link" href="extension.html#log.extension.sinks.frontend_requirements">Frontend |
| requirements</a> |
| </h5> |
| <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> |
| </pre> |
| <p> |
| In order to work with sink backends, frontends use the <code class="computeroutput"><span class="identifier">frontend_requirements</span></code> |
| type defined by all backends. The type combines one or several requirement |
| tags: |
| </p> |
| <div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "> |
| <li class="listitem"> |
| <code class="computeroutput"><a class="link" href="../boost/log/sinks/synchronized_feeding.html" title="Struct synchronized_feeding">synchronized_feeding</a></code>. |
| 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. |
| </li> |
| <li class="listitem"> |
| <code class="computeroutput"><a class="link" href="../boost/log/sinks/concurrent_feeding.html" title="Struct concurrent_feeding">concurrent_feeding</a></code>. |
| This requirement extends <code class="computeroutput"><a class="link" href="../boost/log/sinks/synchronized_feeding.html" title="Struct synchronized_feeding">synchronized_feeding</a></code> |
| by allowing different threads to feed records concurrently. The backend |
| implements all necessary thread synchronization in this case. |
| </li> |
| <li class="listitem"> |
| <code class="computeroutput"><a class="link" href="../boost/log/sinks/formatted_records.html" title="Struct formatted_records">formatted_records</a></code>. |
| The backend expects formatted log records. The frontend implements formatting |
| to a string with character type defined by the <code class="computeroutput"><span class="identifier">char_type</span></code> |
| typedef within the backend. The formatted string will be passed along |
| with the log record to the backend. The <code class="computeroutput"><a class="link" href="../boost/log/sinks/basic_formatte_idp44219440.html" title="Struct template basic_formatted_sink_backend">basic_formatted_sink_backend</a></code> |
| base class automatically adds this requirement to the <code class="computeroutput"><span class="identifier">frontend_requirements</span></code> |
| type. |
| </li> |
| <li class="listitem"> |
| <code class="computeroutput"><a class="link" href="../boost/log/sinks/flushing.html" title="Struct flushing">flushing</a></code>. The |
| backend supports flushing its internal buffers. If the backend indicates |
| this requirement it has to implement the <code class="computeroutput"><span class="identifier">flush</span></code> |
| method taking no arguments; this method will be called by the frontend |
| when flushed. |
| </li> |
| </ul></div> |
| <div class="tip"><table border="0" summary="Tip"> |
| <tr> |
| <td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="../../../../../doc/src/images/tip.png"></td> |
| <th align="left">Tip</th> |
| </tr> |
| <tr><td align="left" valign="top"><p> |
| By choosing either of the thread synchronization requirements you effectively |
| allow or prohibit certain <a class="link" href="detailed/sink_frontends.html" title="Sink frontends">sink |
| frontends</a> from being used with your backend. |
| </p></td></tr> |
| </table></div> |
| <p> |
| Multiple requirements can be combined into <code class="computeroutput"><span class="identifier">frontend_requirements</span></code> |
| 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> |
| metafunction: |
| </p> |
| <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> |
| <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">synchronized_feeding</span><span class="special">,</span> |
| <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">formatted_records</span><span class="special">,</span> |
| <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">flushing</span> |
| <span class="special">>::</span><span class="identifier">type</span> <span class="identifier">frontend_requirements</span><span class="special">;</span> |
| </pre> |
| <p> |
| 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> |
| and <code class="computeroutput"><a class="link" href="../boost/log/sinks/concurrent_feeding.html" title="Struct concurrent_feeding">concurrent_feeding</a></code> |
| should not be combined together as it would make the synchronization requirement |
| ambiguous. The <code class="computeroutput"><a class="link" href="../boost/log/sinks/synchronized_feeding.html" title="Struct synchronized_feeding">synchronized_feeding</a></code> |
| 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>, |
| so whenever the backend requires concurrent feeding it is also capable of |
| synchronized feeding. |
| </p> |
| <p> |
| The <code class="computeroutput"><a class="link" href="../boost/log/sinks/has_requirement.html" title="Struct template has_requirement">has_requirement</a></code> |
| metafunction can be used to test for a specific requirement in the <code class="computeroutput"><span class="identifier">frontend_requirements</span></code> typedef. |
| </p> |
| <h5> |
| <a name="log.extension.sinks.h1"></a> |
| <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 |
| sink backend</a> |
| </h5> |
| <p> |
| 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> |
| 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: |
| </p> |
| <p> |
| </p> |
| <pre class="programlisting"><span class="comment">// The backend collects statistical information about network activity of the application</span> |
| <span class="keyword">class</span> <span class="identifier">stat_collector</span> <span class="special">:</span> |
| <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> |
| <span class="identifier">sinks</span><span class="special">::</span><span class="identifier">combine_requirements</span><span class="special"><</span> |
| <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> |
| <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> |
| <span class="special">>::</span><span class="identifier">type</span> |
| <span class="special">></span> |
| <span class="special">{</span> |
| <span class="keyword">private</span><span class="special">:</span> |
| <span class="comment">// The file to write the collected information to</span> |
| <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> |
| |
| <span class="comment">// Here goes the data collected so far:</span> |
| <span class="comment">// Active connections</span> |
| <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="identifier">m_active_connections</span><span class="special">;</span> |
| <span class="comment">// Sent bytes</span> |
| <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="identifier">m_sent_bytes</span><span class="special">;</span> |
| <span class="comment">// Received bytes</span> |
| <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="identifier">m_received_bytes</span><span class="special">;</span> |
| |
| <span class="comment">// The number of collected records since the last write to the file</span> |
| <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="identifier">m_collected_count</span><span class="special">;</span> |
| <span class="comment">// The time when the collected data has been written to the file last time</span> |
| <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> |
| |
| <span class="keyword">public</span><span class="special">:</span> |
| <span class="comment">// The constructor initializes the internal data</span> |
| <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> |
| |
| <span class="comment">// The function consumes the log records that come from the frontend</span> |
| <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="comment">// The function flushes the file</span> |
| <span class="keyword">void</span> <span class="identifier">flush</span><span class="special">();</span> |
| |
| <span class="keyword">private</span><span class="special">:</span> |
| <span class="comment">// The function resets statistical accumulators to initial values</span> |
| <span class="keyword">void</span> <span class="identifier">reset_accumulators</span><span class="special">();</span> |
| <span class="comment">// The function writes the collected data to the file</span> |
| <span class="keyword">void</span> <span class="identifier">write_data</span><span class="special">();</span> |
| <span class="special">};</span> |
| </pre> |
| <p> |
| </p> |
| <div class="calloutlist"><table border="0" summary="Callout list"> |
| <tr> |
| <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> |
| <td valign="top" align="left"><p> |
| we will have to store internal data, so let's require frontend to synchronize |
| feeding calls to the backend |
| </p></td> |
| </tr> |
| <tr> |
| <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> |
| <td valign="top" align="left"><p> |
| also enable flushing support |
| </p></td> |
| </tr> |
| </table></div> |
| <p> |
| As you can see, the public interface of the backend is quite simple. Only |
| 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. |
| The <code class="computeroutput"><span class="identifier">consume</span></code> 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 <a class="link" href="detailed/expressions.html#log.detailed.expressions.attr_keywords" title="Defining attribute keywords">attribute |
| keywords</a> and <a class="link" href="detailed/attributes.html#log.detailed.attributes.related_components.value_processing.visitation" title="Value visitation">value |
| visitation</a> to accomplish this. |
| </p> |
| <p> |
| </p> |
| <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> |
| <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> |
| |
| <span class="comment">// The function consumes the log records that come from the frontend</span> |
| <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> |
| <span class="special">{</span> |
| <span class="comment">// Accumulate statistical readings</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">"Connected"</span><span class="special">))</span> |
| <span class="special">++</span><span class="identifier">m_active_connections</span><span class="special">;</span> |
| <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> |
| <span class="special">--</span><span class="identifier">m_active_connections</span><span class="special">;</span> |
| <span class="keyword">else</span> |
| <span class="special">{</span> |
| <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> |
| <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> |
| <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> |
| <span class="special">}</span> |
| <span class="special">++</span><span class="identifier">m_collected_count</span><span class="special">;</span> |
| |
| <span class="comment">// Check if it's time to write the accumulated data to the file</span> |
| <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> |
| <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> |
| <span class="special">{</span> |
| <span class="identifier">write_data</span><span class="special">();</span> |
| <span class="identifier">m_last_store_time</span> <span class="special">=</span> <span class="identifier">now</span><span class="special">;</span> |
| <span class="special">}</span> |
| <span class="special">}</span> |
| |
| <span class="comment">// The function writes the collected data to the file</span> |
| <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> |
| <span class="special">{</span> |
| <span class="identifier">m_csv_file</span> <span class="special"><<</span> <span class="identifier">m_active_connections</span> |
| <span class="special"><<</span> <span class="char">','</span> <span class="special"><<</span> <span class="identifier">m_sent_bytes</span> |
| <span class="special"><<</span> <span class="char">','</span> <span class="special"><<</span> <span class="identifier">m_received_bytes</span> |
| <span class="special"><<</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">endl</span><span class="special">;</span> |
| <span class="identifier">reset_accumulators</span><span class="special">();</span> |
| <span class="special">}</span> |
| |
| <span class="comment">// The function resets statistical accumulators to initial values</span> |
| <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> |
| <span class="special">{</span> |
| <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> |
| <span class="identifier">m_collected_count</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span> |
| <span class="special">}</span> |
| </pre> |
| <p> |
| </p> |
| <p> |
| Note that we used <a href="http://www.boost.org/doc/libs/release/libs/phoenix/doc/html/index.html" target="_top">Boost.Phoenix</a> |
| to automatically generate visitor function objects for attribute values. |
| </p> |
| <p> |
| The last bit of implementation is the <code class="computeroutput"><span class="identifier">flush</span></code> |
| 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: |
| </p> |
| <p> |
| </p> |
| <pre class="programlisting"><span class="comment">// The function flushes the file</span> |
| <span class="keyword">void</span> <span class="identifier">stat_collector</span><span class="special">::</span><span class="identifier">flush</span><span class="special">()</span> |
| <span class="special">{</span> |
| <span class="comment">// Store any data that may have been collected since the list write to the file</span> |
| <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> |
| <span class="special">{</span> |
| <span class="identifier">write_data</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">microsec_clock</span><span class="special">::</span><span class="identifier">universal_time</span><span class="special">();</span> |
| <span class="special">}</span> |
| |
| <span class="identifier">m_csv_file</span><span class="special">.</span><span class="identifier">flush</span><span class="special">();</span> |
| <span class="special">}</span> |
| </pre> |
| <p> |
| </p> |
| <p> |
| You can find the complete code of this example <a href="../../../../../libs/log/example/doc/extension_stat_collector.cpp" target="_top">here</a>. |
| </p> |
| <h5> |
| <a name="log.extension.sinks.h2"></a> |
| <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 |
| sink backend</a> |
| </h5> |
| <p> |
| 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. |
| </p> |
| <div class="tip"><table border="0" summary="Tip"> |
| <tr> |
| <td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="../../../../../doc/src/images/tip.png"></td> |
| <th align="left">Tip</th> |
| </tr> |
| <tr><td align="left" valign="top"><p> |
| 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 <code class="computeroutput"><span class="identifier">notify</span><span class="special">-</span><span class="identifier">send</span></code> |
| program which is available on Linux (Ubuntu/Debian users can install it |
| 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 |
| 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. |
| </p></td></tr> |
| </table></div> |
| <p> |
| The definition of the backend is very similar to what we have seen in the |
| previous section: |
| </p> |
| <p> |
| </p> |
| <pre class="programlisting"><span class="comment">// The backend starts an external application to display notifications</span> |
| <span class="keyword">class</span> <span class="identifier">app_launcher</span> <span class="special">:</span> |
| <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> |
| <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> |
| <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> |
| <span class="special">></span> |
| <span class="special">{</span> |
| <span class="keyword">public</span><span class="special">:</span> |
| <span class="comment">// The function consumes the log records that come from the frontend</span> |
| <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> |
| <span class="special">};</span> |
| </pre> |
| <p> |
| </p> |
| <div class="calloutlist"><table border="0" summary="Callout list"> |
| <tr> |
| <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> |
| <td valign="top" align="left"><p> |
| target character type |
| </p></td> |
| </tr> |
| <tr> |
| <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> |
| <td valign="top" align="left"><p> |
| in order not to spawn too many application instances we require records |
| to be processed serial |
| </p></td> |
| </tr> |
| </table></div> |
| <p> |
| The first thing to notice is that the <code class="computeroutput"><span class="identifier">app_launcher</span></code> |
| backend derives from <code class="computeroutput"><a class="link" href="../boost/log/sinks/basic_formatte_idp44219440.html" title="Struct template basic_formatted_sink_backend">basic_formatted_sink_backend</a></code> |
| 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>. |
| 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. |
| </p> |
| <p> |
| The second notable difference from the previous examples is that <code class="computeroutput"><span class="identifier">consume</span></code> method takes an additional string |
| parameter besides the log record. This is the result of formatting. The |
| <code class="computeroutput"><span class="identifier">string_type</span></code> type is defined |
| by the <code class="computeroutput"><a class="link" href="../boost/log/sinks/basic_formatte_idp44219440.html" title="Struct template basic_formatted_sink_backend">basic_formatted_sink_backend</a></code> |
| base class and it corresponds to the requested character type. |
| </p> |
| <p> |
| We don't need to flush any buffers in this example, so we didn't specify |
| the <code class="computeroutput"><a class="link" href="../boost/log/sinks/flushing.html" title="Struct flushing">flushing</a></code> requirement |
| and omitted the <code class="computeroutput"><span class="identifier">flush</span></code> method |
| in the backend. Although we don't need any synchronization in our backend, |
| we specified <code class="computeroutput"><a class="link" href="../boost/log/sinks/synchronized_feeding.html" title="Struct synchronized_feeding">synchronized_feeding</a></code> |
| 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 |
| and cause a "fork bomb". |
| </p> |
| <p> |
| Now, the <code class="computeroutput"><span class="identifier">consume</span></code> implementation |
| is trivial: |
| </p> |
| <p> |
| </p> |
| <pre class="programlisting"><span class="comment">// The function consumes the log records that come from the frontend</span> |
| <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> |
| <span class="special">{</span> |
| <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> |
| <span class="special">}</span> |
| </pre> |
| <p> |
| </p> |
| <p> |
| 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. |
| </p> |
| <p> |
| The sink can be configured with the following code: |
| </p> |
| <p> |
| </p> |
| <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> |
| <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> |
| |
| <span class="comment">// Custom severity level formatting function</span> |
| <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> |
| <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> |
| <span class="special">{</span> |
| <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> |
| <span class="keyword">return</span> <span class="string">"normal"</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="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> |
| <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> |
| <span class="keyword">return</span> <span class="string">"low"</span><span class="special">;</span> |
| <span class="keyword">else</span> |
| <span class="keyword">return</span> <span class="string">"critical"</span><span class="special">;</span> |
| <span class="special">}</span> |
| |
| <span class="comment">// The function initializes the logging library</span> |
| <span class="keyword">void</span> <span class="identifier">init_logging</span><span class="special">()</span> |
| <span class="special">{</span> |
| <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> |
| |
| <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> |
| <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> |
| |
| <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> |
| <span class="special">{</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="string">"\""</span><span class="special">,</span> <span class="string">"\\\""</span><span class="special">),</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="string">"$"</span><span class="special">,</span> <span class="string">"\\$"</span><span class="special">),</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="string">"!"</span><span class="special">,</span> <span class="string">"\\!"</span><span class="special">)</span> |
| <span class="special">};</span> |
| |
| <span class="comment">// Make the formatter generate the command line for notify-send</span> |
| <span class="identifier">sink</span><span class="special">-></span><span class="identifier">set_formatter</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="string">"notify-send -t 2000 -u "</span> |
| <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> |
| <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> |
| <span class="special">[</span> |
| <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> |
| <span class="special">]</span> |
| <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> |
| <span class="special">[</span> |
| <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> |
| <span class="special">]</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">expr</span><span class="special">::</span><span class="identifier">message</span> <span class="special">]</span> <span class="special"><<</span> <span class="string">"\""</span> |
| <span class="special">);</span> |
| |
| <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> |
| |
| <span class="comment">// Add attributes that we will use</span> |
| <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> |
| <span class="special">}</span> |
| </pre> |
| <p> |
| </p> |
| <p> |
| 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> |
| frontend (as well as any other frontend) will detect that the <code class="computeroutput"><span class="identifier">app_launcher</span></code> backend requires formatting |
| and enable the corresponding functionality. The <code class="computeroutput"><span class="identifier">set_formatter</span></code> |
| method becomes available and can be used to set the formatting expression |
| 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. |
| We used <a class="link" href="detailed/expressions.html#log.detailed.expressions.attr_keywords" title="Defining attribute keywords">attribute |
| keywords</a> 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 <code class="computeroutput"><a class="link" href="../boost/log/expressions/char_decor_idp42727152.html" title="Function template char_decor">char_decor</a></code> decorator with |
| our custom replacement map. After the sink is configured we also add the |
| <a class="link" href="detailed/attributes.html#log.detailed.attributes.process_name" title="Current process name">current process name</a> |
| attribute to the core so that we don't have to add it to every record. |
| </p> |
| <p> |
| After all this is done, we can finally display some notifications: |
| </p> |
| <p> |
| </p> |
| <pre class="programlisting"><span class="keyword">void</span> <span class="identifier">test_notifications</span><span class="special">()</span> |
| <span class="special">{</span> |
| <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> |
| <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> |
| <span class="special">}</span> |
| </pre> |
| <p> |
| </p> |
| <p> |
| The complete code of this example is available <a href="../../../../../libs/log/example/doc/extension_app_launcher.cpp" target="_top">here</a>. |
| </p> |
| </div> |
| </div> |
| <table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr> |
| <td align="left"></td> |
| <td align="right"><div class="copyright-footer">Copyright © 2007-2015 Andrey |
| Semashev<p> |
| Distributed under the Boost Software License, Version 1.0. (See accompanying |
| 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>). |
| </p> |
| </div></td> |
| </tr></table> |
| <hr> |
| <div class="spirit-nav"> |
| <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> |
| </div> |
| </body> |
| </html> |