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