summaryrefslogtreecommitdiff
path: root/libs/log/doc/html/log/tutorial/attributes.html
blob: 8b5a2afd0e2459e532e83625a9dacda7eae19311 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Adding more information to log: Attributes</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&#160;1.&#160;Boost.Log v2">
<link rel="up" href="../tutorial.html" title="Tutorial">
<link rel="prev" href="sources.html" title="Creating loggers and writing logs">
<link rel="next" href="formatters.html" title="Log record formatting">
</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="sources.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../tutorial.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="formatters.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
</div>
<div class="section">
<div class="titlepage"><div><div><h3 class="title">
<a name="log.tutorial.attributes"></a><a class="link" href="attributes.html" title="Adding more information to log: Attributes">Adding more information to log:
      Attributes</a>
</h3></div></div></div>
<p>
        In previous sections we mentioned attributes and attribute values several
        times. Here we will discover how attributes can be used to add more data
        to log records.
      </p>
<p>
        Each log record can have a number of named attribute values attached. Attributes
        can represent any essential information about the conditions in which the
        log record occurred, such as position in the code, executable module name,
        current date and time, or any piece of data relevant to your particular application
        and execution environment. An attribute may behave as a value generator,
        in which case it would return a different value for each log record it's
        involved in. As soon as the attribute generates the value, the latter becomes
        independent from the creator and can be used by filters, formatters and sinks.
        But in order to use the attribute value one has to know its name and type,
        or at least a set of types it may have. There are a number of commonly used
        attributes implemented in the library, you can find the types of their values
        in the documentation.
      </p>
<p>
        Aside from that, as described in the <a class="link" href="../design.html" title="Design overview">Design overview</a>
        section, there are three possible scopes of attributes: source-specific,
        thread-specific and global. When a log record is made, attribute values from
        these three sets are joined into a single set and passed to sinks. This implies
        that the origin of the attribute makes no difference for sinks. Any attribute
        can be registered in any scope. When registered, an attribute is given a
        unique name in order to make it possible to search for it. If it happens
        that the same named attribute is found in several scopes, the attribute from
        the most specific scope is taken into consideration in any further processing,
        including filtering and formatting. Such behavior makes it possible to override
        global or thread-scoped attributes with the ones registered in your local
        logger, thus reducing thread interference.
      </p>
<p>
        Below is the description of the attribute registration process.
      </p>
<h5>
<a name="log.tutorial.attributes.h0"></a>
        <span class="phrase"><a name="log.tutorial.attributes.commonly_used_attributes"></a></span><a class="link" href="attributes.html#log.tutorial.attributes.commonly_used_attributes">Commonly
        used attributes</a>
      </h5>
<p>
        There are attributes that are likely to be used in nearly any application.
        Log record counter and a time stamp are good candidates. They can be added
        with a single function call:
      </p>
<pre class="programlisting"><span class="identifier">logging</span><span class="special">::</span><span class="identifier">add_common_attributes</span><span class="special">();</span>
</pre>
<p>
        With this call attributes "LineID", "TimeStamp", "ProcessID"
        and "ThreadID" are registered globally. The "LineID"
        attribute is a counter that increments for each record being made, the first
        record gets identifier 1. The "TimeStamp" attribute always yields
        the current time (i.e. the time when the log record is created, not the time
        it was written to a sink). The last two attributes identify the process and
        the thread in which every log record is emitted.
      </p>
<div class="note"><table border="0" summary="Note">
<tr>
<td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="../../../../../../doc/src/images/note.png"></td>
<th align="left">Note</th>
</tr>
<tr><td align="left" valign="top"><p>
          In single-threaded builds the "ThreadID" attribute is not registered.
        </p></td></tr>
</table></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 default, when application starts, no attributes are registered in the
          library. The application has to register all the necessary attributes in
          the library before it starts writing logs. This can be done as a part of
          the library initialization. A curious reader could have wondered how trivial
          logging works then. The answer is that the default sink doesn't really
          use any attribute values, except for the severity level, to compose its
          output. This is done to avoid the need for any initialization for trivial
          logging. Once you use filters or formatters and non-default sinks you will
          have to register the attributes you need.
        </p></td></tr>
