BDE 4.14.0 Production release
Loading...
Searching...
No Matches
ball.h
Go to the documentation of this file.
1
/// @file ball.h
2
///
3
///
4
/// @defgroup ball Package ball
5
/// @brief Basic Application Library Logging (ball)
6
/// @addtogroup bal
7
/// @{
8
/// @addtogroup ball
9
/// [ball]: group__ball.html
10
/// @{
11
///
12
/// # Purpose {#ball-purpose}
13
/// Provide thread-safe logging toolkit suitable for all platforms.
14
///
15
/// # Mnemonic {#ball-mnemonic}
16
/// Basic Application Library Logging (ball)
17
///
18
/// # Description {#ball-description}
19
/// The 'ball' package provides a toolkit for logging messages in
20
/// applications and library code. The logger toolkit has an administration layer
21
/// that allows configuration both at start-up and during program execution, a
22
/// basic logger API for the most general possible use, and two sets of macros for
23
/// somewhat less flexible but simpler, more convenient use. In particular,
24
/// messages may be logged via these macros in either a C++ stream-style syntax
25
/// (i.e., with the '<<' operator) or a 'printf'-style syntax. Users are
26
/// encouraged to use the macros exclusively, because they provide uniformity, and
27
/// because they are less error-prone. See the {Appendix: Macro Reference}
28
/// section below.
29
///
30
/// This document contains a number of code examples, explained below:
31
///
32
/// * {Usage: Key Features} - short examples demonstrating key features of the
33
/// logging system.
34
///
35
/// * {Usage: Tutorial} - A series of related examples, building on each other,
36
/// to illustrate fundamental logging concepts.
37
///
38
/// * {Usage: Advanced Features} - Examples for advanced users aiming to
39
/// customize either the behavior or performance of the logger in more complex
40
/// ways.
41
///
42
/// ## Usage: Key Features
43
///
44
/// The following section provides short examples highlighting some important
45
/// features of logging.
46
///
47
/// ### Key Example 1: Writing to a Log
48
///
49
/// The following trivial example shows how to use the logging macros to log
50
/// messages at various levels of severity.
51
///
52
/// First, we include 'ball_log.h', then create an example function where we
53
/// initialize the log category within the context of this function.
54
/// The logging macros such as 'BALL_LOG_ERROR' will not compile unless a
55
/// category has been specified in the current lexical scope:
56
/// @code
57
/// #include <ball_log.h>
58
///
59
/// int processData() {
60
/// BALL_LOG_SET_CATEGORY("MYLIBRARY.MYSUBSYSTEM");
61
/// @endcode
62
/// Then, we record messages at various levels of severity. These messages will
63
/// be conditionally written to the log depending on the current logging
64
/// threshold of the category (configured using the 'ball::LoggerManager'
65
/// singleton):
66
/// @code
67
/// BALL_LOG_FATAL << "Write this message to the log if the log threshold "
68
/// << "is above 'ball::Severity::e_FATAL' (i.e., 32).";
69
///
70
/// BALL_LOG_TRACE << "Write this message to the log if the log threshold "
71
/// << "is above 'ball::Severity::e_TRACE' (i.e., 192).";
72
/// @endcode
73
/// Next, we demonstrate how to use proprietary code within logging macros.
74
/// Suppose you want to add the content of a vector to the log trace:
75
/// @code
76
/// bsl::vector<int> myVector(4, 328);
77
/// BALL_LOG_TRACE_BLOCK {
78
/// BALL_LOG_OUTPUT_STREAM << "myVector = [ ";
79
/// unsigned int position = 0;
80
/// for (bsl::vector<int>::const_iterator it = myVector.begin(),
81
/// end = myVector.end();
82
/// it != end;
83
/// ++it, ++position) {
84
/// BALL_LOG_OUTPUT_STREAM << position << ':' << *it << ' ';
85
/// }
86
/// BALL_LOG_OUTPUT_STREAM << ']';
87
/// }
88
/// }
89
/// @endcode
90
/// Notice that the code block will be conditionally executed depending on the
91
/// current logging threshold of the category. The code within the block must
92
/// not produce any side effects, because its execution depends on the current
93
/// logging configuration. The special macro 'BALL_LOG_OUTPUT_STREAM' provides
94
/// access to the log stream within the block.
95
///
96
/// Then we show a simple class that declares a log category for the class (log
97
/// categories can also be configured at namespace scope in a '.cpp' file):
98
/// @code
99
/// class AccountInformation {
100
/// BALL_LOG_SET_CLASS_CATEGORY("MYLIBRARY.AccountInformation");
101
///
102
/// void privateRetrieveData();
103
/// public:
104
/// void addSecurity(const bsl::string_view& security);
105
/// void removeSecurity(const bsl::string_view& security);
106
/// };
107
///
108
/// void AccountInformation::addSecurity(const bsl::string_view& security)
109
/// {
110
/// BALL_LOG_INFO << "addSecurity";
111
/// }
112
/// @endcode
113
/// Finally we can use a 'ball::ScopedAttribute' to associate an attribute with
114
/// the current thread's logging context.
115
/// @code
116
/// void AccountInformation::privateRetrieveData()
117
/// {
118
/// BALL_LOG_INFO << "retrieveData";
119
/// }
120
///
121
/// void AccountInformation::removeSecurity(const bsl::string_view& security)
122
/// {
123
/// ball::ScopedAttribute securityAttribute("mylibrary.security", security);
124
/// BALL_LOG_INFO << "removeSecurity";
125
///
126
/// privateRetrieveData();
127
/// }
128
/// @endcode
129
/// Notice that the attribute "mylibrary.security" will be associated with each
130
/// log message generated by the current thread until the destruction of the
131
/// 'securityAttribute' object (including the log message created by
132
/// 'privateRetrieveData'). To publish the attribute to the log the
133
/// 'ball::Observer' must be configured correctly (e.g., using the "%a" format
134
/// specification with 'ball::FileObserver' or 'ball::RecordStringFormatter'), as
135
/// we do in the subsequent example.
136
///
137
/// ### Key Example 2: Initialization
138
///
139
/// Clients that perform logging must first instantiate the singleton logger
140
/// manager using the 'ball::LoggerManagerScopedGuard' class. This example
141
/// shows how to create a logger manager with basic "default behavior".
142
/// Subsequent examples will show more customized behavior.
143
///
144
/// The following snippets of code illustrate the initialization sequence
145
/// (typically performed near the top of 'main').
146
///
147
/// First, we create a 'ball::LoggerManagerConfiguration' object,
148
/// 'configuration', and set the logging "pass-through" level -- the level at
149
/// which log records are published to registered observers -- to 'WARN' (see
150
/// {'Categories, Severities, and Threshold Levels'}):
151
/// @code
152
/// // myApp.cpp
153
///
154
/// int main()
155
/// {
156
/// ball::LoggerManagerConfiguration configuration;
157
/// configuration.setDefaultThresholdLevelsIfValid(ball::Severity::e_WARN);
158
/// @endcode
159
/// Next, create a 'ball::LoggerManagerScopedGuard' object whose constructor
160
/// takes the configuration object just created. The guard will initialize the
161
/// logger manager singleton on creation and destroy the singleton upon
162
/// destruction. This guarantees that any resources used by the logger manager
163
/// will be properly released when they are not needed:
164
/// @code
165
/// ball::LoggerManagerScopedGuard guard(configuration);
166
/// @endcode
167
/// Note that the application is now prepared to log messages using the 'ball'
168
/// logging subsystem, but until the application registers an observer, all log
169
/// messages will be discarded.
170
///
171
/// Finally, we create a 'ball::FileObserver' object 'observer' that will
172
/// publish records to a file, and exceptional records to 'stdout'. We
173
/// configure the log format to publish log attributes (see
174
/// {Key Example 1: Write to a Log}, enable the logger to write to a log file,
175
/// and then register 'observer' with the logger manager. Note that observers
176
/// must be registered by name; this example simply uses "default" for a name:
177
/// @code
178
/// bslma::Allocator *alloc = bslma::Default::globalAllocator(0);
179
///
180
/// bsl::shared_ptr<ball::FileObserver> observer =
181
/// bsl::allocate_shared<ball::FileObserver>(alloc);
182
///
183
/// observer->setLogFormat(
184
/// ball::RecordStringFormatter::k_BASIC_ATTRIBUTE_FORMAT,
185
/// ball::RecordStringFormatter::k_BASIC_ATTRIBUTE_FORMAT);
186
///
187
/// if (0 != observer->enableFileLogging("myapplication.log.%T")) {
188
/// bsl::cout << "Failed to enable logging" << bsl::endl;
189
/// return -1;
190
/// }
191
/// ball::LoggerManager::singleton().registerObserver(observer, "default");
192
/// @endcode
193
/// The application is now prepared to log messages using the 'ball' logging
194
/// subsystem:
195
/// @code
196
/// // ...
197
///
198
/// BALL_LOG_SET_CATEGORY("MYLIBRARY.MYSUBSYSTEM");
199
/// BALL_LOG_ERROR << "Exiting the application (0)";
200
///
201
/// return 0;
202
/// }
203
/// @endcode
204
///
205
/// ## Hierarchical Synopsis
206
///
207
/// The 'ball' package currently has 51 components having 17 levels of physical
208
/// dependency. The list below shows the hierarchical ordering of the components.
209
/// The order of components within each level is not architecturally significant,
210
/// just alphabetical.
211
/// @code
212
/// 17. ball_asyncfileobserver
213
///
214
/// 16. ball_fileobserver
215
/// ball_logfilecleanerutil
216
///
217
/// 15. ball_fileobserver2
218
/// ball_logthrottle
219
///
220
/// 14. ball_log
221
///
222
/// 13. ball_administration
223
///
224
/// 12. ball_loggercategoryutil
225
/// ball_loggerfunctorpayloads
226
///
227
/// 11. ball_loggermanager
228
/// ball_scopedattribute
229
/// ball_scopedattributes
230
///
231
/// 10. ball_attributecontext
232
///
233
/// 9. ball_categorymanager
234
///
235
/// 8. ball_category
236
///
237
/// 7. ball_broadcastobserver
238
/// ball_filteringobserver
239
/// ball_multiplexobserver !DEPRECATED!
240
/// ball_ruleset
241
///
242
/// 6. ball_observeradapter
243
/// ball_rule
244
/// ball_streamobserver
245
/// ball_testobserver
246
///
247
/// 5. ball_fixedsizerecordbuffer
248
/// ball_observer
249
/// ball_predicateset !DEPRECATED!
250
/// ball_recordjsonformatter
251
/// ball_recordstringformatter
252
///
253
/// 4. ball_managedattributeset
254
/// ball_record
255
///
256
/// 3. ball_attributecontainerlist
257
/// ball_defaultattributecontainer
258
/// ball_predicate !DEPRECATED!
259
/// ball_userfields
260
///
261
/// 2. ball_attributecollectorregistry
262
/// ball_attributecontainer
263
/// ball_context
264
/// ball_loggermanagerconfiguration
265
/// ball_managedattribute
266
/// ball_recordbuffer
267
/// ball_severityutil
268
/// ball_userfieldvalue
269
///
270
/// 1. ball_attribute
271
/// ball_countingallocator
272
/// ball_loggermanagerdefaults
273
/// ball_patternutil
274
/// ball_recordattributes
275
/// ball_severity
276
/// ball_thresholdaggregate
277
/// ball_transmission
278
/// ball_userfieldtype
279
/// @endcode
280
///
281
/// ## Component Synopsis
282
///
283
/// @ref ball_administration :
284
/// Provide a suite of utility functions for logging administration.
285
///
286
/// @ref ball_asyncfileobserver :
287
/// Provide an asynchronous observer that logs to a file and `stdout`.
288
///
289
/// @ref ball_attribute :
290
/// Provide a representation of (literal) name/value pairs.
291
///
292
/// @ref ball_attributecollectorregistry :
293
/// Provide a registry for attribute collector functors.
294
///
295
/// @ref ball_attributecontainer :
296
/// Provide a protocol for containers holding logging attributes.
297
///
298
/// @ref ball_attributecontainerlist :
299
/// Provide a list of attribute container addresses.
300
///
301
/// @ref ball_attributecontext :
302
/// Provide a container for storing attributes and caching results.
303
///
304
/// @ref ball_broadcastobserver :
305
/// Provide a broadcast observer that forwards to other observers.
306
///
307
/// @ref ball_category :
308
/// Provide a container for a name and associated thresholds.
309
///
310
/// @ref ball_categorymanager :
311
/// Provide a manager of named categories each having "thresholds".
312
///
313
/// @ref ball_context :
314
/// Provide a container for the context of a transmitted log record.
315
///
316
/// @ref ball_countingallocator :
317
/// Provide a concrete allocator that keeps count of allocated bytes.
318
///
319
/// @ref ball_defaultattributecontainer :
320
/// Provide a default container for storing attribute name/value pairs.
321
///
322
/// @ref ball_fileobserver :
323
/// Provide a thread-safe observer that logs to a file and to `stdout`.
324
///
325
/// @ref ball_fileobserver2 :
326
/// Provide a thread-safe observer that emits log records to a file.
327
///
328
/// @ref ball_filteringobserver :
329
/// Provide an observer that filters log records.
330
///
331
/// @ref ball_fixedsizerecordbuffer :
332
/// Provide a thread-safe fixed-size buffer of record handles.
333
///
334
/// 'ball_log':
335
/// Provide macros and utility functions to facilitate logging.
336
///
337
/// @ref ball_logfilecleanerutil :
338
/// Provide a utility class for removing log files.
339
///
340
/// @ref ball_loggercategoryutil :
341
/// Provide a suite of utility functions for category management.
342
///
343
/// @ref ball_loggerfunctorpayloads :
344
/// Provide a suite of logger manager singleton functor payloads.
345
///
346
/// @ref ball_loggermanager :
347
/// Provide a manager of core logging functionality.
348
///
349
/// @ref ball_loggermanagerconfiguration :
350
/// Provide a constrained-attribute class for the logger manager.
351
///
352
/// @ref ball_loggermanagerdefaults :
353
/// Provide constrained default attributes for the logger manager.
354
///
355
/// @ref ball_logthrottle :
356
/// Provide throttling equivalents of some of the `ball_log` macros.
357
///
358
/// @ref ball_managedattribute :
359
/// Provide a wrapper for `ball::Attribute` with managed name storage.
360
///
361
/// @ref ball_managedattributeset :
362
/// Provide a container for managed attributes.
363
///
364
/// @ref ball_multiplexobserver : !DEPRECATED!
365
/// Provide a multiplexing observer that forwards to other observers.
366
///
367
/// @ref ball_observer :
368
/// Define a protocol for receiving and processing log records.
369
///
370
/// @ref ball_observeradapter :
371
/// Provide a helper for implementing the `ball::Observer` protocol.
372
///
373
/// @ref ball_patternutil :
374
/// Provide a utility class for string pattern matching.
375
///
376
/// @ref ball_predicate : !DEPRECATED!
377
/// Provide a predicate object that consists of a name/value pair.
378
///
379
/// @ref ball_predicateset : !DEPRECATED!
380
/// Provide a container for managed attributes.
381
///
382
/// @ref ball_record :
383
/// Provide a container for the fields and attributes of a log record.
384
///
385
/// @ref ball_recordattributes :
386
/// Provide a container for a fixed set of fields suitable for logging.
387
///
388
/// @ref ball_recordbuffer :
389
/// Provide a protocol for managing log record handles.
390
///
391
/// @ref ball_recordjsonformatter :
392
/// Provide a formatter for log records that renders output in JSON.
393
///
394
/// @ref ball_recordstringformatter :
395
/// Provide a record formatter that uses a `printf`-style format spec.
396
///
397
/// @ref ball_rule :
398
/// Provide an object having a pattern, thresholds, and attributes.
399
///
400
/// @ref ball_ruleset :
401
/// Provide a set of unique rules.
402
///
403
/// @ref ball_scopedattribute :
404
/// Provide a scoped guard for a single BALL attribute.
405
///
406
/// @ref ball_scopedattributes :
407
/// Provide a class to add and remove attributes automatically.
408
///
409
/// @ref ball_severity :
410
/// Enumerate a set of logging severity levels.
411
///
412
/// @ref ball_severityutil :
413
/// Provide a suite of utility functions on `ball::Severity` levels.
414
///
415
/// @ref ball_streamobserver :
416
/// Provide an observer that emits log records to a stream.
417
///
418
/// @ref ball_testobserver :
419
/// Provide an instrumented observer for testing.
420
///
421
/// @ref ball_thresholdaggregate :
422
/// Provide an aggregate of the four logging threshold levels.
423
///
424
/// @ref ball_transmission :
425
/// Enumerate the set of states for log record transmission.
426
///
427
/// @ref ball_userfields :
428
/// Provide a container of user supplied field values.
429
///
430
/// @ref ball_userfieldtype :
431
/// Enumerate the set of data types for a user supplied attribute.
432
///
433
/// @ref ball_userfieldvalue :
434
/// Provide a type for the value of a user supplied field.
435
///
436
/// ## Multi-Threaded Logging
437
///
438
/// The 'ball' logging toolkit is thread-enabled and suitable for multi-threaded
439
/// applications. At the user's option, the multi-threaded toolkit permits each
440
/// thread to install a distinct instance of 'ball::Logger'; the process-wide
441
/// "default" logger is available to any thread that does not install its own
442
/// logger.
443
///
444
/// ## Logging Features Overview
445
///
446
/// This section provides a brief overview of the features of the 'ball' logging
447
/// toolkit, and introduces (without formal definition) some of the terminology
448
/// used in 'ball'. Refer to the hierarchical and alphabetical Synopsis sections
449
/// above to associate these features with the overall 'ball' design, and see the
450
/// various sections below for more detailed descriptions.
451
///
452
/// * User-assigned Severity Level associated with each log message
453
///
454
/// * User-defined Category associated with each log message
455
///
456
/// * Four Severity Threshold Levels associated with each Category
457
///
458
/// * Category threshold levels are configurable both at start-up and during
459
/// program execution
460
///
461
/// * Severity Level plus (administrable) per-category Threshold Levels allow:
462
/// * efficient suppression of message-logging operation (one 'if' statement)
463
/// * efficient logging-to-memory (subsequent publication is user-controlled)
464
/// * immediate publication of log message (to user-defined "observer")
465
/// * immediate publication of all messages currently held in (finite) buffer
466
///
467
/// * Logging to memory permits efficient logging of large quantities of "trace"
468
/// information that will not be published *unless* a specific user-defined
469
/// logging event occurs ("trace-back" feature)
470
///
471
/// * User can always configure logger to publish every message, if desired
472
///
473
/// * User defines and registers one or more "observers", each of which in turn
474
/// defines the behavior of the "publish" operation (e.g., write to file, write
475
/// to console, process message and take specific action, etc.)
476
///
477
/// * Convenient logging macros simplify usage for the most common operations
478
///
479
/// * Optional functors and other parameters allow customized logging behavior
480
/// (e.g., default threshold levels, category name filtering)
481
///
482
/// * The ability to configure thread-context dependent rules for logging (e.g.,
483
/// configuring more verbose logging for threads processing a request for
484
/// a particular user id). See {'Rule-Based Logging'} below.
485
///
486
///
487
/// The 'ball' package provides a flexible logging toolkit that supports several
488
/// standard and non-standard features. Perhaps most notable among the
489
/// non-standard features is the ability to write messages to an in-memory
490
/// "circular" (finite) buffer with the expectation that those messages will be
491
/// overwritten and never actually "logged" to any permanent medium. With this
492
/// feature, during normal production operation a large quantity of "trace-back"
493
/// information can be "logged" to memory, and typically be discarded, without
494
/// having clogged production resources. If, however, some "error" occurs, the
495
/// information just prior to that error will be available for fast diagnosis and
496
/// debugging. It is easy to re-configure logger operation so that every message
497
/// is archived, if that is the preferred behavior, but the efficient trace-back
498
/// feature is an explicit design goal. See the "Hello world!" examples under
499
/// {Usage} below for illustrations of how to configure the logger behavior.
500
///
501
/// Another key design feature is the "observer" object. 'ball' defines the
502
/// 'ball::Observer' protocol (abstract interface), and provides a few concrete
503
/// observers. It is expected that most users will find the concrete observers
504
/// that are provided in 'ball' to be sufficient for their needs. However, users
505
/// are free to define their own observers tailored to meet their specific
506
/// requirements. In the current release, exactly one observer is registered with
507
/// the 'ball' logger on initialization; we anticipate that multiple observers,
508
/// e.g., one per thread or perhaps one per logger, may be available in future
509
/// releases. An observer makes its 'publish' method available to the logger;
510
/// since the 'publish' method is free to do almost anything that the user wants,
511
/// the limitation of one observer per process is not very restrictive. The
512
/// observer may: write the message to a simple file, write the message to a set
513
/// of managed files, write the message to the console (perhaps with some
514
/// information removed and/or added), process the message (complete with Category
515
/// and Severity information) and take specific responsive actions, or any
516
/// combination of these or other behaviors. In particular, 'ball' provides a
517
/// "broadcast observer", 'ball::BroadcastObserver', that forwards log records to
518
/// any number of observers registered with the broadcast observer. Note that
519
/// 'ball::LoggerManager' contains an integrated broadcast observer and all
520
/// observers registered with the logger manager will receive log records.
521
///
522
/// ## Severity Levels and Categories: a Brief Overview
523
///
524
/// The logger supports the notions of severity levels and categories. Every
525
/// message is logged at some severity level and to some category. Categories are
526
/// user-defined (except for the "default category"), and can be separately
527
/// managed; in particular, the behavior of any given message-logging operation
528
/// depends upon specific severity level threshold settings for the category to
529
/// which the message is being logged. See the @ref ball_loggermanager component for
530
/// more details about categories and category administration.
531
///
532
/// Severity levels are, from the perspective of the basic logger API,
533
/// user-settable in the range '[0 .. 255]'. Much more commonly, however, (and
534
/// necessarily when using the 'ball' convenience macros defined in the 'ball_log'
535
/// component), the user will choose to use the fixed set of enumerated severity
536
/// levels as defined in the @ref ball_severity component. The enumerator names
537
/// suggest a meaning that is consistent with common practice, but no policy is
538
/// enforced by the basic logger API or the macros.
539
///
540
/// For convenient reference only, the 'ball::Severity::Level' 'enum'
541
/// implementation is presented below. Note that this implementation and the
542
/// specific numerical values may not be relied upon. Also note that the
543
/// enumerator 'NONE' is deprecated, since its name has proven to be confusing.
544
/// As an argument to 'logMessage', it would satisfy "none" of the thresholds, but
545
/// when provided as a threshold value itself, it would enable "all" of the
546
/// enumerated 'logMessage' requests (and all of the macros). Use 'e_OFF' as a
547
/// threshold level to disable all logging events for a particular logging
548
/// behavior (see the "Log Record Storage and Publication" section below).
549
/// @code
550
/// enum Level {
551
/// e_OFF = 0, // disable generation of corresponding message
552
/// e_FATAL = 32, // a condition that will (likely) cause a *crash*
553
/// e_ERROR = 64, // a condition that *will* cause incorrect behavior
554
/// e_WARN = 96, // a *potentially* problematic condition
555
/// e_INFO = 128, // data about the running process
556
/// e_DEBUG = 160, // information useful while debugging
557
/// e_TRACE = 192, // execution trace data
558
/// };
559
/// @endcode
560
/// Note that numerically lower 'Level' values correspond to conditions having
561
/// greater severity.
562
///
563
/// ## Messages, Records, and other ball Terminology
564
///
565
/// The entity that the logger logs is called a "log record" in 'ball'. A log
566
/// record consists of a number of fixed and user-defined fields; one of the fixed
567
/// fields is called a "log message" (or "message" for short). In casual usage,
568
/// we use "record" and "message" interchangeably, but note that they are distinct
569
/// concepts. The set of fixed fields that constitute a log record are defined in
570
/// the @ref ball_recordattributes component. A brief description of the fixed
571
/// fields (or "record attributes") is given in the following table.
572
/// @code
573
/// Attribute Type Description Default
574
/// ---------- -------------- ------------------------------ -------
575
/// timestamp bdlt::Datetime creation date and time (*Note*)
576
/// processID int process id of creator 0
577
/// threadID Uint64 thread id of creator 0
578
/// fileName bsl::string file where created (__FILE__) ""
579
/// lineNumber int line number in file (__LINE__) 0
580
/// category bsl::string category of logged record ""
581
/// severity int severity of logged record 0
582
/// message bsl::string log message text ""
583
///
584
/// Note: The default value given to the timestamp attribute is implementation
585
/// defined. (See the @ref bdlt_datetime component-level documentation for
586
/// more information.)
587
/// @endcode
588
/// The user may wish to specify a set of user-defined fields to be included
589
/// within every log record for a given program. Such user-defined fields are
590
/// represented in each log record by an instance of 'ball::UserFields'. A
591
/// 'ball::LoggerManagerConfiguration::UserFieldsPopulatorCallback' functor may
592
/// be provided to the logger manager on construction (via a
593
/// 'ball::LoggerManagerConfiguration' object) that, when invoked, "populates"
594
/// (i.e., provides the values for) all elements of the 'ball::UserFields' object
595
/// containing the user-defined fields. See the @ref ball_record component for more
596
/// information about the use of 'ball::UserFields' in logging, and see the
597
/// @ref ball_loggermanager component for more information about installing a user
598
/// populator callback.
599
///
600
/// ### Constraints on Message Encodings
601
///
602
/// The 'ball' infrastructure has no provisions for specifying or enforcing
603
/// constraints on the encoding used for logged messages (in general, logged
604
/// messages are published to registered observers in the encoding in which they
605
/// were originally supplied). However, particular applications, frameworks, and
606
/// consumers of log records (e.g., readers of log files) may place their own
607
/// constraints on log records with respect to encoding. Clients generating log
608
/// records should be aware of the encoding requirements for logged messages
609
/// generated by their application (e.g., many applications at Bloomberg require
610
/// UTF-8 encoded messages).
611
///
612
/// ## Log Record Storage and Publication
613
///
614
/// The logger achieves good run-time performance while capturing critical log
615
/// messages by *selectively* storing log records and *selectively* publishing a
616
/// subset of those stored records. The determination of whether a log record
617
/// should be stored or published is based on the "category" and "severity" level
618
/// that are associated with each record. The severity of a record is intended to
619
/// indicate that record's relative urgency. A category is comprised of a name
620
/// (an arbitrary string) and four severity threshold levels (see the
621
/// component-level documentation of @ref ball_severity for more information on
622
/// typical severity levels). A message's text, its associated severity, and the
623
/// name of its associated category are each among the fixed fields in the logged
624
/// record (see the "Messages, Records, and other 'ball' Terminology" section
625
/// above).
626
///
627
/// The following four settings define the log record severity levels at which the
628
/// logger takes certain actions. The logger determines the disposition of a
629
/// record based on its severity level in relation to the four *severity*
630
/// *threshold* *levels* of the category associated with the message:
631
///
632
/// Record:
633
/// If the severity level of the record is at least as severe as the Record
634
/// threshold level of the associated category, then the record will be stored
635
/// by the logger in its log record buffer (i.e., it will be recorded).
636
///
637
/// Pass:
638
/// If the severity level of the record is at least as severe as the Pass
639
/// threshold level of the associated category, then the record will be
640
/// immediately published by the logger (i.e., it will be transmitted to the
641
/// logger's downstream recipient -- the observer).
642
///
643
/// Trigger:
644
/// If the severity level of the record is at least as severe as the Trigger
645
/// threshold level of the associated category, then the record will cause
646
/// immediate publication of that record and any records in the logger's log
647
/// record buffer (i.e., this record will trigger a general log record dump).
648
///
649
/// Trigger-All:
650
/// If the severity level of the record is at least as severe as the
651
/// Trigger-All threshold level of the associated category, then the record
652
/// will cause immediate publication of that record and all other log records
653
/// stored by *all* active loggers.
654
///
655
/// Note that more than one of the above actions can apply to a given log record
656
/// since the four threshold levels are independent of one another. Also note
657
/// that the determination of whether a log record should cause a Trigger or
658
/// Trigger-All event is based on the trigger and trigger-all threshold levels,
659
/// respectively, of the category associated with the record being logged. The
660
/// categories and severities associated with log records stored earlier are not
661
/// taken into account.
662
///
663
/// ## The Basic Tools in the ball Logging Toolkit
664
///
665
/// This section will be expanded upon in the future. For now, we provide a brief
666
/// introduction to the objects that are most central to understanding basic
667
/// logger operation, so that we may use their names elsewhere in this
668
/// documentation.
669
///
670
/// ### ball::LoggerManager
671
///
672
/// 'ball::LoggerManager' is a singleton that must be constructed with a
673
/// configuration object (see below) and an optional 'bslma' allocator before any
674
/// true logging operations can be performed. The logger manager's main tasks are
675
/// to administer categories and to "allocate" loggers. If the user has *not*
676
/// allocated and installed any logger instances, then the 'getLogger' method will
677
/// return the "default logger" that is a specific instance of 'ball::Logger'
678
/// owned and managed by the logger manager. This default logger is suitable for
679
/// single-threaded and multi-threaded use, and is used "transparently" by users
680
/// willing to accept default-logger behavior. See {Usage} below.
681
///
682
/// The logger manager maintains an internal broadcast observer (see below) and
683
/// provides the 'registerObserver', 'deregisterObserver', and 'findObserver'
684
/// methods that register, deregister, and find observers, respectively. The
685
/// internal broadcast observer forwards all log records that it receives to all
686
/// registered observers.
687
///
688
/// The logger manager provides the 'allocateLogger' and 'setLogger' methods that
689
/// allocate arbitrarily many loggers and install (at most) one logger per thread,
690
/// respectively. Any thread in which the 'setLogger' method was not called will
691
/// use the default logger. See {Example 6} and {Example 7} below for a
692
/// discussion of how and why to allocate multiple loggers.
693
///
694
/// ### ball::Logger
695
///
696
/// 'ball::Logger' provides the most central logging functionality, namely the
697
/// 'logMessage' method. However, most users will never call 'logMessage'
698
/// directly, but rather will use the sets of macros defined in the 'ball_log'
699
/// component. See the {Appendix: Macro Reference} section below.
700
///
701
/// The "default" 'ball::Logger' instance managed by the logger manager is
702
/// suitable for many purposes, so typical users need never interact with logger
703
/// instances explicitly at all. However, in multi-threaded applications (and
704
/// perhaps also in certain special-purpose single-threaded programs), the user
705
/// may want to instantiate one or more logger instances and install at most one
706
/// logger instance per thread. See {Example 6} and {Example 7} below.
707
///
708
/// ### ball::LoggerManagerConfiguration
709
///
710
/// 'ball::LoggerManagerConfiguration' is a "configuration" object that must be
711
/// supplied to the logger manager at construction. The default object configures
712
/// the "default" logging behavior; the user can change the logger behavior by
713
/// changing attributes in the configuration object. The name, type, and
714
/// description of the configuration attributes are presented in the two tables
715
/// below; the awkward repetition of 'NAME' is due to the long type names of the
716
/// functor types.
717
/// @code
718
/// NAME TYPE
719
/// ------------------- -----------------------------------------------------
720
/// defaults ball::LoggerManagerDefaults
721
///
722
/// userFieldsPopulatorCallback
723
/// bsl::function<void(ball::UserFields *)>
724
///
725
/// categoryNameFilterCallback
726
/// bsl::function<void(bsl::string *, const char *)>
727
///
728
/// defaultThresholdLevelsCallback
729
/// bsl::function<void(int *, int *, int *, int *,
730
/// const char *)>
731
///
732
/// logOrder ball::LoggerManagerConfiguration::LogOrder
733
/// @endcode
734
///
735
/// @code
736
/// NAME DESCRIPTION
737
/// ------------------- -----------------------------------------------------
738
/// defaults constrained defaults for buffer size and thresholds
739
///
740
/// userFieldsPopulatorCallback
741
/// populates optional user fields in a log record
742
/// [!DEPRECATED!]
743
///
744
/// categoryNameFilterCallback
745
/// invoked on category names, e.g., to re-map characters
746
///
747
/// defaultThresholdLevelsCallback
748
/// sets category severity threshold levels (by default)
749
///
750
/// logOrder log message publication order on "trigger" events
751
/// @endcode
752
/// Note that the configuration object is not value-semantic because its three
753
/// functor attributes are not value-semantic. For this reason, the single
754
/// "defaults" attribute, a value-semantic simply-constrained attribute type, was
755
/// factored out from the configuration type. The defaults object holds six
756
/// numerical attributes and is described next.
757
///
758
/// ### ball::LoggerManagerDefaults
759
///
760
/// 'ball::LoggerManagerDefaults' is a value-semantic simply-constrained
761
/// attribute type that is itself an attribute of the above "configuration"
762
/// object. The "defaults" object contains the following six constrained
763
/// attributes.
764
/// @code
765
/// TYPE NAME DESCRIPTION
766
/// ----------- ---------------- -------------------------------------
767
/// int recordBufferSize size in bytes of *default* logger's
768
/// record buffer
769
///
770
/// int loggerBufferSize default size in bytes of *each*
771
/// logger's "scratch" buffer (for macros)
772
///
773
/// char recordLevel default record severity level
774
///
775
/// char passLevel default pass-through severity level
776
///
777
/// char triggerLevel default trigger severity level
778
///
779
/// char triggerAllLevel default trigger-all severity level
780
/// @endcode
781
/// The constraints are as follows:
782
/// @code
783
/// NAME CONSTRAINT
784
/// +--------------------+---------------------------------------------+
785
/// | recordBufferSize | 1 <= recordBufferSize |
786
/// +--------------------+---------------------------------------------+
787
/// | loggerBufferSize | 1 <= loggerBufferSize |
788
/// +--------------------+---------------------------------------------+
789
/// | recordLevel | 0 <= recordLevel <= 255 |
790
/// | passLevel | 0 <= passLevel <= 255 |
791
/// | triggerLevel | 0 <= triggerLevel <= 255 |
792
/// | triggerAllLevel | 0 <= triggerAllLevel <= 255 |
793
/// +--------------------+---------------------------------------------+
794
/// @endcode
795
/// 'ball::LoggerManagerDefaults' was factored out of the configuration object
796
/// because the former is purely value-semantic, and can be generated from, e.g.,
797
/// a configuration file. From this perspective, it is very convenient to set
798
/// this one attribute in the configuration object. It is quite possible to
799
/// ignore the independent existence of 'ball::LoggerManagerDefaults' and set the
800
/// above constrained attributes directly in the
801
/// 'ball::LoggerManagerConfiguration' object. The latter is more convenient in
802
/// "simple" usage, and is illustrated in {Usage} below.
803
///
804
/// ### ball::Observer
805
///
806
/// 'ball::Observer' is the object that receives "published" log messages.
807
/// Specifically, 'ball::Observer' is a protocol (abstract interface), and the
808
/// user must supply a concrete implementation (separately written or chosen from
809
/// existing 'ball' observers). The observer provides a 'publish' method that
810
/// defines the behavior of the logical concept of "publication" of a message used
811
/// throughout this document. Note that multiple observers may be registered with
812
/// the logger manager and each registered observer will receive "published" log
813
/// messages.
814
///
815
/// ### Logging Macros
816
///
817
/// For both convenience and uniformity of programming, 'ball' provides a suite of
818
/// logging macros (defined in the 'ball_log' component). All but the most
819
/// demanding and customized applications will use the logging macros for basic
820
/// logging operations (e.g., setting categories and logging messages). The
821
/// logger manager interface is used primarily to administer global and
822
/// category-specific severity threshold levels, and to allocate loggers. See the
823
/// {Usage} and {Appendix: Macro Reference} sections below for examples and
824
/// reference documentation, respectively.
825
///
826
/// The user should note the following two facts about macro usage:
827
///
828
/// 1. The 'BALL_LOG_SET_CATEGORY' macro is not only convenient, it is most often
829
/// *required* for macro use, since it defines symbols that the other macros
830
/// expect to see. (Alternatively, either the 'BALL_LOG_SET_DYNAMIC_CATEGORY'
831
/// or 'BALL_LOG_SET_CLASS_CATEGORY' macro may be used instead; see the
832
/// 'ball_log' component for details.)
833
///
834
/// 2. There are two styles of logging macros, C++ stream style and 'printf'
835
/// style. The 'printf'-style logging macros write to an intermediate
836
/// fixed-size buffer managed by the active logger instance. For any one macro
837
/// invocation, data larger than the buffer size is (silently) truncated. The
838
/// buffer size is 8k bytes by default; the buffer size can be queried by the
839
/// logger 'messageBufferSize' instance method, and can be set at logger
840
/// manager construction via the 'ball::LoggerManagerConfiguration' object.
841
/// (This is not the case with the C++ stream-style macros, which are more
842
/// efficient.)
843
///
844
/// ## More About Categories
845
///
846
/// When the logger manager singleton is created, a distinguished category known
847
/// as the *Default* *Category* is created. At initialization, the *Default*
848
/// *Category* is given "factory-supplied" default threshold levels. These
849
/// threshold levels, values distributed in the range '[0 .. 255]', are not
850
/// published and are subject to change over time. Explicit settings for these
851
/// factory values may be specified when the singleton is constructed. This is
852
/// done by constructing a 'ball::LoggerManagerConfiguration' object (needed for
853
/// logger manager construction in any event) and then calling the
854
/// 'setDefaultThresholdLevelsIfValid' method with the desired values for its four
855
/// arguments before supplying the configuration object to the logger manager
856
/// configuration.
857
///
858
/// The *Default* *Category* may arise during logging whenever the
859
/// 'setCategory(const char *categoryName)' method is called. That method returns
860
/// the address of the category having 'categoryName', if it exists; if no such
861
/// category exists, and a category having 'categoryName' cannot be created due to
862
/// a capacity limitation on the category registry maintained by the logger
863
/// manager singleton, then the *Default* *Category* is returned.
864
///
865
/// Categories that are added to the registry during logging through calls to the
866
/// 'setCategory(const char *)' method are given threshold levels by one of two
867
/// means. One alternative is to use the
868
/// 'ball::LoggerManagerConfiguration::DefaultThresholdLevelsCallback' functor
869
/// that is optionally supplied by the client when the logger manager singleton is
870
/// initialized. If such a functor *is* provided by the client, then it is used
871
/// to supply threshold levels to categories added by 'setCategory(const char *)'.
872
/// Otherwise, default threshold levels maintained by the logger manager for that
873
/// purpose are used. At initialization, these default threshold levels are given
874
/// the same "factory-supplied" settings as those for the *Default* *Category*
875
/// that, again, may be explicitly overridden at construction.
876
///
877
/// The default threshold levels can be adjusted ('setDefaultThresholdLevels') and
878
/// reset to their original values ('resetDefaultThresholdLevels'). Note that if
879
/// factory values are overridden at initialization, a reset will restore
880
/// thresholds to the user-specified default values. In addition, there is a
881
/// method to set the threshold levels of a given category to the current default
882
/// threshold levels ('setCategoryThresholdsToCurrentDefaults') or to the
883
/// factory-supplied (or client-overridden) default values
884
/// ('setCategoryThresholdsToFactoryDefaults').
885
///
886
/// One final note regarding categories is that the client can optionally supply a
887
/// 'ball::LoggerManagerConfiguration::CategoryNameFilterCallback' functor to
888
/// translate category names from an external to an internal representation. For
889
/// example, a project may allow programmers to refer to categories using
890
/// mixed-case within an application, but provide a 'toLower'
891
/// 'CategoryNameFilterCallback' to map all external upper-case letters to
892
/// lower-case internally. Such a name-filtering functor is set in the
893
/// 'ball::LoggerManagerConfiguration' object before that configuration object is
894
/// passed to the logger manager scoped guard constructor. In this scenario, the
895
/// (hypothetical) external category names "EQUITY.MARKET.NYSE" and
896
/// "equity.market.nyse" would be mapped to the same category internally by the
897
/// presumed 'toLower' functor.
898
///
899
/// ## Log Attributes
900
///
901
///The 'ball' logging framework provides the ability to associatiate "attributes"
902
///(name-value pairs) with the current logging context, which can be both be
903
///written to the log as part of a log record, as well as used in logging rules
904
///(see {Rule-Based Logging} below). This is typically done using the
905
///@ref ball_scopedattribute component.
906
///
907
///Below is a simple example using log attributes:
908
///
909
/// @code
910
/// int processData(const bsl::string& security,
911
/// const bsl::vector<char>& data)
912
/// {
913
/// ball::ScopedAttribute securityAttribute("mylibrary.security", security);
914
///
915
/// // ...
916
///
917
/// int rc = reticulateSplines(data);
918
///
919
/// return rc;
920
/// }
921
///
922
/// int reticulateSplines(data)
923
/// {
924
/// // ...
925
///
926
/// if (0 != rc) {
927
/// BALL_LOG_ERROR << "Error computing splines (" << rc << ")";
928
/// }
929
/// return rc;
930
/// }
931
/// @endcode
932
///
933
///In the above example a logging attribute, "mylibrary.security", is associated
934
///with the current thread's logging context for the lifetime of the
935
///'securityAttribute' object. As a result, if attributes have been
936
///enabled in the log format, the error log message generated by this example
937
///might look like:
938
///
939
/// @code
940
/// ERROR example.cpp:105 EXAMPLE.CATEGORY mylibrary.security="IBM US Equity" Error computing splines (-1)
941
/// @endcode
942
///
943
///Notice the attribute rendered does not appear in the logging message itself ("Error
944
///computing splines (-1)"), and is rendered as a name-value pair, meaning it can be
945
///easily parsed by log management systems like Humio or Splunk.
946
///
947
///Attributes will not appear in your log unless your ball::Observer format
948
///specification is configured to render attributes (see below).
949
///
950
/// ### Configuring an Observer to Output Attributes
951
///
952
///Log attributes are not rendered by default as part of the log message (for
953
///backward compatibility). Clients can enable log attributes to be rendered for
954
///observers that support log record formatting:
955
///
956
/// * 'ball::FileObserver'
957
/// * 'ball::AsyncFileObserver'
958
/// * 'ball::StreamObserver'
959
/// * 'ball::FileObserver2'
960
///
961
///Log message formatting is implemented by the @ref ball_recordstringformatter
962
///component, which supports the following (new) format specifiers to render log
963
///attributes:
964
///
965
/// @code
966
/// +------------------+--------------------------------------------------------------------------+
967
/// | Format Specifier | Description |
968
/// +==================+==========================================================================+
969
/// | %A | Log all the attributes of the record |
970
/// +------------------+--------------------------------------------------------------------------+
971
/// | %a | Log only those attributes not already logged by the "%a[name]" or |
972
/// | | "%av[name]" specifiers |
973
/// +------------------+--------------------------------------------------------------------------+
974
/// | %a[name] | Log an attribute with the specified 'name' as "name=value", |
975
/// | | log nothing if the attribute with the specified 'name' is not found |
976
/// +------------------+--------------------------------------------------------------------------+
977
/// | %av[name] | Log only the *value* of the attribute with the specified 'name', |
978
/// | | log nothing if the attribute with the specified 'name' is not found |
979
/// +------------------+--------------------------------------------------------------------------+
980
/// @endcode
981
///
982
///The following code snippet illustrates the creation and configuration commonly used
983
///by observers:
984
///
985
/// @code
986
/// int initFileObserver() {
987
/// // 'ball::FileObserver' uses an internal record string formatter and
988
/// // is configured by supplying a format string via 'setLogFormat().
989
///
990
/// bslma::Allocator *alloc = bslma::Default::globalAllocator(0);
991
///
992
/// bsl::shared_ptr<ball::FileObserver> observer =
993
/// bsl::allocate_shared<ball::FileObserver>(alloc);
994
///
995
///
996
/// // Set the log format for file and console logs to "\n%d %p:%t %s %f:%l %c %a %m\n"
997
/// observer->setLogFormat(ball::RecordStringFormatter::k_BASIC_ATTRIBUTE_FORMAT,
998
/// ball::RecordStringFormatter::k_BASIC_ATTRIBUTE_FORMAT);
999
///
1000
/// if (0 != observer->enableFileLogging("myapplication.log.%T")) {
1001
/// bsl::cout << "Failed to enable logging" << bsl::endl;
1002
/// return -1;
1003
/// }
1004
/// ball::LoggerManager::singleton().registerObserver(observer, "default");
1005
/// return 0;
1006
/// }
1007
/// @endcode
1008
///
1009
/// ### Suggested Log Record Format
1010
///
1011
///The logger now provides a new constant,
1012
///'ball::RecordStringFormatter::k_BASIC_ATTRIBUTE_FORMAT', which is the
1013
///recommended log format specification for most users (see the example above).
1014
///The default log format specification remains the same to avoid changing
1015
///behavior for existing applications. Note that the default format
1016
///specification might be changed in a subsequent release of BDE.
1017
///
1018
///
1019
/// ### Details on Using Attributes
1020
///
1021
///
1022
///The 'ball' library provides the following classes that implement ready-to-use
1023
///application solutions for log attributes:
1024
///
1025
/// * @ref ball_attribute : This class represents an attribute that consists of a
1026
/// (literal) name (held but not owned), and an associated value (owned). The
1027
/// value might be a number or string (see @ref ball_attribute for more detail).
1028
///
1029
/// * @ref ball_scopedattribute : Scoped guard that creates an attribute container
1030
/// with a single attribute supplied at construction and adds this container to
1031
/// the current thread's context. The container is removed from the thread
1032
/// context when the guard leaves the scope.
1033
///
1034
/// * @ref ball_recordformatter : Implements a log record formatting functor.
1035
/// Formats log records into human-readable log messages according to the
1036
/// specified format specs.
1037
///
1038
///Note that 'ball::ScopedAttribute' is recommended for most applications.
1039
///However, some low-level performance-critical systems (e.g., BAS) may implement
1040
///custom attribute-collection mechanisms. This adds considerable complexity, but
1041
///can deliver a small benefit in performance by taking advantage of compile time
1042
///knowledge of the attributes being collected. See
1043
///@ref ball_attributecollectorregistry for more details on attribute collection
1044
///mechanism.
1045
///
1046
/// ### Attribute Naming Recommendations
1047
///
1048
///Use the following naming conventions for attribute names:
1049
///
1050
/// * An attribute name should start with an alphabetic character; no other
1051
/// special characters or digits should be allowed as the first character of an
1052
/// attribute name.
1053
/// * An attribute name should not contain whitespaces.
1054
/// * An attribute name should contain only alphanumeric characters,
1055
/// underscores('_'), and dots('.'). Do not use any other special characters.
1056
///
1057
///It is highly recommended to use "namespaces" when naming attributes to avoid
1058
///attribute name collisions. For example, consider these attribute names:
1059
///
1060
/// @code
1061
/// +--------------------------+----------------------------+
1062
/// | "context id" | BAD (contains whitespace) |
1063
/// +--------------------------+----------------------------+
1064
/// | "contextid" | BAD (no namespace) |
1065
/// +--------------------------+----------------------------+
1066
/// | "bde.contextid" | GOOD |
1067
/// +--------------------------+----------------------------+
1068
/// | "bde.logger.context_id" | GOOD |
1069
/// +--------------------------+----------------------------+
1070
/// @endcode
1071
///
1072
///Handling of attributes with identical names is dictated by the underlying
1073
///container(s) that store the attributes and the 'ball' library cannot guarantee any
1074
///deterministic behavior in this case. It is the responsibility of the users to
1075
///guarantee uniqueness of attribute names.
1076
///
1077
///Log record attributes are rendered as space-separated 'name="value"' pairs
1078
///(for example: 'mylibrary.requestType="POST"'). Note that attribute names are
1079
///*not* quoted, whereas attribute values, if they are strings, are
1080
///*always* quoted.
1081
///
1082
/// ## Rule-Based Logging
1083
///
1084
///The 'ball' logging toolkit provides a set of components that collectively
1085
///allow clients to define rules to modify logging severity levels depending
1086
///on attributes associated with the current thread. The canonical example of
1087
///rule-based logging is to enable verbose logging when processing requests
1088
///for a particular user in a service.
1089
///
1090
///A usage example can be found in
1091
///{'Advanced Features Example 1: Rule-Based Logging'}.
1092
///
1093
///To enable rule-based logging clients must do two things:
1094
/// 1. Configure a set of (process-wide) rules to set logging thresholds based on
1095
/// 'ball::Attribute' values.
1096
///
1097
/// 2. Associate (on a per-thread basis) 'ball::Attribute' values with the
1098
/// current thread.
1099
///
1100
///The configuration of process-wide rules is accomplished by creating rules
1101
///(see {@ref ball_rule }) that set a logging category's thresholds if a set of
1102
///attributes associated with the current thread matches those of the rule (see
1103
///{@ref ball_managedattribute }). For example, a 'ball::Rule' might express: set
1104
///the logging threshold for any logging category starting with the prefix
1105
///"DATABASE" to 'e_TRACE' if the "myLib.userid" is 123456 and the
1106
///"myLib.requesttype" is "trade" (where "myLib.userid" and "myLib.requesttype"
1107
///are names of attributes that will be associated with a processing thread).
1108
///Clients can then call 'ball::LoggerManager::addRule' to add this rule to the
1109
///logger manager.
1110
///
1111
///To associate an attribute (like "myLib.userid" and "myLib.requesttype") with a
1112
///particular thread, clients must add a 'ball::AttributeContainer' holding the
1113
///attribute to the 'ball::AttributeContext' (note that an attribute context is a
1114
///thread-local object). The simplest means for doing this is to create a
1115
///'ball::ScopedAttribute' object as a local variable, which adds an attribute
1116
///container (holding a single attribute) that exists for the lifetime of the
1117
///scoped-attribute object. A 'ball::ScopedAttribute' appends a container having
1118
///a single attribute to a list of containers, which is easy and efficient if
1119
///there are only a small number of attributes. Clients with a large number of
1120
///attributes could use a 'ball::DefaultAttributeContainer', which provides an
1121
///efficient container for a large number of attributes. However, clients *must*
1122
///take care to remove the container from the 'ball::AttributeContext' before the
1123
///object goes out of scope and is destroyed. See @ref ball_scopedattributes
1124
///(plural) for an example. Finally, clients may also provide their own
1125
///implementations of 'ball::AttributeContainer' customized for their needs (e.g.,
1126
///by creating a simple, efficient 'ball::AttributeContainer' specifically for the
1127
///"myLib.userid" and "myLib.requesttype" attributes); see
1128
///@ref ball_attributecontainer for an example.
1129
///
1130
///{'Advanced Features Example 1: Rule-Based Logging'} below demonstrates creating
1131
///a simple logging rule, and associating attributes with a processing thread.
1132
///
1133
/// ## A Note on Multi-Threaded Logging Usage
1134
///
1135
/// The 'ball' logging toolkit may be used in single-threaded and multi-threaded
1136
/// library code and applications with equal ease, and with virtually no
1137
/// difference in coding. In particular, the same call to the logger manager
1138
/// scoped guard constructor is required in 'main' in both cases, and individual
1139
/// calls to the 'ball::Logger' instance method 'logMessage' (and logging calls
1140
/// via the logging macros -- see 'ball_log' and the {Appendix: Macro Reference}
1141
/// below) are identical from the user's perspective. Category threshold
1142
/// administration is also identical in both cases, since that is done by the
1143
/// singleton logger manager.
1144
///
1145
/// Differences in logger usage, or, more precisely, additional options for the
1146
/// multi-threaded user, arise when the user wishes to allocate one or more
1147
/// loggers beyond the default logger that is owned by the singleton logger
1148
/// manager. If a user does *not* explicitly allocate a logger (via the logger
1149
/// manager instance method 'allocateLogger') and install that logger for a given
1150
/// thread (via the manager instance method 'setLogger'), then all records from
1151
/// all threads in a program will be logged to the one default logger. However,
1152
/// since each thread of execution may have its own logger instance,
1153
/// multi-threaded users may choose to allocate and install multiple loggers.
1154
/// Note that each thread may have at most one logger, but a single logger may be
1155
/// used by any number of threads.
1156
///
1157
/// Multi-threaded users of logging may prefer to allocate and install one logger
1158
/// per thread in order to take advantage of the "trace-back" feature described
1159
/// above on a per-thread basis. In the event of an "error condition" (as defined
1160
/// by the *user*), such a logging configuration can provide a trace-back through
1161
/// the record buffer of the thread that caused the error, without any dilution
1162
/// from records logged from other threads. Conversely, if several threads are
1163
/// known to interact closely, it may be advantageous to have them share a common
1164
/// logger so that the trace-back log *does* include relevant records from all
1165
/// designated threads, in reverse chronological order.
1166
///
1167
/// ## Managing the ball::LoggerManager Singleton
1168
///
1169
/// The recommended way to initialize the logger manager singleton is to create a
1170
/// 'ball::LoggerManagerScopedGuard' object in 'main' (*before* creating any
1171
/// threads). The logger manager scoped guard constructor takes a configuration
1172
/// object (an instance of 'ball::LoggerManagerConfiguration') and an optional
1173
/// allocator. The logger manager singleton is created as a side-effect of
1174
/// creating the scoped guard object. When the guard object goes out of scope
1175
/// (i.e., on program exit), the logger manager singleton is automatically
1176
/// destroyed.
1177
///
1178
/// As an alternative to using the logger manager scoped guard, the 'static'
1179
/// 'ball::LoggerManager::initSingleton' method that takes the same arguments as
1180
/// the scoped guard constructor may be used to initialize the singleton.
1181
/// However, in this case the 'ball::LoggerManager::shutDownSingleton' method must
1182
/// be explicitly called to destroy the logger manager singleton on program exit.
1183
/// Unless 'shutDownSingleton' is called, the singleton will not be destroyed and
1184
/// resources used by the singleton will leak.
1185
///
1186
/// Direct use of the 'public' logger manager constructor to initialize the logger
1187
/// manager singleton is *deprecated*. The constructor will be declared 'private'
1188
/// in a future release.
1189
///
1190
/// Direct use of any of the 16 'ball::LoggerManager::initSingleton' methods that
1191
/// do *not* take an instance of 'ball::LoggerManagerConfiguration' to initialize
1192
/// the logger manager singleton is *deprecated*. These methods will be
1193
/// eliminated in a future release.
1194
///
1195
/// ## Usage: Tutorial
1196
///
1197
/// This section provides a set of logging examples designed to introduce and
1198
/// illustrate basic concepts, features, and resulting operation. The first four
1199
/// examples are very simple "Hello world!" programs that show single-threaded
1200
/// usage. The next three examples illustrate multi-threaded usage. As simple as
1201
/// these examples are, they show sufficient functionality to use the 'ball'
1202
/// logger in a fair number of production applications. In particular, all of
1203
/// these examples use the macros from the 'ball_log' component to perform the
1204
/// actual logging. Methods from @ref ball_loggermanager are used for initialization
1205
/// and administration, but for most applications, anything that *can* be done
1206
/// using the logging macros *should* be done with the macros.
1207
///
1208
/// ### Tutorial Example 1: Hello World!
1209
///
1210
/// This "Hello world!" example illustrates the most basic use of logging within a
1211
/// single translation unit containing a 'main' function and essentially nothing
1212
/// else. Among the points of usage that we illustrate are:
1213
///
1214
/// * Instantiating a 'ball::LoggerManagerConfiguration' and passing it to the
1215
/// logger manager scoped guard constructor.
1216
///
1217
/// * Instantiating a 'ball::StreamObserver' and registering it with the logger
1218
/// manager singleton.
1219
///
1220
/// * Invoking the 'BALL_LOG_SET_CATEGORY' macro to set a category (named "main
1221
/// category" in this example); records logged from a scope where the effects
1222
/// of the macro are visible (and not superseded by a macro invocation in a
1223
/// nested scope) will be logged to this category.
1224
///
1225
/// * Using the 'BALL_LOG_INFO' macro to log a "Hello world!" message at 'e_INFO'
1226
/// severity; messages at 'e_INFO' severity are ignored by default.
1227
///
1228
/// * Depending upon a command-line argument, a message is logged at 'e_ERROR'
1229
/// severity that will be published as a "pass-through". If the command-line
1230
/// argument is absent, the program will succeed with no messages published.
1231
/// This is the intended behavior.
1232
///
1233
/// Note that, when logging using the macros, the 'BALL_LOG_SET_CATEGORY' macro is
1234
/// not only extremely convenient, it is typically required, since it in turn
1235
/// defines symbols that the logging macros expect to see. Also note that
1236
/// 'BALL_LOG_SET_CATEGORY' must be used at function scope, and can be used only
1237
/// once within a scope, although it can be nested in inner scopes.
1238
///
1239
/// The entire program file is as follows.
1240
/// @code
1241
/// // logging.m.cpp -*-C++-*-
1242
///
1243
/// #include <ball_log.h>
1244
/// #include <ball_loggermanager.h>
1245
/// #include <ball_loggermanagerconfiguration.h>
1246
/// #include <ball_streamobserver.h>
1247
///
1248
/// #include <bslma_allocator.h>
1249
/// #include <bslma_default.h>
1250
///
1251
/// #include <bsl_iostream.h>
1252
/// #include <bsl_memory.h>
1253
///
1254
/// using namespace BloombergLP;
1255
///
1256
/// int main(int argc, char *argv[])
1257
/// {
1258
/// int verbose = argc > 1;
1259
/// // Enable command-line control of program behavior.
1260
///
1261
/// bslma::Allocator *alloc_p = bslma::Default::globalAllocator();
1262
/// // Get global allocator.
1263
///
1264
/// ball::LoggerManagerConfiguration configuration;
1265
/// configuration.setDefaultThresholdLevelsIfValid(ball::Severity::e_WARN);
1266
/// // Configure the minimum threshold at which records are published to
1267
/// // the observer to 'e_WARN'.
1268
///
1269
/// ball::LoggerManagerScopedGuard scopedGuard(configuration);
1270
/// // Instantiate the logger manager singleton.
1271
///
1272
/// ball::LoggerManager& manager = ball::LoggerManager::singleton();
1273
///
1274
/// bsl::shared_ptr<ball::StreamObserver> observer(
1275
/// new(*alloc_p) ball::StreamObserver(&bsl::cout),
1276
/// alloc_p);
1277
/// // Create simple observer; writes to 'stdout'.
1278
///
1279
/// manager.registerObserver(observer, "default");
1280
/// // Register the observer under (arbitrary) name "default".
1281
///
1282
/// BALL_LOG_SET_CATEGORY("main category");
1283
/// // Set a category -- an arbitrary name.
1284
///
1285
/// BALL_LOG_INFO << "Hello world!";
1286
/// // With default settings, this line has no effect.
1287
///
1288
/// if (verbose) { // 'if' to allow command-line activation
1289
/// BALL_LOG_ERROR << "Good-bye world!";
1290
/// // Log 'message' at 'ball::Severity::e_ERROR' severity level.
1291
/// }
1292
/// else {
1293
/// bsl::cout << "This program should produce no other output.\n";
1294
/// // By default, 'e_INFO' is ignored; only 'e_ERROR' and above are
1295
/// // published.
1296
/// }
1297
///
1298
/// return 0;
1299
/// }
1300
/// @endcode
1301
/// If the above program is run with no command-line arguments, the output will
1302
/// appear on 'stdout' as:
1303
/// @code
1304
/// This program should produce no other output.
1305
/// @endcode
1306
/// This case illustrates the default behavior, namely that "extraordinary"
1307
/// severity events (i.e., 'e_ERROR' and 'e_FATAL') are published immediately, but
1308
/// other events (such as 'e_INFO', 'e_DEBUG', and 'e_TRACE') are ignored. The
1309
/// user can easily modify this default behavior; subsequent examples will address
1310
/// these alternate usage modes.
1311
///
1312
/// If, however, any argument at all is provided to the executable on the command
1313
/// line, the output to 'stdout' will appear something like the following. (The
1314
/// spacing is changed to aid readability.)
1315
/// @code
1316
/// 27SEP2007_13:26:46.322 6152 1 ERROR logging.m.cpp 33 main category
1317
/// Good-bye world!
1318
/// @endcode
1319
/// Note that any number of observers can be registered with a logger manager.
1320
/// Also note that concrete observers that can be configured after their creation
1321
/// (e.g., as to whether log records are published in UTC or local time)
1322
/// generally can have their configuration adjusted at any time, either before
1323
/// or after being registered. For an example of such an observer, see
1324
/// @ref ball_asyncfileobserver .
1325
///
1326
/// ### Tutorial Example 2: Hello World! With Modified Defaults
1327
///
1328
/// This "Hello world!" example illustrates how to enable "recording" log records
1329
/// to the in-memory record buffer, and also how to change the severity level at
1330
/// which records are published as "pass-through". The code is identical to
1331
/// {Example 1}, except that attributes of 'ball::LoggerManagerConfiguration' are
1332
/// changed from their defaults (in a single line, in this case).
1333
///
1334
/// The 'setDefaultThresholdLevelsIfValid' method of
1335
/// 'ball::LoggerManagerConfiguration' that we will use to change the default
1336
/// configuration takes four arguments, each in the range '[0 .. 255]'. The
1337
/// method returns a status, and will fail (with no effect) if any argument is not
1338
/// a valid threshold level. Note that any 'ball::Severity::Level' enumerator
1339
/// value is valid, and it is good practice to limit arguments to one of those
1340
/// enumerators. The one line that effects our change is:
1341
/// @code
1342
/// configuration.setDefaultThresholdLevelsIfValid(
1343
/// ball::Severity::e_TRACE, // record level
1344
/// ball::Severity::e_WARN, // pass-through level
1345
/// ball::Severity::e_ERROR, // trigger level
1346
/// ball::Severity::e_FATAL); // trigger-all level
1347
/// @endcode
1348
/// This method call sets the four severity threshold levels as indicated and has
1349
/// the following effect on logging behavior.
1350
///
1351
/// 1. Any record logged with a severity that is at least that of 'e_TRACE' is
1352
/// written to the record buffer, but not necessarily published.
1353
///
1354
/// 2. Any record logged with a severity that is at least that of 'e_WARN' is
1355
/// published immediately as a pass-through record *and* is recorded in the
1356
/// record buffer. Note that, should the record buffer be published before
1357
/// such a record is overwritten, the record will be published a second time,
1358
/// avoiding "holes" in the trace-back if and when the buffer is published.
1359
///
1360
/// 3. Any record logged with a severity that is at least that of 'e_ERROR' is
1361
/// published immediately as a pass-through record *and* is recorded in the
1362
/// record buffer, and *then* triggers the publication of the record buffer.
1363
/// This behavior, among other things, guarantees that the 'e_ERROR' record
1364
/// will be published twice. Although this might seem like a bug, it is the
1365
/// intended behavior. Each published record is sent to the observer with a
1366
/// publication cause (a 'ball::Transmission::Cause' value) that the observer
1367
/// is free to use in filtering and re-routing published records. Future
1368
/// examples will illustrate various filtering techniques.
1369
///
1370
/// 4. Any record logged with a severity that is at least that of 'e_FATAL' will
1371
/// behave as for 'e_ERROR' above, except that the record buffer from *every*
1372
/// active logger registered with the logger manager will be published. See
1373
/// the multi-threaded examples below for more information about multiple
1374
/// loggers.
1375
///
1376
/// With the one extra line of code as described above, plus the explicit
1377
/// '#include' directive of 'ball_severity.h' (since we use symbols from that
1378
/// header explicitly), the code is otherwise exactly as in {Example 1} above.
1379
/// @code
1380
/// // logging.m.cpp -*-C++-*-
1381
///
1382
/// #include <ball_log.h>
1383
/// #include <ball_loggermanager.h>
1384
/// #include <ball_loggermanagerconfiguration.h>
1385
/// #include <ball_severity.h>
1386
/// #include <ball_streamobserver.h>
1387
///
1388
/// #include <bslma_allocator.h>
1389
/// #include <bslma_default.h>
1390
///
1391
/// #include <bsl_iostream.h>
1392
/// #include <bsl_memory.h>
1393
///
1394
/// using namespace BloombergLP;
1395
///
1396
/// int main(int argc, char *argv[])
1397
/// {
1398
/// int verbose = argc > 1;
1399
/// // Enable command-line control of program behavior.
1400
///
1401
/// bslma::Allocator *alloc_p = bslma::Default::globalAllocator();
1402
/// // Get global allocator.
1403
///
1404
/// ball::LoggerManagerConfiguration configuration;
1405
/// // Instantiate the default configuration.
1406
///
1407
/// configuration.setDefaultThresholdLevelsIfValid(
1408
/// ball::Severity::e_TRACE, // record level
1409
/// ball::Severity::e_WARN, // pass-through level
1410
/// ball::Severity::e_ERROR, // trigger level
1411
/// ball::Severity::e_FATAL); // trigger-all level
1412
/// // Set the four severity threshold levels; note that this method can
1413
/// // fail, and therefore returns a status.
1414
///
1415
/// ball::LoggerManagerScopedGuard scopedGuard(configuration);
1416
/// // Instantiate the logger manager singleton.
1417
///
1418
/// ball::LoggerManager& manager = ball::LoggerManager::singleton();
1419
///
1420
/// bsl::shared_ptr<ball::StreamObserver> observer(
1421
/// new(*alloc_p) ball::StreamObserver(&bsl::cout),
1422
/// alloc_p);
1423
/// // Create simple observer; writes to 'stdout'.
1424
///
1425
/// manager.registerObserver(observer, "default");
1426
/// // Register the observer under (arbitrary) name "default".
1427
///
1428
/// BALL_LOG_SET_CATEGORY("main category");
1429
/// // Set a category -- an arbitrary name.
1430
///
1431
/// BALL_LOG_INFO << "Hello world!";
1432
/// // With default settings, this line has no effect.
1433
///
1434
/// if (verbose) { // 'if' to allow command-line activation
1435
/// BALL_LOG_ERROR << "Good-bye world!";
1436
/// // Log 'message' at 'ball::Severity::e_ERROR' severity level
1437
/// // *and* trigger the publication of the record buffer
1438
/// // "trace-back".
1439
/// }
1440
/// else {
1441
/// bsl::cout << "This program should produce no other output.\n";
1442
/// // 'e_INFO' messages are recorded but *not* automatically
1443
/// // published; an 'e_ERROR' or 'e_FATAL' message is needed to
1444
/// // trigger the publication of the record buffer.
1445
/// }
1446
///
1447
/// return 0;
1448
/// }
1449
/// @endcode
1450
/// Once again, if the program is run with no command-line arguments, the output
1451
/// will appear on 'stdout' as:
1452
/// @code
1453
/// This program should produce no other output.
1454
/// @endcode
1455
/// Now, however, if a command-line argument is provided, the output is a little
1456
/// more extensive. (The spacing is changed to aid readability, and the
1457
/// timestamps are altered for illustrative purposes to show the temporal sequence
1458
/// more clearly).
1459
/// @code
1460
/// 27SEP2007_13:29:18.147 105 1 ERROR logging.m.cpp 42 main category
1461
/// Good-bye world!
1462
///
1463
/// 27SEP2007_13:29:18.147 105 1 ERROR logging.m.cpp 42 main category
1464
/// Good-bye world!
1465
///
1466
/// 27SEP2007_13:29:18.147 105 1 INFO logging.m.cpp 38 main category
1467
/// Hello world!
1468
/// @endcode
1469
/// Notice that the 'e_ERROR' message appears twice. This is not a bug but, as
1470
/// mentioned above, an intended consequence of the settings. As described above,
1471
/// the sequence of events is as follows: (1) The 'e_INFO' message is written to
1472
/// an in-memory buffer but *not* published (because 'e_INFO' is above the
1473
/// "Record" threshold but below the "Pass-Through", "Trigger", and "Trigger-All"
1474
/// thresholds). (2) The (one) 'e_ERROR' message is *first* published (a
1475
/// relatively quick operation insuring that the error indication is seen) because
1476
/// 'e_ERROR' is above the "Pass-Through" threshold, and *then* is written to the
1477
/// in-memory buffer, because 'e_ERROR' is above the "Record" threshold. Then,
1478
/// the entire contents of the in-memory buffer is published, in "Last In, First
1479
/// Out" (LIFO) order, because 'e_ERROR' is at the "Trigger" threshold.
1480
///
1481
/// ### Tutorial Example 3: Hello World! With Manual publish
1482
///
1483
/// This "Hello world!" example alters {Example 2} in one aspect. The conditional
1484
/// 'e_ERROR' message is removed, as is the use of command-line arguments, since
1485
/// they are not needed to illustrate our point. Instead, the user calls the
1486
/// 'publish' method of the logger instance "manually" before exiting, to force
1487
/// the publication of all unpublished messages in the logger's buffer. Note that
1488
/// this is not "default" behavior, but is accomplished simply enough. The line:
1489
/// @code
1490
/// manager.getLogger().publish();
1491
/// @endcode
1492
/// uses the singleton logger manager instance to obtain the default logger
1493
/// instance, and with that logger instance invokes the 'publish' method. Note
1494
/// that the logger's "manual" 'publish' method should not be confused with the
1495
/// 'ball::Observer' 'publish' method that is invoked by all "manual" and
1496
/// "automatic" logger "publishing" operations. Also note that should the
1497
/// 'manager' instance for some reason not be visible, the class method
1498
/// 'ball::LoggerManager::singleton()' can be used to obtain a reference to that
1499
/// instance. The full program file appears as follows.
1500
/// @code
1501
/// // logging.m.cpp -*-C++-*-
1502
///
1503
/// #include <ball_log.h>
1504
/// #include <ball_loggermanager.h>
1505
/// #include <ball_loggermanagerconfiguration.h>
1506
/// #include <ball_severity.h>
1507
/// #include <ball_streamobserver.h>
1508
///
1509
/// #include <bslma_allocator.h>
1510
/// #include <bslma_default.h>
1511
///
1512
/// #include <bsl_iostream.h>
1513
/// #include <bsl_memory.h>
1514
///
1515
/// using namespace BloombergLP;
1516
///
1517
/// int main()
1518
/// {
1519
/// bslma::Allocator *alloc_p = bslma::Default::globalAllocator();
1520
/// // Get global allocator.
1521
///
1522
/// ball::LoggerManagerConfiguration configuration;
1523
/// // Create default configuration.
1524
///
1525
/// configuration.setDefaultThresholdLevelsIfValid(
1526
/// ball::Severity::e_TRACE, // record level
1527
/// ball::Severity::e_WARN, // pass-through level
1528
/// ball::Severity::e_ERROR, // trigger level
1529
/// ball::Severity::e_FATAL); // trigger-all level
1530
/// // Set the four severity threshold levels; note that this method can
1531
/// // fail, and therefore returns a status.
1532
///
1533
/// ball::LoggerManagerScopedGuard scopedGuard(configuration);
1534
/// // Instantiate the logger manager singleton.
1535
///
1536
/// ball::LoggerManager& manager = ball::LoggerManager::singleton();
1537
///
1538
/// bsl::shared_ptr<ball::StreamObserver> observer(
1539
/// new(*alloc_p) ball::StreamObserver(&bsl::cout),
1540
/// alloc_p);
1541
/// // Create simple observer; writes to 'stdout'.
1542
///
1543
/// manager.registerObserver(observer, "default");
1544
/// // Register the observer under (arbitrary) name "default".
1545
///
1546
/// BALL_LOG_SET_CATEGORY("main category");
1547
/// // Set a category -- an arbitrary name.
1548
///
1549
/// BALL_LOG_INFO << "Hello world!";
1550
///
1551
/// BALL_LOG_INFO << "Hello again, world!";
1552
///
1553
/// bsl::cout << "We're almost ready to exit." " "
1554
/// "Let's publish the buffer first:" "\n";
1555
///
1556
/// manager.getLogger().publish();
1557
/// // This chain of calls insures that all messages that are still held
1558
/// // in the logger's in-memory buffer are published before the program
1559
/// // terminates.
1560
///
1561
/// return 0;
1562
/// }
1563
/// @endcode
1564
/// This program produces the following output on 'stdout', altered here again in
1565
/// spacing format and timestamp value for ease of reading.
1566
/// @code
1567
/// We're almost ready to exit. Let's publish the buffer first:
1568
///
1569
/// 27SEP2007_13:32:17.778 13702 1 INFO logging.m.cpp 37 main category
1570
/// Hello again, world!
1571
///
1572
/// 27SEP2007_13:32:17.778 13702 1 INFO logging.m.cpp 35 main category
1573
/// Hello world!
1574
/// @endcode
1575
/// Note again that the output is published in LIFO order.
1576
///
1577
/// ### Tutorial Example 4: Hello World! in Three Files
1578
///
1579
/// This example adds a few new aspects to the scenario of {Example 1} and
1580
/// {Example 2}. First of all, in addition to the file defining 'main', we
1581
/// introduce a separate translation unit 'f_log.cpp' that defines a function
1582
/// 'logThisInfoMsg'. The 'f_log.cpp' file defines its own category, "function
1583
/// category", that is distinct from the "main category" defined in 'main'. When
1584
/// 'logThisInfoMsg' is invoked, the resulting message is logged to "function
1585
/// category". Note, however, that when the now-familiar logging macros are used
1586
/// from 'main' directly, those messages are logged to "main category".
1587
///
1588
/// In addition to the above, we also show another way to alter the default
1589
/// thresholds. In this case, we choose values so that all messages logged with a
1590
/// severity at least that of 'e_INFO' are passed through immediately to the
1591
/// observer (in this case, to 'stdout'). In this example, we make the change by
1592
/// calling the 'setDefaultThresholdLevels' method of the logger manager:
1593
/// @code
1594
/// manager.setDefaultThresholdLevels(
1595
/// ball::Severity::e_TRACE, // sets "Record" threshold
1596
/// ball::Severity::e_INFO, // sets "Pass-Through" threshold
1597
/// ball::Severity::e_ERROR, // sets "Trigger" threshold
1598
/// ball::Severity::e_FATAL); // sets "Trigger-All" threshold
1599
/// @endcode
1600
/// The above call has an effect that differs subtly from the configuration
1601
/// object's 'setDefaultThresholdLevelsIfValid' method. The latter sets the
1602
/// permanent "factory defaults", as well as the "instantaneous defaults" that may
1603
/// be altered as often as desired using the above call. If, after the above call
1604
/// is made, the 'setCategoryThresholdsToFactoryDefaults' method is called, the
1605
/// effects of 'setDefaultThresholdLevels' are erased. This distinction is
1606
/// important for more advanced administration, but is not central to this
1607
/// example; we offer the alternative to illustrate logger functionality.
1608
///
1609
/// Note that it is both unnecessary and a *fatal* *error* for 'f_log' (or *any*
1610
/// library code) to instantiate a logger manager. The logger manager is a
1611
/// singleton, and should be constructed using the logger manager scoped guard
1612
/// ('ball::LoggerManagerScopedGuard'). In any sound design, this should be done
1613
/// in 'main' before any other threads have been created.
1614
///
1615
/// The main 'logging.m.cpp' file, plus 'f_log.cpp' and its associated 'f_log.h'
1616
/// file, are shown below. First the 'f_log.h' header file:
1617
/// @code
1618
/// // f_log.h -*-C++-*-
1619
/// #ifndef INCLUDED_F_LOG
1620
/// #define INCLUDED_F_LOG
1621
///
1622
/// namespace BloombergLP {
1623
///
1624
/// void logThisInfoMsg(const char *message);
1625
/// // Log the specified 'message' at the 'ball::Severity::e_INFO' severity
1626
/// // level. The category to which 'message' is logged is defined in
1627
/// // the implementation. Note that this function may affect the
1628
/// // operation of other logging operations that do not explicitly set
1629
/// // their own categories.
1630
///
1631
/// } // close enterprise namespace
1632
///
1633
/// #endif
1634
/// @endcode
1635
/// Then the 'f_log.cpp' implementation file:
1636
/// @code
1637
/// // f_log.cpp -*-C++-*-
1638
///
1639
/// #include <ball_log.h>
1640
///
1641
/// namespace BloombergLP {
1642
///
1643
/// void logThisInfoMsg(const char *message)
1644
/// {
1645
/// // Note that the uppercase symbols are macros defined in 'ball_log'.
1646
///
1647
/// BALL_LOG_SET_CATEGORY("function category");
1648
/// // Set a category -- arbitrary and may be changed.
1649
///
1650
/// BALL_LOG_INFO << message;
1651
/// // Log 'message' at 'ball::Severity::e_INFO' severity level.
1652
/// }
1653
///
1654
/// } // close enterprise namespace
1655
/// @endcode
1656
/// Finally, the 'logging.m.cpp' file that defines 'main':
1657
/// @code
1658
/// // logging.m.cpp -*-C++-*-
1659
///
1660
/// #include <ball_log.h>
1661
/// #include <ball_loggermanager.h>
1662
/// #include <ball_loggermanagerconfiguration.h>
1663
/// #include <ball_severity.h>
1664
/// #include <ball_streamobserver.h>
1665
///
1666
/// #include <bslma_allocator.h>
1667
/// #include <bslma_default.h>
1668
///
1669
/// #include <bsl_iostream.h>
1670
/// #include <bsl_memory.h>
1671
///
1672
/// #include <f_log.h>
1673
///
1674
/// using namespace BloombergLP;
1675
///
1676
/// int main(int argc, char *argv[])
1677
/// {
1678
/// int verbose = argc > 1;
1679
/// // Enable command-line control of program behavior.
1680
///
1681
/// bslma::Allocator *alloc_p = bslma::Default::globalAllocator();
1682
/// // Get global allocator.
1683
///
1684
/// ball::LoggerManagerConfiguration configuration;
1685
/// // Instantiate the default configuration.
1686
///
1687
/// ball::LoggerManagerScopedGuard scopedGuard(configuration);
1688
/// // Instantiate the logger manager singleton.
1689
///
1690
/// ball::LoggerManager& manager = ball::LoggerManager::singleton();
1691
///
1692
/// manager.setDefaultThresholdLevels(
1693
/// ball::Severity::e_TRACE, // sets "Record" threshold
1694
/// ball::Severity::e_INFO, // sets "Pass-Through" threshold
1695
/// ball::Severity::e_ERROR, // sets "Trigger" threshold
1696
/// ball::Severity::e_FATAL); // sets "Trigger-All" threshold
1697
///
1698
/// bsl::shared_ptr<ball::StreamObserver> observer(
1699
/// new(*alloc_p) ball::StreamObserver(&bsl::cout),
1700
/// alloc_p);
1701
/// // Create simple observer; writes to 'stdout'.
1702
///
1703
/// manager.registerObserver(observer, "default");
1704
/// // Register the observer under (arbitrary) name "default".
1705
///
1706
/// BALL_LOG_SET_CATEGORY("main category");
1707
/// // Set a category -- note that this will *not* affect the category
1708
/// // set in 'logThisInfoMsg'.
1709
///
1710
/// BALL_LOG_INFO << "Called directly from 'main'";
1711
/// // Logged to "main category".
1712
///
1713
/// if (verbose) { // 'if' to allow command-line activation
1714
/// for (int i = 0; i < 3; ++i) {
1715
/// logThisInfoMsg("Hello world!");
1716
/// bsl::cout << "Watch the loop execute: i = " << i << bsl::endl;
1717
/// // proves Msg is published as "pass-through"
1718
/// }
1719
/// }
1720
///
1721
/// BALL_LOG_INFO << "Called again directly from 'main'";
1722
/// // Logged to "main category".
1723
///
1724
/// return 0;
1725
/// }
1726
/// @endcode
1727
/// Building these two files and running the executable with no additional
1728
/// command-line arguments produces output from the two direct logging calls only.
1729
/// Once again, the format and timestamps are altered for readability.
1730
/// @code
1731
/// 27SEP2007_13:35:33.356 9427 1 INFO logging.m.cpp 40 main category
1732
/// Called directly from 'main'
1733
///
1734
/// 27SEP2007_13:35:33.357 9427 1 INFO logging.m.cpp 51 main category
1735
/// Called again directly from 'main'
1736
/// @endcode
1737
/// Note that this sequence is not in LIFO order since this output is not from the
1738
/// message buffer, but rather is direct "Pass-Through" output.
1739
///
1740
/// If we provide a command-line argument that will cause the 'for' loop to
1741
/// execute, we then get the following output (with formats and timestamps altered
1742
/// as is now familiar in these examples).
1743
/// @code
1744
/// 27SEP2007_13:39:07.150 8249 1 INFO logging.m.cpp 40 main category
1745
/// Called directly from 'main'
1746
///
1747
/// 27SEP2007_13:39:07.151 8249 1 INFO f_log.cpp 16 function category
1748
/// Hello world!
1749
/// Watch the loop execute: i = 0
1750
///
1751
/// 27SEP2007_13:39:07.151 8249 1 INFO f_log.cpp 16 function category
1752
/// Hello world!
1753
/// Watch the loop execute: i = 1
1754
///
1755
/// 27SEP2007_13:39:07.152 8249 1 INFO f_log.cpp 16 function category
1756
/// Hello world!
1757
/// Watch the loop execute: i = 2
1758
///
1759
/// 27SEP2007_13:39:07.152 8249 1 INFO logging.m.cpp 51 main category
1760
/// Called again directly from 'main'
1761
/// @endcode
1762
///
1763
/// ### Tutorial Example 5: Logging in Two Threads
1764
///
1765
/// This example illustrates how to use logging in a multi-threaded application.
1766
/// Since we have already shown how logging works seamlessly across multiple
1767
/// translation units, we will implement everything that we need for this example
1768
/// in a single file named 'mtlogging1.cpp'.
1769
///
1770
/// We define two functions, 'f1' and 'f2', that log their 'message' text argument
1771
/// to function-defined categories, at 'e_INFO' and 'e_WARN' severities,
1772
/// respectively. We also define two 'extern' "C" thread functions,
1773
/// 'threadFunction1' and 'threadFunction2', that call 'f1' and 'f2',
1774
/// respectively, in a loop of three iterations. The thread functions also differ
1775
/// slightly in their calls to 'bslmt::ThreadUtil::sleep' within the loop. These
1776
/// 'sleep' calls, in conjunction with the logged timestamps, show the interleaved
1777
/// operation of the threads. Note that although the thread functions define
1778
/// their own categories, the logging macros in 'f1' and 'f2' use only the
1779
/// category defined within the scope of 'f1' and 'f2', respectively.
1780
///
1781
/// 'main' creates an observer that will write to 'stdout', initializes the logger
1782
/// manager singleton, sets a category, and sets the default threshold values,
1783
/// just as in the single-threaded examples above. Then 'main' uses 'bslmt' to
1784
/// spawn two threads running the above two thread functions. Each thread logs
1785
/// its messages, but only the second thread's log messages having 'e_WARN'
1786
/// severity will be published immediately as "Pass-Through" records. Finally,
1787
/// using the same 'verbose' mechanism (set by the presence or absence of a
1788
/// command-line argument) as the above examples, we conditionally log a message
1789
/// in 'main' at 'e_ERROR' severity. If this block of code executes, then the
1790
/// entire record buffer for the example (i.e., for both threads and 'main') will
1791
/// be published, in reverse chronological order.
1792
///
1793
/// The full code as described above is presented without interruption for ease of
1794
/// reading.
1795
/// @code
1796
/// // logging.m.cpp -*-C++-*-
1797
///
1798
/// #include <ball_log.h>
1799
/// #include <ball_loggermanager.h>
1800
/// #include <ball_loggermanagerconfiguration.h>
1801
/// #include <ball_severity.h>
1802
/// #include <ball_streamobserver.h>
1803
///
1804
/// #include <bslma_allocator.h>
1805
/// #include <bslma_default.h>
1806
///
1807
/// #include <bslmt_threadutil.h>
1808
///
1809
/// #include <bsls_timeinterval.h>
1810
///
1811
/// #include <bsl_iostream.h>
1812
/// #include <bsl_memory.h>
1813
///
1814
/// using namespace BloombergLP;
1815
///
1816
/// void f1(const char *message)
1817
/// // Log the specified 'message' to the "Function 1" category at 'e_INFO'
1818
/// // severity.
1819
/// {
1820
/// BALL_LOG_SET_CATEGORY("Function 1");
1821
/// BALL_LOG_INFO << message;
1822
/// }
1823
///
1824
/// void f2(const char *message)
1825
/// // Log the specified 'message' to the "Function 2" category at 'e_WARN'
1826
/// // severity.
1827
/// {
1828
/// BALL_LOG_SET_CATEGORY("Function 2");
1829
/// BALL_LOG_WARN << message;
1830
/// }
1831
///
1832
/// extern "C" void *threadFunction1(void *)
1833
/// // Log to the default logger a sequence of messages to the "Function 1"
1834
/// // category at 'e_INFO' severity.
1835
/// {
1836
/// char buf[10] = "Message n";
1837
/// bsls::TimeInterval waitTime(4.0);
1838
///
1839
/// for (int i = 0; i < 3; ++i) {
1840
/// buf[8] = '0' + i;
1841
/// f1(buf);
1842
/// bslmt::ThreadUtil::sleep(waitTime);
1843
/// }
1844
/// return 0;
1845
/// }
1846
///
1847
/// extern "C" void *threadFunction2(void *)
1848
/// // Log to the default logger a sequence of messages to the "Function 2"
1849
/// // category at 'e_WARN' severity.
1850
/// {
1851
/// char buf[10] = "Message n";
1852
/// bsls::TimeInterval waitTime(2.0);
1853
///
1854
/// for (int i = 0; i < 3; ++i) {
1855
/// buf[8] = '0' + i;
1856
/// bslmt::ThreadUtil::sleep(waitTime);
1857
/// f2(buf);
1858
/// }
1859
/// return 0;
1860
/// }
1861
///
1862
/// int main(int argc, char *argv[])
1863
/// {
1864
/// int verbose = argc > 1; // allows user to control output from command
1865
/// // line
1866
///
1867
/// bslma::Allocator *alloc_p = bslma::Default::globalAllocator();
1868
/// // Get global allocator.
1869
///
1870
/// ball::LoggerManagerConfiguration configuration;
1871
/// configuration.setDefaultThresholdLevelsIfValid(
1872
/// ball::Severity::e_TRACE, // "Record"
1873
/// ball::Severity::e_WARN, // "Pass-Through"
1874
/// ball::Severity::e_ERROR, // "Trigger"
1875
/// ball::Severity::e_FATAL); // "Trigger-All"
1876
///
1877
/// ball::LoggerManagerScopedGuard scopedGuard(configuration);
1878
/// // Instantiate the logger manager singleton.
1879
///
1880
/// ball::LoggerManager& manager = ball::LoggerManager::singleton();
1881
///
1882
/// bsl::shared_ptr<ball::StreamObserver> observer(
1883
/// new(*alloc_p) ball::StreamObserver(&bsl::cout),
1884
/// alloc_p);
1885
/// // Create simple observer; writes to 'stdout'.
1886
///
1887
/// manager.registerObserver(observer, "default");
1888
/// // Register the observer under (arbitrary) name "default".
1889
///
1890
/// BALL_LOG_SET_CATEGORY("main");
1891
///
1892
/// bslmt::ThreadAttributes attributes;
1893
/// bslmt::ThreadUtil::Handle handle1;
1894
/// bslmt::ThreadUtil::Handle handle2;
1895
///
1896
/// bslmt::ThreadUtil::create(&handle1, attributes, threadFunction1, 0);
1897
/// bslmt::ThreadUtil::create(&handle2, attributes, threadFunction2, 0);
1898
///
1899
/// bslmt::ThreadUtil::join(handle1);
1900
/// bslmt::ThreadUtil::join(handle2);
1901
///
1902
/// if (verbose) { // 'if' to allow command-line activation
1903
/// BALL_LOG_ERROR << "Force publication.";
1904
/// }
1905
/// return 0;
1906
/// }
1907
/// @endcode
1908
/// Running the above program with no extra command-line argument produces the
1909
/// output shown below. Note that only the 'e_WARN' records (from thread 3) are
1910
/// published, and that they are in chronological order.
1911
/// @code
1912
/// 27SEP2007_13:50:20.023 6625 3 WARN logging.m.cpp 28 Function 2 Message 0
1913
///
1914
/// 27SEP2007_13:50:22.033 6625 3 WARN logging.m.cpp 28 Function 2 Message 1
1915
///
1916
/// 27SEP2007_13:50:24.042 6625 3 WARN logging.m.cpp 28 Function 2 Message 2
1917
/// @endcode
1918
/// However, if we supply an additional command-line argument, the 'if' statement
1919
/// containing the 'BALL_LOG_ERROR' macro will execute. This causes the 'e_ERROR'
1920
/// record itself to be published (from thread 1, the 'main' thread, as a
1921
/// "pass-through"), and it also triggers the publication of the entire record
1922
/// buffer. Note how the records from threads 1, 2, and 3 are published in
1923
/// inverse chronological order. Note in particular how the 'e_INFO' and 'e_WARN
1924
/// records interleave irregularly because of the different 'sleep' statements in
1925
/// the above code.
1926
/// @code
1927
/// 27SEP2007_13:52:20.435 13809 3 WARN logging.m.cpp 28 Function 2 Message 0
1928
///
1929
/// 27SEP2007_13:52:22.445 13809 3 WARN logging.m.cpp 28 Function 2 Message 1
1930
///
1931
/// 27SEP2007_13:52:24.455 13809 3 WARN logging.m.cpp 28 Function 2 Message 2
1932
///
1933
/// 27SEP2007_13:52:30.456 13809 1 ERROR logging.m.cpp 89 main Force publication.
1934
///
1935
/// 27SEP2007_13:52:30.456 13809 1 ERROR logging.m.cpp 89 main Force publication.
1936
///
1937
/// 27SEP2007_13:52:26.446 13809 2 INFO logging.m.cpp 20 Function 1 Message 2
1938
///
1939
/// 27SEP2007_13:52:24.455 13809 3 WARN logging.m.cpp 28 Function 2 Message 2
1940
///
1941
/// 27SEP2007_13:52:22.445 13809 3 WARN logging.m.cpp 28 Function 2 Message 1
1942
///
1943
/// 27SEP2007_13:52:22.435 13809 2 INFO logging.m.cpp 20 Function 1 Message 1
1944
///
1945
/// 27SEP2007_13:52:20.435 13809 3 WARN logging.m.cpp 28 Function 2 Message 0
1946
///
1947
/// 27SEP2007_13:52:18.433 13809 2 INFO logging.m.cpp 20 Function 1 Message 0
1948
/// @endcode
1949
///
1950
/// ### Tutorial Example 6: Logging in Two Threads Using Two Loggers
1951
///
1952
/// This example is similar to {Example 5} above, with only a few "minor" changes.
1953
/// Nevertheless, as we use more of the logger's features, we need to become more
1954
/// familiar with the details of logger operation. In this example, we install a
1955
/// distinct logger instance (i.e., not the default logger) for one of the
1956
/// threads. (The other thread continues to use the default logger that is shared
1957
/// with 'main', to make clear that the "one-logger-per-thread" usage pattern
1958
/// applies on a thread-by-thread basis, and is not "all-or-nothing".) By
1959
/// choosing this more sophisticated usage, however, we need to address the
1960
/// question of logger resource management and lifetime -- a question that is
1961
/// completely avoided by using the default logger. As is often the case in
1962
/// programming, there is no one "right" answer to this resource question, and so
1963
/// we will discuss our choices and the alternatives in this example.
1964
///
1965
/// Installing a distinct logger instance in 'threadFunction1' is simple enough to
1966
/// do, but a question arises immediately: who should own the logger resources?
1967
/// Before we discuss this question, let's look at the specific solution that we
1968
/// have chosen to clarify the question of what resources a logger needs.
1969
///
1970
/// We've modified 'threadFunction1' to take a logger as an argument (passed as a
1971
/// 'void *') and to use the logger manager's 'setLogger' function to install the
1972
/// logger as the active logger for that thread. The code to do this in
1973
/// 'threadFunction1' is straightforward:
1974
/// @code
1975
/// extern "C" void *threadFunction1(void *logger)
1976
/// // ...
1977
/// {
1978
/// // ...
1979
/// ball::LoggerManager::singleton().setLogger((ball::Logger*)logger);
1980
/// // Install a local 'logger' for this thread.
1981
/// // ...
1982
/// }
1983
/// @endcode
1984
/// Clearly, in this solution, 'threadFunction1' does *not* own 'logger' or any of
1985
/// its resources. This *must* be the case, because the function has no way of
1986
/// knowing in how many other threads the passed-in 'logger' might be installed.
1987
/// Recall that any one logger may be installed in any number of threads.
1988
///
1989
/// This one-line addition to call the 'setLogger' method is, from the thread
1990
/// function's perspective, the only change needed if the thread is *not* to
1991
/// manage its own logger resources. But if 'threadFunction1' does not manage the
1992
/// logger resources, then 'main' must do so. Let's look at what 'main' needs to
1993
/// do so that we can be concrete about what resources are needed in the first
1994
/// place.
1995
///
1996
/// Several steps are needed to instantiate a 'ball::Logger' instance. The logger
1997
/// needs a (concrete) 'ball::RecordBuffer', and the preferred record buffer,
1998
/// 'ball::FixedSizeRecordBuffer', in turn needs a size for the record buffer
1999
/// ('LOGBUF_SIZE'). We also provide an explicit 'bslma::Allocator' to supply
2000
/// memory; the global allocator returned by 'bslma::Default::globalAllocator()'
2001
/// is sufficient here. Assuming that 32k bytes is a good size for the buffer,
2002
/// the following eight lines will appear in 'main'.
2003
/// @code
2004
/// enum { LOGBUF_SIZE = 32 * 1024 };
2005
/// bslma::Allocator *alloc_p = bslma::Default::globalAllocator();
2006
/// ball::FixedSizeRecordBuffer rb(LOGBUF_SIZE, alloc_p);
2007
/// ball::LoggerManager& manager = ball::LoggerManager::singleton();
2008
/// ball::Logger *logger = manager.allocateLogger(&rb);
2009
/// // ...
2010
/// manager.deallocateLogger(logger); // free resources
2011
/// @endcode
2012
/// The last line frees resources and is executed just before 'main' returns.
2013
///
2014
/// With this implementation choice, 'main' clearly owns 'logger's resources. We
2015
/// now have enough information to address the question of what the consequences
2016
/// of this decision are, and whether or not 'main' actually *should* own these
2017
/// resources.
2018
///
2019
/// At first glance, it might seem that the thread itself should own and manage
2020
/// everything it needs for its own logger. This is certainly a reasonable
2021
/// choice, and {Example 6} below shows what 'threadFunction1' would look like in
2022
/// that case, but there are two separate reasons why the owner of 'main' might
2023
/// prefer *not* to delegate those responsibilities to the thread function.
2024
///
2025
/// The first reason -- not relevant in this particular example -- is that 'main'
2026
/// may wish to install the *same* logger in several threads. In this case, no
2027
/// one thread can manage "its own" logger, because the logger is (or at least
2028
/// might be) shared. Sharing loggers among groups of threads is at least a
2029
/// reasonable design choice, and so it is useful to see how 'main' takes
2030
/// ownership of logger resources.
2031
///
2032
/// The second reason for wanting 'main' and not the thread function to own the
2033
/// logger's resources is illustrated -- albeit trivially -- in this example:
2034
/// 'main' can *potentially* log a 'e_FATAL' record (here the "Trigger-All"
2035
/// threshold) after all other threads have terminated. If the user wants the
2036
/// (now-terminated) thread's trace-back log to be published by the "Trigger-All",
2037
/// then 'main' must preserve the logger resources. Otherwise, if a thread were
2038
/// to own its own logger resources, then any "trace-back" records generated by
2039
/// that thread would necessarily be discarded (or, at best, unconditionally
2040
/// published) when the thread function returned.
2041
///
2042
/// In this example we also made a trivial change that has nothing to do with
2043
/// threading, but illustrates a useful feature of 'ball::StreamObserver', namely
2044
/// that an instance can write to a file as easily as to 'stdout'. The three
2045
/// lines in 'main':
2046
/// @code
2047
/// ball::LoggerManagerScopedGuard scopedGuard(configuration);
2048
/// // Instantiate the logger manager singleton.
2049
///
2050
/// bsl::ofstream outFile("outFile");
2051
/// bsl::shared_ptr<ball::StreamObserver> observer(
2052
/// new(*alloc_p) ball::StreamObserver(&outFile),
2053
/// alloc_p);
2054
/// // Create simple observer; writes to 'outFile".
2055
///
2056
/// ball::LoggerManager& manager = ball::LoggerManager::singleton();
2057
///
2058
/// manager.registerObserver(observer, "default");
2059
/// // Register the observer under (arbitrary) name "default".
2060
/// @endcode
2061
/// install a "file observer" that will write to "outFile", creating the file if
2062
/// it doesn't exist and overwriting it if it does. Clearly, this is just taking
2063
/// advantage of the standard C++ 'ofstream' class, but it is useful all the same.
2064
///
2065
/// The full code as described above is presented without interruption for ease of
2066
/// reading.
2067
/// @code
2068
/// // logging.m.cpp -*-C++-*-
2069
///
2070
/// #include <ball_fixedsizerecordbuffer.h>
2071
/// #include <ball_log.h>
2072
/// #include <ball_loggermanager.h>
2073
/// #include <ball_loggermanagerconfiguration.h>
2074
/// #include <ball_severity.h>
2075
/// #include <ball_streamobserver.h>
2076
///
2077
/// #include <bslma_allocator.h>
2078
/// #include <bslma_default.h>
2079
///
2080
/// #include <bslmt_threadutil.h>
2081
///
2082
/// #include <bsls_timeinterval.h>
2083
///
2084
/// #include <bsl_fstream.h>
2085
/// #include <bsl_memory.h>
2086
///
2087
/// using namespace BloombergLP;
2088
///
2089
/// void f1(const char *message)
2090
/// // Log the specified 'message' to the "Function 1" category at 'e_INFO'
2091
/// // severity.
2092
/// {
2093
/// BALL_LOG_SET_CATEGORY("Function 1");
2094
/// BALL_LOG_INFO << message;
2095
/// }
2096
///
2097
/// void f2(const char *message)
2098
/// // Log the specified 'message' to the "Function 2" category at 'e_WARN'
2099
/// // severity.
2100
/// {
2101
/// BALL_LOG_SET_CATEGORY("Function 2");
2102
/// BALL_LOG_WARN << message;
2103
/// }
2104
///
2105
/// extern "C" void *threadFunction1(void *logger)
2106
/// // Log to the specified 'logger' a sequence of messages to the "Function
2107
/// // 1" category at 'e_INFO' severity.
2108
/// {
2109
/// ball::LoggerManager::singleton().setLogger((ball::Logger *)logger);
2110
/// // Install a local logger for this thread.
2111
///
2112
/// char buf[10] = "Message n";
2113
/// bsls::TimeInterval waitTime(4.0);
2114
///
2115
/// for (int i = 0; i < 3; ++i) {
2116
/// buf[8] = '0' + i;
2117
/// f1(buf);
2118
/// bslmt::ThreadUtil::sleep(waitTime);
2119
/// }
2120
/// return 0;
2121
/// }
2122
///
2123
/// extern "C" void *threadFunction2(void *)
2124
/// // Log to the default logger a sequence of messages to the "Function 2"
2125
/// // category at 'e_WARN' severity.
2126
/// {
2127
/// char buf[10] = "Message n";
2128
/// bsls::TimeInterval waitTime(2.0);
2129
///
2130
/// for (int i = 0; i < 3; ++i) {
2131
/// buf[8] = '0' + i;
2132
/// bslmt::ThreadUtil::sleep(waitTime);
2133
/// f2(buf);
2134
/// }
2135
/// return 0;
2136
/// }
2137
///
2138
/// int main(int argc, char *argv[])
2139
/// {
2140
/// int verbose = argc > 1; // allows user to control output from command
2141
/// // line
2142
///
2143
/// bslma::Allocator *alloc_p = bslma::Default::globalAllocator();
2144
/// // Get global allocator.
2145
///
2146
/// ball::LoggerManagerConfiguration configuration;
2147
/// configuration.setDefaultThresholdLevelsIfValid(
2148
/// ball::Severity::e_TRACE, // "Record"
2149
/// ball::Severity::e_WARN, // "Pass-Through"
2150
/// ball::Severity::e_ERROR, // "Trigger"
2151
/// ball::Severity::e_FATAL); // "Trigger-All"
2152
///
2153
/// ball::LoggerManagerScopedGuard scopedGuard(configuration);
2154
/// // Instantiate the logger manager singleton.
2155
///
2156
/// ball::LoggerManager& manager = ball::LoggerManager::singleton();
2157
///
2158
/// bsl::ofstream outFile("outFile");
2159
/// bsl::shared_ptr<ball::StreamObserver> observer(
2160
/// new(*alloc_p) ball::StreamObserver(&outFile),
2161
/// alloc_p);
2162
/// // Create simple observer; writes to 'outFile".
2163
///
2164
/// manager.registerObserver(observer, "default");
2165
/// // Register the observer under (arbitrary) name "default".
2166
///
2167
/// BALL_LOG_SET_CATEGORY("main");
2168
///
2169
/// // The following lines prepare resources to allocate a 'logger' from the
2170
/// // logger manager. 'main' is responsible for managing resource
2171
/// // lifetimes.
2172
///
2173
/// enum { LOGBUF_SIZE = 32 * 1024 };
2174
/// ball::FixedSizeRecordBuffer rb(LOGBUF_SIZE, alloc_p);
2175
/// ball::Logger *logger = manager.allocateLogger(&rb);
2176
///
2177
/// bslmt::ThreadAttributes attributes;
2178
/// bslmt::ThreadUtil::Handle handle1;
2179
/// bslmt::ThreadUtil::Handle handle2;
2180
///
2181
/// // first thread gets 'logger'; second thread uses default logger
2182
/// bslmt::ThreadUtil::create(&handle1, attributes, threadFunction1, logger);
2183
/// bslmt::ThreadUtil::create(&handle2, attributes, threadFunction2, 0);
2184
///
2185
/// bslmt::ThreadUtil::join(handle1);
2186
/// bslmt::ThreadUtil::join(handle2);
2187
///
2188
/// if (verbose) { // 'if' to allow command-line activation
2189
/// BALL_LOG_FATAL << "Force publication.";
2190
/// }
2191
///
2192
/// manager.deallocateLogger(logger); // free resources
2193
/// return 0;
2194
/// }
2195
/// @endcode
2196
/// Running the above program with no extra command-line argument produces the
2197
/// output shown below that in this example is written to 'outFile'. Note that,
2198
/// except for the timestamps, process id, and filename, the output is the same
2199
/// as for {Example 5} above: only the 'e_WARN' records (from thread 3) are
2200
/// published and they are in chronological order.
2201
/// @code
2202
/// 27SEP2007_13:57:52.012 3648 3 WARN logging.m.cpp 29 Function 2 Message 0
2203
///
2204
/// 27SEP2007_13:57:54.022 3648 3 WARN logging.m.cpp 29 Function 2 Message 1
2205
///
2206
/// 27SEP2007_13:57:56.032 3648 3 WARN logging.m.cpp 29 Function 2 Message 2
2207
/// @endcode
2208
/// When we supply an additional command-line argument, causing the logging macro
2209
/// in 'main' (now 'BALL_LOG_FATAL') to execute, the *initial* output is similar
2210
/// to {Example 5}. Specifically, the three 'e_WARN' records are published in
2211
/// chronological order as "Pass-Through" records, as is the 'e_FATAL' record
2212
/// (that now replaces the 'e_ERROR' record). From here on, however, the sequence
2213
/// of events is a bit different.
2214
///
2215
/// Recall that in this example 'e_FATAL' is the "Trigger-All" threshold level.
2216
/// For a "Trigger-All", each record buffer is published in turn. *Within* a
2217
/// record buffer, records are published in reverse chronological order, but, as
2218
/// can clearly be seen from the timestamps, the sequence of records as a whole is
2219
/// not well ordered in time at all. Note, rather, that the 'e_FATAL' and
2220
/// 'e_WARN' records from the default logger are published in reverse
2221
/// chronological order, and *then* the 'e_INFO' records from the installed
2222
/// 'logger' are published in reverse chronological order.
2223
/// @code
2224
/// 27SEP2007_13:59:23.504 8707 3 WARN logging.m.cpp 29 Function 2 Message 0
2225
///
2226
/// 27SEP2007_13:59:25.514 8707 3 WARN logging.m.cpp 29 Function 2 Message 1
2227
///
2228
/// 27SEP2007_13:59:27.524 8707 3 WARN logging.m.cpp 29 Function 2 Message 2
2229
///
2230
/// 27SEP2007_13:59:33.524 8707 1 FATAL logging.m.cpp 103 main Force publication.
2231
///
2232
/// 27SEP2007_13:59:33.524 8707 1 FATAL logging.m.cpp 103 main Force publication.
2233
///
2234
/// 27SEP2007_13:59:27.524 8707 3 WARN logging.m.cpp 29 Function 2 Message 2
2235
///
2236
/// 27SEP2007_13:59:25.514 8707 3 WARN logging.m.cpp 29 Function 2 Message 1
2237
///
2238
/// 27SEP2007_13:59:23.504 8707 3 WARN logging.m.cpp 29 Function 2 Message 0
2239
///
2240
/// 27SEP2007_13:59:29.514 8707 2 INFO logging.m.cpp 21 Function 1 Message 2
2241
///
2242
/// 27SEP2007_13:59:25.504 8707 2 INFO logging.m.cpp 21 Function 1 Message 1
2243
///
2244
/// 27SEP2007_13:59:21.498 8707 2 INFO logging.m.cpp 21 Function 1 Message 0
2245
/// @endcode
2246
///
2247
/// ### Tutorial Example 7: A Thread that Owns its Own Logger Resources
2248
///
2249
/// This example is almost identical to {Example 6} above, except that the eight
2250
/// lines (as highlighted above) needed to manage resources are moved from 'main'
2251
/// to 'threadFunction1', so that the latter function no longer needs an argument.
2252
/// Therefore, we show only 'threadFunction1' and 'main' explicitly in this
2253
/// example. Refer to the discussion of {Example 6} for details.
2254
/// @code
2255
/// // ...
2256
///
2257
/// extern "C" void *threadFunction1(void *)
2258
/// // Log to an internally managed logger a sequence of messages to the
2259
/// // "Function 1" category at 'e_INFO' severity.
2260
/// {
2261
/// // The following lines prepare resources to allocate a 'logger' from the
2262
/// // logger manager. This thread is responsible for managing resource
2263
/// // lifetimes.
2264
///
2265
/// enum { LOGBUF_SIZE = 32 * 1024 };
2266
/// bslma::Allocator *alloc_p = bslma::Default::globalAllocator();
2267
/// ball::FixedSizeRecordBuffer rb(LOGBUF_SIZE, alloc_p);
2268
/// ball::LoggerManager& manager = ball::LoggerManager::singleton();
2269
/// ball::Logger *logger = manager.allocateLogger(&rb);
2270
///
2271
/// manager.setLogger((ball::Logger*)logger);
2272
/// // Install a local logger for this thread.
2273
///
2274
/// char buf[10] = "Message n";
2275
/// bsls::TimeInterval waitTime(4.0);
2276
///
2277
/// for (int i = 0; i < 3; ++i) {
2278
/// buf[8] = '0' + i;
2279
/// f1(buf);
2280
/// bslmt::ThreadUtil::sleep(waitTime);
2281
/// }
2282
///
2283
/// manager.deallocateLogger(logger); // free resources
2284
/// return 0;
2285
/// }
2286
///
2287
/// // ...
2288
///
2289
/// int main(int argc, char *argv[])
2290
/// {
2291
/// int verbose = argc > 1; // allows user to control output from command
2292
/// // line
2293
///
2294
/// bslma::Allocator *alloc_p = bslma::Default::globalAllocator();
2295
/// // Get global allocator.
2296
///
2297
/// ball::LoggerManagerConfiguration configuration; // default configuration
2298
/// configuration.setDefaultThresholdLevelsIfValid(
2299
/// ball::Severity::e_TRACE, // "Record"
2300
/// ball::Severity::e_WARN, // "Pass-Through"
2301
/// ball::Severity::e_ERROR, // "Trigger"
2302
/// ball::Severity::e_FATAL); // "Trigger-All"
2303
///
2304
/// ball::LoggerManagerScopedGuard scopedGuard(configuration);
2305
/// // Instantiate the logger manager singleton.
2306
///
2307
/// ball::LoggerManager& manager = ball::LoggerManager::singleton();
2308
///
2309
/// bsl::ofstream outFile("outFile");
2310
/// bsl::shared_ptr<ball::StreamObserver> observer(
2311
/// new(*alloc_p) ball::StreamObserver(&outFile),
2312
/// alloc_p);
2313
/// // Create simple observer; writes to 'outFile".
2314
///
2315
/// manager.registerObserver(observer, "default");
2316
/// // Register the observer under (arbitrary) name "default".
2317
///
2318
/// BALL_LOG_SET_CATEGORY("main");
2319
///
2320
/// bslmt::ThreadAttributes attributes;
2321
/// bslmt::ThreadUtil::Handle handle1;
2322
/// bslmt::ThreadUtil::Handle handle2;
2323
///
2324
/// // first thread manages its own logger; second thread uses default logger
2325
/// bslmt::ThreadUtil::create(&handle1, attributes, threadFunction1, 0);
2326
/// bslmt::ThreadUtil::create(&handle2, attributes, threadFunction2, 0);
2327
///
2328
/// bslmt::ThreadUtil::join(handle1);
2329
/// bslmt::ThreadUtil::join(handle2);
2330
///
2331
/// if (verbose) { // 'if' to allow command-line activation
2332
/// BALL_LOG_FATAL << "Force publication.";
2333
/// }
2334
/// return 0;
2335
/// }
2336
/// @endcode
2337
/// When the above program is run with a command-line argument, causing the
2338
/// 'e_FATAL' record to be logged and thus a "Trigger-All", the records from
2339
/// thread 2 are not available to be published. This is of course not an error
2340
/// but desired behavior chosen by the programmer. The output is as follows.
2341
/// @code
2342
/// 27SEP2007_14:09:31.197 1615 3 WARN logging.m.cpp 29 Function 2 Message 0
2343
///
2344
/// 27SEP2007_14:09:33.207 1615 3 WARN logging.m.cpp 29 Function 2 Message 1
2345
///
2346
/// 27SEP2007_14:09:35.217 1615 3 WARN logging.m.cpp 29 Function 2 Message 2
2347
///
2348
/// 27SEP2007_14:09:41.217 1615 1 FATAL logging.m.cpp 107 main Force publication.
2349
///
2350
/// 27SEP2007_14:09:41.217 1615 1 FATAL logging.m.cpp 107 main Force publication.
2351
///
2352
/// 27SEP2007_14:09:35.217 1615 3 WARN logging.m.cpp 29 Function 2 Message 2
2353
///
2354
/// 27SEP2007_14:09:33.207 1615 3 WARN logging.m.cpp 29 Function 2 Message 1
2355
///
2356
/// 27SEP2007_14:09:31.197 1615 3 WARN logging.m.cpp 29 Function 2 Message 0
2357
/// @endcode
2358
///
2359
/// ## Usage: Advanced Features
2360
///
2361
///The following section shows examples of features used to customize the behavior
2362
///and performance of the 'ball' logging system. They might be used, for example,
2363
///by a low-level infrastructure library where the trade-off of greater complexity
2364
///would be justified.
2365
///
2366
/// ### Advanced Features Example 1: Rule-Based Logging
2367
///
2368
/// The following example demonstrates the use of attributes and rules to
2369
/// conditionally enable logging.
2370
///
2371
/// We start by defining a function, 'processData', that is passed data in a
2372
/// 'vector<char>' and information about the user who sent the data. This
2373
/// example function performs no actual processing, but does log a single
2374
/// message at the 'ball::Severity::e_DEBUG' threshold level. The 'processData'
2375
/// function also adds the user information passed to this function to the
2376
/// thread's attribute context. We will use these attributes later, to create a
2377
/// logging rule that enables verbose logging only for a particular user.
2378
/// @code
2379
/// void processData(int uuid,
2380
/// int luw,
2381
/// int terminalNumber,
2382
/// const bsl::vector<char>& data)
2383
/// // Process the specified 'data' associated with the specified Bloomberg
2384
/// // 'uuid', 'luw', and 'terminalNumber'.
2385
/// {
2386
/// (void)data; // suppress "unused" warning
2387
/// @endcode
2388
/// We add our attributes using 'ball::ScopedAttribute', which adds an attribute
2389
/// container with one attribute to a list of containers. This is easy and
2390
/// efficient if the number of attributes is small, but should not be used if
2391
/// there are a large number of attributes. If motivated, we could use
2392
/// 'ball::DefaultAttributeContainer', which provides an efficient container for
2393
/// a large number of attributes, or even create a more efficient attribute
2394
/// container implementation specifically for these three attributes (uuid, luw,
2395
/// and terminalNumber). See {@ref ball_scopedattributes } (plural) for an example
2396
/// of using a different attribute container, and {@ref ball_attributecontainer }
2397
/// for an example of creating a custom attribute container.
2398
/// @code
2399
/// // We use 'ball::ScopedAttribute' here because the number of
2400
/// // attributes is relatively small.
2401
///
2402
/// ball::ScopedAttribute uuidAttribute("mylibrary.uuid", uuid);
2403
/// ball::ScopedAttribute luwAttribute("mylibrary.luw", luw);
2404
/// ball::ScopedAttribute termNumAttribute("mylibrary.terminalNumber",
2405
/// terminalNumber);
2406
/// @endcode
2407
/// In this simplified example we perform no actual processing, and simply log
2408
/// a message at the 'ball::Severity::e_DEBUG' level.
2409
/// @code
2410
/// BALL_LOG_SET_CATEGORY("EXAMPLE.CATEGORY");
2411
///
2412
/// BALL_LOG_DEBUG << "An example message";
2413
/// @endcode
2414
/// Notice that if we were not using a "scoped" attribute container like that
2415
/// provided automatically by 'ball::ScopedAttribute' (e.g., if we were using a
2416
/// local 'ball::DefaultAttributeContainer' instead), then the container
2417
/// **must** be removed from the 'ball::AttributeContext' before it is
2418
/// destroyed! See @ref ball_scopedattributes (plural) for an example.
2419
/// @code
2420
/// }
2421
/// @endcode
2422
/// Next we demonstrate how to create a logging rule that sets the pass-through
2423
/// logging threshold to 'ball::Severity::e_TRACE' (i.e., enables verbose logging)
2424
/// for a particular user when calling the 'processData' function defined
2425
/// above.
2426
///
2427
/// We start by creating the singleton logger manager that we configure with
2428
/// the stream observer and a default configuration. We then call the
2429
/// 'processData' function: This first call to 'processData' will not result in
2430
/// any logged messages because 'processData' logs its message at the
2431
/// 'ball::Severity::e_DEBUG' level, which is below the default configured logging
2432
/// threshold.
2433
/// @code
2434
/// int main(int argc, const char *argv[])
2435
/// {
2436
/// ball::LoggerManagerConfiguration configuration;
2437
/// ball::LoggerManagerScopedGuard lmg(configuration);
2438
/// ball::LoggerManager& manager = ball::LoggerManager::singleton();
2439
///
2440
/// bsl::shared_ptr<ball::StreamObserver> observer =
2441
/// bsl::make_shared<ball::StreamObserver>(&bsl::cout);
2442
/// manager.registerObserver(observer, "default");
2443
///
2444
/// BALL_LOG_SET_CATEGORY("EXAMPLE.CATEGORY");
2445
///
2446
/// bsl::vector<char> message;
2447
///
2448
/// BALL_LOG_ERROR << "Processing the first message.";
2449
/// processData(3938908, 2, 9001, message);
2450
///
2451
/// @endcode
2452
/// Now we add a logging rule, setting the pass-through threshold to be
2453
/// 'ball::Severity::e_TRACE' (i.e., enabling verbose logging) if the thread's
2454
/// context contains a "uuid" of 3938908. Note that we use the wild-card
2455
/// value '*' for the category so that the 'ball::Rule' rule will apply to all
2456
/// categories.
2457
/// @code
2458
/// ball::Rule rule("*", 0, ball::Severity::e_TRACE, 0, 0);
2459
/// rule.addAttribute(ball::ManagedAttribute("mylibrary.uuid", 3938908));
2460
/// ball::LoggerManager::singleton().addRule(rule);
2461
///
2462
/// BALL_LOG_ERROR << "Processing the second message.";
2463
/// processData(3938908, 2, 9001, message);
2464
/// @endcode
2465
/// The final call to the 'processData' function below, passes a "uuid" of
2466
/// 2171395 (not 3938908) so the logging rule we defined will *not* apply and
2467
/// no message will be logged.
2468
/// @code
2469
/// BALL_LOG_ERROR << "Processing the third message.";
2470
/// processData(2171395, 2, 9001, message);
2471
/// }
2472
/// @endcode
2473
/// The resulting logged output for this example looks like the following:
2474
/// @code
2475
/// ERROR example.cpp:105 EXAMPLE.CATEGORY Processing the first message.
2476
/// ERROR example.cpp:117 EXAMPLE.CATEGORY Processing the second message.
2477
/// DEBUG example.cpp:35 EXAMPLE.CATEGORY An example message
2478
/// ERROR example.cpp:129 EXAMPLE.CATEGORY Processing the third message.
2479
/// @endcode
2480
/// Please see {Key Example 2: Initialization} to learn how scoped attributes can
2481
/// be added to log messages to provide additional log context.
2482
///
2483
/// ### Advanced Features Example 2: Customizing Attribute Collection
2484
///
2485
/// This example demonstrates how to customize the collection of log attributes.
2486
/// Attributes are typically associated with a log record using a
2487
/// 'ball::ScopedAttribute' object (see {Key Example 1: Write to a Log} and
2488
/// {Key Example 2: Initialization}). However, advanced users can customize
2489
/// the collection of attributes, either by registering an attribute-collector
2490
/// callback with the 'ball::LoggerManager', or by creating their own
2491
/// 'ball::AttributeCollector' implementation, or both. These customizations
2492
/// can be used to implement alternative behavior, or provide faster
2493
/// implementation (e.g., by taking advantage of compile-time knowledge of the
2494
/// attributes being recorded).
2495
///
2496
/// Suppose we are writing a performance critical infrastructure system
2497
/// that processes requests. We want to:
2498
///
2499
/// * Obtain a (very small) performance benefit by implementing our own
2500
/// 'ball::AttributeContainer (rather than using 'ball::ScopedAttribute').
2501
///
2502
/// * Install a separate attribute collector callback function, in this
2503
/// instance one that will report information about global process state.
2504
///
2505
/// First we create a 'ball::AttributeContainer' implementation. Although the
2506
/// performance gains from doing so are typically insignificant, the use of a
2507
/// context-specific attribute container object allows us to take advantage of
2508
/// compile-time knowledge of the attributes being collected and make small
2509
/// improvements in the overhead required, which may be important for
2510
/// performance critical systems.
2511
/// @code
2512
/// // serviceattributes.h
2513
///
2514
/// class ServiceAttributes : public ball::AttributeContainer {
2515
/// // Provide a concrete implementation of the 'ball::AttributeContainer'
2516
/// // protocol that holds the 'uuid', 'luw', and 'firmId' associated with a
2517
/// // request to the example service.
2518
///
2519
/// int d_uuid;
2520
/// int d_luw;
2521
/// int d_firmId;
2522
///
2523
/// // ...
2524
///
2525
/// public:
2526
/// // CREATORS
2527
/// ServiceAttributes(int uuid, int luw, int firmId);
2528
/// // Create a service-attributes object with the specified 'uuid',
2529
/// // 'luw', and 'firmId'.
2530
///
2531
/// virtual ~ServiceAttributes();
2532
///
2533
/// // ACCESSORS
2534
/// virtual bool hasValue(const ball::Attribute& value) const;
2535
///
2536
/// virtual void visitAttributes(
2537
/// const bsl::function<void(const ball::Attribute&)>& visitor) const;
2538
///
2539
/// virtual bsl::ostream& print(bsl::ostream& stream,
2540
/// int level = 0,
2541
/// int spacesPerLevel = 4) const;
2542
/// // Format this object to the specified output 'stream'.
2543
/// };
2544
///
2545
/// // CREATORS
2546
/// inline
2547
/// ServiceAttributes::ServiceAttributes(int uuid, int luw, int firmId)
2548
/// : d_uuid(uuid)
2549
/// , d_luw(luw)
2550
/// , d_firmId(firmId)
2551
/// {
2552
/// }
2553
///
2554
/// // serviceattributes.cpp
2555
///
2556
/// // CREATORS
2557
/// ServiceAttributes::~ServiceAttributes()
2558
/// {
2559
/// }
2560
///
2561
/// // ACCESSORS
2562
/// bool ServiceAttributes::hasValue(const ball::Attribute& value) const
2563
/// {
2564
/// return ball::Attribute("mylibrary.uuid", d_uuid) == value
2565
/// || ball::Attribute("mylibrary.luw", d_luw) == value
2566
/// || ball::Attribute("mylibrary.firmId", d_firmId) == value;
2567
/// }
2568
///
2569
/// void ServiceAttributes::visitAttributes(
2570
/// const bsl::function<void(const ball::Attribute&)>& visitor) const
2571
/// {
2572
/// visitor(ball::Attribute("mylibrary.uuid", d_uuid));
2573
/// visitor(ball::Attribute("mylibrary.luw", d_luw));
2574
/// visitor(ball::Attribute("mylibrary.firmId", d_firmId));
2575
/// }
2576
///
2577
/// bsl::ostream& ServiceAttributes::print(bsl::ostream& stream,
2578
/// int level,
2579
/// int spacesPerLevel) const
2580
/// {
2581
/// bslim::Printer printer(&stream, level, spacesPerLevel);
2582
/// printer.start();
2583
/// printer.printAttribute("uuid", d_uuid);
2584
/// printer.printAttribute("luw", d_luw);
2585
/// printer.printAttribute("firmId", d_firmId);
2586
/// printer.end();
2587
/// return stream;
2588
/// }
2589
/// @endcode
2590
/// Then we create a guard to add and remove a 'ServiceAttributes' container
2591
/// from the current logging attribute context:
2592
/// @code
2593
/// class ServiceAttributesGuard {
2594
/// // DATA
2595
/// ServiceAttributes d_attributes;
2596
/// // attributes
2597
///
2598
/// const ball::AttributeContext::iterator d_it;
2599
/// // reference to attribute container
2600
///
2601
/// public:
2602
/// ServiceAttributesGuard(int uuid, int luw, int firmId)
2603
/// : d_attributes(uuid, luw, firmId)
2604
/// , d_it(
2605
/// ball::AttributeContext::getContext()->addAttributes(&d_attributes))
2606
/// {
2607
/// }
2608
///
2609
/// ~ServiceAttributesGuard()
2610
/// {
2611
/// ball::AttributeContext::getContext()->removeAttributes(d_it);
2612
/// }
2613
/// };
2614
/// @endcode
2615
/// Now we use a 'ServiceAttributesGuard' in a critical infrastructure
2616
/// function:
2617
/// @code
2618
/// int processData(int uuid, int luw, int firmId, const char *data)
2619
/// {
2620
/// BALL_LOG_SET_CATEGORY("MYLIBRARY.MYSUBSYSTEM");
2621
/// ServiceAttributesGuard attributes(uuid, luw, firmId);
2622
///
2623
/// BALL_LOG_TRACE << "Processing data: " << data;
2624
///
2625
/// int rc = 0;
2626
///
2627
/// // ...
2628
///
2629
/// if (0 != rc) {
2630
/// BALL_LOG_WARN << "Error processing data: " << data;
2631
/// }
2632
/// return rc;
2633
/// }
2634
/// @endcode
2635
/// Notice that when 'processData' is called, attributes for 'uuid', 'luw', and
2636
/// 'firmId' will be associated with each log message emitted during that
2637
/// function call.
2638
///
2639
/// Next, we create a callback function that will be used to associate
2640
/// a hostname attribute to each log record for the lifetime of the process:
2641
/// @code
2642
/// void loadHostnameAttribute(
2643
/// const ball::LoggerManager::AttributeVisitor& visitor)
2644
/// {
2645
/// char hostname[256];
2646
/// if (0!= gethostname(hostname, 256)) {
2647
/// bsl::strcpy(hostname, "failed.to.get.hostname");
2648
/// }
2649
/// visitor(ball::Attribute("mylibrary.hostname", hostname));
2650
/// }
2651
/// @endcode
2652
/// Finally we demonstrate a function that registers the
2653
/// 'loadHostnameAttribute' with the logger manager:
2654
/// @code
2655
/// int configureLibrary()
2656
/// {
2657
/// ball::LoggerManager::singleton().registerAttributeCollector(
2658
/// &loadHostnameAttribute, "mylibrary.hostnamecollector");
2659
///
2660
/// // ...
2661
/// }
2662
/// @endcode
2663
/// Notice that the attribute "mylibrary.hostname" will now be associated with
2664
/// every log message created (until "mylibrary.hostnamecollector" is unregistered
2665
/// or the logger manager is destroyed).
2666
///
2667
/// ## Appendix: Macro Reference
2668
///
2669
/// This section documents the preprocessor macros defined in 'ball_log' that are
2670
/// most commonly used.
2671
///
2672
/// The following two macros establish the logging context required by the other
2673
/// macros. A use of one of these two macros must be visible from within the
2674
/// lexical scope where the C++ stream-based and 'printf'-style macros are used:
2675
///
2676
/// 'BALL_LOG_SET_CATEGORY(CATEGORY)':
2677
/// Set the category for logging to the specified 'CATEGORY' (assumed to be of
2678
/// type convertible to 'const char *'). Note that this macro must be used at
2679
/// block scope and can be used at most once in any given block (or else a
2680
/// compiler diagnostic will result). Also note that this macro invokes the
2681
/// 'ball::Log::setCategory' method to retrieve the address of an appropriate
2682
/// category structure for its scope. (See the function- level documentation
2683
/// of 'ball::Log::setCategory' for more information.) Also note that the
2684
/// category is set only on the *first* invocation of this macro in a code
2685
/// block; subsequent invocations will use a cached address of the category.
2686
///
2687
/// 'BALL_LOG_SET_DYNAMIC_CATEGORY(CATEGORY)':
2688
/// Set, *on each invocation*, the category for logging to the specified
2689
/// 'CATEGORY'. This macro is identical to 'BALL_LOG_SET_CATEGORY' in scoping,
2690
/// parameter, and use of the 'ball::Log::setCategory' method. However, the
2691
/// address returned from 'ball::Log::setCategory' is not cached for subsequent
2692
/// calls. Use this macro to create categories that depend on run-time values
2693
/// (e.g., UUID).
2694
///
2695
/// The seven macros based on C++ streams, 'BALL_LOG_TRACE', 'BALL_LOG_DEBUG',
2696
/// 'BALL_LOG_INFO', 'BALL_LOG_WARN', 'BALL_LOG_ERROR', and 'BALL_LOG_FATAL',
2697
/// have the following usage pattern:
2698
/// @code
2699
/// BALL_LOG_TRACE << X << Y ...;
2700
/// BALL_LOG_DEBUG << X << Y ...;
2701
/// BALL_LOG_INFO << X << Y ...;
2702
/// BALL_LOG_WARN << X << Y ...;
2703
/// BALL_LOG_ERROR << X << Y ...;
2704
/// BALL_LOG_FATAL << X << Y ...;
2705
/// where X, Y, ... represents any sequence of values for which 'operator<<'
2706
/// is defined. The resulting formatted message string is logged with the
2707
/// severity indicated by the name of the initial macro (e.g.,
2708
/// 'BALL_LOG_TRACE' logs with severity 'ball::Severity::e_TRACE'). Note
2709
/// that the formatted string includes the category and filename as
2710
/// established by the 'BALL_LOG_SET_CATEGORY' (or
2711
/// 'BALL_LOG_SET_DYNAMIC_CATEGORY') and '__FILE__' macros, respectively.
2712
/// @endcode
2713
/// The remaining macros are based on 'printf'-style format specifications:
2714
/// @code
2715
/// BALL_LOGVA_TRACE(MSG, ...);
2716
/// BALL_LOGVA_DEBUG(MSG, ...);
2717
/// BALL_LOGVA_INFO( MSG, ...);
2718
/// BALL_LOGVA_WARN( MSG, ...);
2719
/// BALL_LOGVA_ERROR(MSG, ...);
2720
/// BALL_LOGVA_FATAL(MSG, ...);
2721
/// Format the specified '...' optional arguments, if any, according to the
2722
/// 'printf'-style format specification in the specified 'MSG' (assumed to be
2723
/// of type convertible to 'const char *') and log the resulting formatted
2724
/// message string with the severity indicated by the name of the macro
2725
/// (e.g., 'BALL_LOGVA_INFO' logs with severity 'ball::Severity::e_INFO').
2726
/// The behavior is undefined unless the number and types of optional
2727
/// arguments are compatible with the format specification in 'MSG'. Note
2728
/// that each use of these macros must be terminated by a ';'.
2729
/// @endcode
2730
///
2731
/// @}
2732
/** @} */
doxygen_input
bde
groups
bal
ball
doc
ball.h
Generated by
1.9.8