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/** @} */