</table></div>
<p>
        The <code class="computeroutput"><a class="link" href="../../boost/log/add_common_attributes.html" title="Function add_common_attributes">add_common_attributes</a></code>
        function is one of the several convenience helpers described <a class="link" href="../detailed/utilities.html#log.detailed.utilities.setup.convenience" title="Convenience functions">here</a>.
      </p>
<p>
        Some attributes are registered automatically on loggers construction. For
        example, <a class="link" href="../detailed/sources.html#log.detailed.sources.severity_level_logger" title="Loggers with severity level support"><code class="computeroutput"><span class="identifier">severity_logger</span></code></a> registers a source-specific
        attribute "Severity" which can be used to add a level of emphasis
        for different log records. For example:
      </p>
<p>
</p>
<pre class="programlisting"><span class="comment">// We define our own severity levels</span>
<span class="keyword">enum</span> <span class="identifier">severity_level</span>
<span class="special">{</span>
    <span class="identifier">normal</span><span class="special">,</span>
    <span class="identifier">notification</span><span class="special">,</span>
    <span class="identifier">warning</span><span class="special">,</span>
    <span class="identifier">error</span><span class="special">,</span>
    <span class="identifier">critical</span>
<span class="special">};</span>

<span class="keyword">void</span> <span class="identifier">logging_function</span><span class="special">()</span>
<span class="special">{</span>
    <span class="comment">// The logger implicitly adds a source-specific attribute 'Severity'</span>
    <span class="comment">// of type 'severity_level' on construction</span>
    <span class="identifier">src</span><span class="special">::</span><span class="identifier">severity_logger</span><span class="special">&lt;</span> <span class="identifier">severity_level</span> <span class="special">&gt;</span> <span class="identifier">slg</span><span class="special">;</span>

    <span class="identifier">BOOST_LOG_SEV</span><span class="special">(</span><span class="identifier">slg</span><span class="special">,</span> <span class="identifier">normal</span><span class="special">)</span> <span class="special">&lt;&lt;</span> <span class="string">"A regular message"</span><span class="special">;</span>
    <span class="identifier">BOOST_LOG_SEV</span><span class="special">(</span><span class="identifier">slg</span><span class="special">,</span> <span class="identifier">warning</span><span class="special">)</span> <span class="special">&lt;&lt;</span> <span class="string">"Something bad is going on but I can handle it"</span><span class="special">;</span>
    <span class="identifier">BOOST_LOG_SEV</span><span class="special">(</span><span class="identifier">slg</span><span class="special">,</span> <span class="identifier">critical</span><span class="special">)</span> <span class="special">&lt;&lt;</span> <span class="string">"Everything crumbles, shoot me now!"</span><span class="special">;</span>
<span class="special">}</span>
</pre>
<p>
      </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>
          You can define your own formatting rules for the severity level by defining
          <code class="computeroutput"><span class="keyword">operator</span> <span class="special">&lt;&lt;</span></code>
          for this type. It will be automatically used by the library formatters.
          See <a class="link" href="../detailed/expressions.html#log.detailed.expressions.attr" title="Generic attribute placeholder">this</a> section for
          more details.
        </p></td></tr>
</table></div>
<p>
        The <code class="computeroutput"><span class="identifier">BOOST_LOG_SEV</span></code> macro acts
        pretty much like <code class="computeroutput"><span class="identifier">BOOST_LOG</span></code>
        except that it takes an additional argument for the <code class="computeroutput"><span class="identifier">open_record</span></code>
        method of the logger. The <code class="computeroutput"><span class="identifier">BOOST_LOG_SEV</span></code>
        macro can be replaced with this equivalent:
      </p>
<p>
</p>
<pre class="programlisting"><span class="keyword">void</span> <span class="identifier">manual_logging</span><span class="special">()</span>
<span class="special">{</span>
    <span class="identifier">src</span><span class="special">::</span><span class="identifier">severity_logger</span><span class="special">&lt;</span> <span class="identifier">severity_level</span> <span class="special">&gt;</span> <span class="identifier">slg</span><span class="special">;</span>

    <span class="identifier">logging</span><span class="special">::</span><span class="identifier">record</span> <span class="identifier">rec</span> <span class="special">=</span> <span class="identifier">slg</span><span class="special">.</span><span class="identifier">open_record</span><span class="special">(</span><span class="identifier">keywords</span><span class="special">::</span><span class="identifier">severity</span> <span class="special">=</span> <span class="identifier">normal</span><span class="special">);</span>
    <span class="keyword">if</span> <span class="special">(</span><span class="identifier">rec</span><span class="special">)</span>
    <span class="special">{</span>
        <span class="identifier">logging</span><span class="special">::</span><span class="identifier">record_ostream</span> <span class="identifier">strm</span><span class="special">(</span><span class="identifier">rec</span><span class="special">);</span>
        <span class="identifier">strm</span> <span class="special">&lt;&lt;</span> <span class="string">"A regular message"</span><span class="special">;</span>
        <span class="identifier">strm</span><span class="special">.</span><span class="identifier">flush</span><span class="special">();</span>
        <span class="identifier">slg</span><span class="special">.</span><span class="identifier">push_record</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">move</span><span class="special">(</span><span class="identifier">rec</span><span class="special">));</span>
    <span class="special">}</span>
<span class="special">}</span>
</pre>
<p>
      </p>
<p>
        You can see here that the <code class="computeroutput"><span class="identifier">open_record</span></code>
        can take named arguments. Some logger types provided by the library have
        support for such additional parameters and this approach can certainly be
        used by users when writing their own loggers.
      </p>
<h5>
<a name="log.tutorial.attributes.h1"></a>
        <span class="phrase"><a name="log.tutorial.attributes.more_attributes"></a></span><a class="link" href="attributes.html#log.tutorial.attributes.more_attributes">More
        attributes</a>
      </h5>
<p>
        Let's see what's under the hood of that <a class="link" href="../detailed/utilities.html#log.detailed.utilities.setup.convenience" title="Convenience functions"><code class="computeroutput"><span class="identifier">add_common_attributes</span></code></a> function we
        used in the simple form section. It might look something like this:
      </p>
<pre class="programlisting"><span class="keyword">void</span> <span class="identifier">add_common_attributes</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">&lt;</span> <span class="identifier">logging</span><span class="special">::</span><span class="identifier">core</span> <span class="special">&gt;</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="identifier">core</span><span class="special">-&gt;</span><span class="identifier">add_global_attribute</span><span class="special">(</span><span class="string">"LineID"</span><span class="special">,</span> <span class="identifier">attrs</span><span class="special">::</span><span class="identifier">counter</span><span class="special">&lt;</span> <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="special">&gt;(</span><span class="number">1</span><span class="special">));</span>
    <span class="identifier">core</span><span class="special">-&gt;</span><span class="identifier">add_global_attribute</span><span class="special">(</span><span class="string">"TimeStamp"</span><span class="special">,</span> <span class="identifier">attrs</span><span class="special">::</span><span class="identifier">local_clock</span><span class="special">());</span>

    <span class="comment">// other attributes skipped for brevity</span>
<span class="special">}</span>
</pre>
<p>
        Here the <a class="link" href="../detailed/attributes.html#log.detailed.attributes.counter" title="Counters"><code class="computeroutput"><span class="identifier">counter</span></code></a>
        and <a class="link" href="../detailed/attributes.html#log.detailed.attributes.clock" title="Wall clock"><code class="computeroutput"><span class="identifier">local_clock</span></code></a>
        components are attribute classes, they derive from the common attribute interface
        <code class="computeroutput"><a class="link" href="../../boost/log/attribute.html" title="Class attribute">attribute</a></code>. The library
        provides a number of other <a class="link" href="../detailed/attributes.html" title="Attributes">attribute
        classes</a>, including the <a class="link" href="../detailed/attributes.html#log.detailed.attributes.function" title="Function objects as attributes"><code class="computeroutput"><span class="identifier">function</span></code></a> attribute that calls some
        function object on value acquisition. For example, we can in a similar way
        register a <a class="link" href="../detailed/attributes.html#log.detailed.attributes.named_scope" title="Named scopes"><code class="computeroutput"><span class="identifier">named_scope</span></code></a> attribute:
      </p>
<pre class="programlisting"><span class="identifier">core</span><span class="special">-&gt;</span><span class="identifier">add_global_attribute</span><span class="special">(</span><span class="string">"Scope"</span><span class="special">,</span> <span class="identifier">attrs</span><span class="special">::</span><span class="identifier">named_scope</span><span class="special">());</span>
</pre>
<p>
        This will give us the ability to store scope names in log for every log record
        the application makes. Here is how it's used:
      </p>
<p>
</p>
<pre class="programlisting"><span class="keyword">void</span> <span class="identifier">named_scope_logging</span><span class="special">()</span>
<span class="special">{</span>
    <span class="identifier">BOOST_LOG_NAMED_SCOPE</span><span class="special">(</span><span class="string">"named_scope_logging"</span><span class="special">);</span>

    <span class="identifier">src</span><span class="special">::</span><span class="identifier">severity_logger</span><span class="special">&lt;</span> <span class="identifier">severity_level</span> <span class="special">&gt;</span> <span class="identifier">slg</span><span class="special">;</span>

    <span class="identifier">BOOST_LOG_SEV</span><span class="special">(</span><span class="identifier">slg</span><span class="special">,</span> <span class="identifier">normal</span><span class="special">)</span> <span class="special">&lt;&lt;</span> <span class="string">"Hello from the function named_scope_logging!"</span><span class="special">;</span>
<span class="special">}</span>
</pre>
<p>
      </p>
<p>
        Logger-specific attributes are no less useful than global ones. Severity
        levels and channel names are the most obvious candidates to be implemented
        on the source level. Nothing prevents you from adding more attributes to
        your loggers, like this:
      </p>
<p>
</p>
<pre class="programlisting"><span class="keyword">void</span> <span class="identifier">tagged_logging</span><span class="special">()</span>
<span class="special">{</span>
    <span class="identifier">src</span><span class="special">::</span><span class="identifier">severity_logger</span><span class="special">&lt;</span> <span class="identifier">severity_level</span> <span class="special">&gt;</span> <span class="identifier">slg</span><span class="special">;</span>
    <span class="identifier">slg</span><span class="special">.</span><span class="identifier">add_attribute</span><span class="special">(</span><span class="string">"Tag"</span><span class="special">,</span> <span class="identifier">attrs</span><span class="special">::</span><span class="identifier">constant</span><span class="special">&lt;</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="special">&gt;(</span><span class="string">"My tag value"</span><span class="special">));</span>

    <span class="identifier">BOOST_LOG_SEV</span><span class="special">(</span><span class="identifier">slg</span><span class="special">,</span> <span class="identifier">normal</span><span class="special">)</span> <span class="special">&lt;&lt;</span> <span class="string">"Here goes the tagged record"</span><span class="special">;</span>
<span class="special">}</span>
</pre>
<p>
      </p>
<p>
        Now all log records made through this logger will be tagged with the specific
        attribute. This attribute value may be used later in filtering and formatting.
      </p>
<p>
        Another good use of attributes is the ability to mark log records made by
        different parts of application in order to highlight activity related to
        a single process. One can even implement a rough profiling tool to detect
        performance bottlenecks. For example:
      </p>
<p>
</p>
<pre class="programlisting"><span class="keyword">void</span> <span class="identifier">timed_logging</span><span class="special">()</span>
<span class="special">{</span>
    <span class="identifier">BOOST_LOG_SCOPED_THREAD_ATTR</span><span class="special">(</span><span class="string">"Timeline"</span><span class="special">,</span> <span class="identifier">attrs</span><span class="special">::</span><span class="identifier">timer</span><span class="special">());</span>

    <span class="identifier">src</span><span class="special">::</span><span class="identifier">severity_logger</span><span class="special">&lt;</span> <span class="identifier">severity_level</span> <span class="special">&gt;</span> <span class="identifier">slg</span><span class="special">;</span>
    <span class="identifier">BOOST_LOG_SEV</span><span class="special">(</span><span class="identifier">slg</span><span class="special">,</span> <span class="identifier">normal</span><span class="special">)</span> <span class="special">&lt;&lt;</span> <span class="string">"Starting to time nested functions"</span><span class="special">;</span>

    <span class="identifier">logging_function</span><span class="special">();</span>

    <span class="identifier">BOOST_LOG_SEV</span><span class="special">(</span><span class="identifier">slg</span><span class="special">,</span> <span class="identifier">normal</span><span class="special">)</span> <span class="special">&lt;&lt;</span> <span class="string">"Stopping to time nested functions"</span><span class="special">;</span>
<span class="special">}</span>
</pre>
<p>
      </p>
<p>
        Now every log record made from the <code class="computeroutput"><span class="identifier">logging_function</span></code>
        function, or any other function it calls, will contain the "Timeline"
        attribute with a high precision time duration passed since the attribute
        was registered. Based on these readings, one will be able to detect which
        parts of the code require more or less time to execute. The "Timeline"
        attribute will be unregistered upon leaving the scope of function <code class="computeroutput"><span class="identifier">timed_logging</span></code>.
      </p>
<p>
        See the <a class="link" href="../detailed/attributes.html" title="Attributes">Attributes</a> section
        for detailed description of attributes provided by the library. The complete
        code for this section is available <a href="../../../../../../libs/log/example/doc/tutorial_attributes.cpp" target="_top">here</a>.
      </p>
<h5>
<a name="log.tutorial.attributes.h2"></a>
        <span class="phrase"><a name="log.tutorial.attributes.defining_attribute_placeholders"></a></span><a class="link" href="attributes.html#log.tutorial.attributes.defining_attribute_placeholders">Defining
        attribute placeholders</a>
      </h5>
<p>
        As we will see in the coming sections, it is useful to define a keyword describing
        a particular attribute the application uses. This keyword will be able to
        participate in filtering and formatting expressions, like the <code class="computeroutput"><span class="identifier">severity</span></code> placeholder we have used in previous
        sections. For example, to define placeholders for some of the attributes
        we used in the previous examples we can write this:
      </p>
<pre class="programlisting"><span class="identifier">BOOST_LOG_ATTRIBUTE_KEYWORD</span><span class="special">(</span><span class="identifier">line_id</span><span class="special">,</span> <span class="string">"LineID"</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">severity</span><span class="special">,</span> <span class="string">"Severity"</span><span class="special">,</span> <span class="identifier">severity_level</span><span class="special">)</span>
<span class="identifier">BOOST_LOG_ATTRIBUTE_KEYWORD</span><span class="special">(</span><span class="identifier">tag_attr</span><span class="special">,</span> <span class="string">"Tag"</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">scope</span><span class="special">,</span> <span class="string">"Scope"</span><span class="special">,</span> <span class="identifier">attrs</span><span class="special">::</span><span class="identifier">named_scope</span><span class="special">::</span><span class="identifier">value_type</span><span class="special">)</span>
<span class="identifier">BOOST_LOG_ATTRIBUTE_KEYWORD</span><span class="special">(</span><span class="identifier">timeline</span><span class="special">,</span> <span class="string">"Timeline"</span><span class="special">,</span> <span class="identifier">attrs</span><span class="special">::</span><span class="identifier">timer</span><span class="special">::</span><span class="identifier">value_type</span><span class="special">)</span>
</pre>
<p>
        Each macro defines a keyword. The first argument is the placeholder name,
        the second is the attribute name and the last parameter is the attribute
        value type. Once defined, the placeholder can be used in template expressions
        and some other contexts of the library. More details on defining attribute
        keywords are available <a class="link" href="../detailed/expressions.html#log.detailed.expressions.attr_keywords" title="Defining attribute keywords">here</a>.
      </p>
</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 &#169; 2007-2014 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="sources.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../tutorial.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="formatters.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
</div>
</body>
</html>