// ball_loggermanager.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BALL_LOGGERMANAGER #define INCLUDED_BALL_LOGGERMANAGER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a manager of core logging functionality. // //@CLASSES: // ball::Logger: log record store and publication manager // ball::LoggerManager: logger factory and category administrator // ball::LoggerManagerScopedGuard: scoped guard for 'LoggerManager' singleton // //@SEE_ALSO: ball_record, ball_recordattributes, ball_observer, ball_context, // ball_loggermanagerdefaults, ball_loggermanagerconfiguration, // ball_severity, ball_transmission, ball_log // //@DESCRIPTION: This component provides the core of the 'ball' logging toolkit: // the logger class itself, 'ball::Logger', that manages log record storage // and publication control; the logger manager class, 'ball::LoggerManager', // typically instantiated as a singleton, that is both a factory for loggers // and a category manager; and the logger manager scoped guard, // 'ball::LoggerManagerScopedGuard', that provides a convenient way to // initialize and manage lifetime of the logger manager singleton object. // ///General Features and Behavior ///----------------------------- // The 'ball' logging toolkit is very flexible. A user can log messages with // very little effort, and with only a superficial understanding of logger // operation, in which case the logger will exhibit its "default behavior". // The user can also elect to customize many aspects of logging, such as // storage and publication behavior, both at start-up and dynamically during // program execution. Naturally, to exercise such control, the user must // become more familiar with 'ball' logger operation; the user can choose more // convenience or more versatility, with a reasonably fine granularity. // // Log records incorporate both fixed (logger-defined) and optional // (user-defined) fields, affording yet more flexibility (see "Log Record // Contents" below). The logger directly populates certain of the required // fields, and indirectly manages population of any optional fields by invoking // a client-supplied callback function that sets the optional values. // // Clients obtain one or more loggers from the logger manager, although at most // one logger may be "active" in any one thread; a request to log a message is // directed to the active logger in that thread. Each logger both stores and // publishes appropriate log records. // // All loggers share a single internal broadcast observer to which log records // are transmitted when they are published (see the component-level // documentation of 'ball_observer' for more information on observers). // // A logger can achieve high performance through the use of an in-memory record // buffer for storing the records logged by a program. Each logger is // constructed with a record manager that is an instance of a concrete class // derived from 'ball::RecordBuffer'. The singleton logger manager supplies a // "default" record manager to the default logger; loggers allocated by the // logger manager's 'allocateLogger' method use a record manager supplied by // the client. The default log record buffer is of user-configurable static // size and is circular (see the 'ball_circularrecordbuffer' component for // details), whereby continuous logging (without publication of logged records) // can result in older records being overwritten by newer ones. A circular // buffer provides an efficient "trace-back" strategy, wherein only log records // proximate to a user-specified logging event (see below) are published. Such // a circular buffer may not be appropriate for all situations; the user can // change the behavior of the default logger by adjusting the logging threshold // levels (see below) or can install a logger that uses a different kind of // record buffer. // ///Logger Manager Singleton Initialization ///--------------------------------------- // The recommended way to initialize the logger manager singleton is to create // a 'ball::LoggerManagerScopedGuard' object in 'main' *before* creating any // threads. The logger manager scoped guard constructor takes a configuration // object (an instance of 'ball::LoggerManagerConfiguration'), and an optional // allocator. The logger manager singleton is created as a side-effect of // creating the scoped guard object. When the guard object goes out of scope // (i.e., on program exit), the logger manager singleton is automatically // destroyed. // // The 'ball::LoggerManagerConfiguration' object is used to supply a set of // user-defined "default" values and other options. However, to obtain the // "default" logging behavior, it is sufficient to instantiate a default // 'ball::LoggerManagerConfiguration' object and pass that to the constructor // of the scoped guard along with an observer. (See {Usage} below.) // // As an alternative to using the scoped guard, the 'initSingleton' method that // takes the same arguments as the scoped guard may be used to initialize the // singleton. However, in this case the 'shutDownSingleton' method must be // explicitly called to destroy the logger manager singleton on program exit. // Unless 'shutDownSingleton' is called, the singleton will not be destroyed // and resources used by the singleton will leak. // // Note that the logger manager singleton *can* be reinitialized after it has // been destroyed. However, such practice should generally be restricted to // test drivers and very specialized use cases. Clients should generally avoid // initializing and destroying the singleton more than once in a program unless // they know what they are doing. // ///Deprecation Notice ///------------------ // Direct use of any of the 'ball::LoggerManager' or // 'ball::LoggerManagerScopedGuard' methods that take raw pointers to observers // is *deprecated*. These methods will be eliminated in a future release. // // The 'ball::LoggerManagerCategoryIter' and 'ball::LoggerManagerCategoryManip' // classes are *deprecated*. Clients of 'ball::LoggerManager' should use the // 'visitCategories' accessor (the replacement for 'LoggerManagerCategoryIter') // or 'visitCategories' manipulator (replacing 'LoggerManagerCategoryManip') // instead. // ///Categories, Severities, and Threshold Levels ///-------------------------------------------- // The logger supports the notions of "severity level" and "category"; every // record is logged at some severity level and to some category. Categories // are user-defined (except for the "default category"), and have unique names. // Severity levels are integers in the range '[0 .. 255]', and are most // typically chosen from among the enumeration in the 'ball_severity' // component, although use of the 'enum' is optional. The severity level and // the category name are each among the fixed fields of the record being logged // (see "Log Record Contents" below). // // From the logger's perspective, all categories are peers; there is no special // significance to any sequence of characters in a category name. The user may // impose a hierarchical *meaning* to category names, and the logger manager // facilitates a certain degree of hierarchical *behavior* via several callback // functors provided within this component (see below, and also the // 'ball_loggerfunctorpayloads' component). However, such hierarchy is not // fundamental to categories, nor to the behavior described in this section. // Similarly, there is no a priori significance to severity levels except that // they are ordered and may be compared for inequality, although the enumerator // names in the 'ball::Severity::Level' enumeration (e.g., 'DEBUG', 'WARN', // 'ERROR', etc.) suggest the intended "standard" meanings. // // Every category has associated with it four "severity threshold levels" that // may be set explicitly by the user on category creation/registration (via the // 'addCategory' method) or else will default to specific values via one of // several mechanisms described below (invoked by the one-argument // 'setCategory' method). Category threshold levels may also be changed during // program execution via the five-argument 'setCategory' method. // // When the user logs a record to a given category and at a given severity (via // the 'ball::Logger' 'logMessage' method or via the logging macros -- see the // 'ball_log' component), the logger manager uses the specified severity and // the category's registered severity threshold levels to govern the logger's // behavior; depending on the thresholds, the message may be recorded to an // in-memory buffer, published to an external observer, or ignored. In // addition, if thresholds are set appropriately, the entire contents of the // in-memory buffer of one or more loggers may be published to external // observers. Clients of the logger can use, and dynamically administer, the // category threshold levels to enhance run-time performance and/or to reduce // message volume while still capturing all critical log messages. // // The names and exact meanings of the four severity threshold levels are as // follows: // //: Record: //: If the severity level of the record is at least as severe as the Record //: threshold level of the associated category, then the record will be //: stored by the logger in its log record buffer (i.e., it will be //: recorded). //: //: Pass: //: If the severity of the record is at least as severe as the Pass //: threshold level of the associated category, then the record will be //: immediately published by the logger (i.e., it will be transmitted to //: the logger's downstream recipient -- the observer). //: //: Trigger: //: If the severity of the record is at least as severe as the Trigger //: threshold level of the associated category, then the record will cause //: immediate publication of that record and any records in the logger's //: log record buffer (i.e., this record will trigger a general log record //: dump). //: //: Trigger-All: //: If the severity of the record is at least as severe as the Trigger-All //: threshold level of the associated category, then the record will cause //: immediate publication of that record and all other log records stored //: by *all* active loggers. // // Note that more than one of the above actions can apply to a given log // record, since the four threshold levels are independent of one another. // Note also that *all* of these actions are governed by the threshold levels // of the record being logged, and not by the threshold levels of any stored // records that are published as a result of a Trigger or Trigger-All event. // ///Terminology: "Factory Default" Thresholds ///- - - - - - - - - - - - - - - - - - - - - // The logger manager supplies "default values" for category threshold levels // whenever a category is created without client-supplied values. These // default values can come from any one of several possible sources, depending // upon options that the user has elected; the system is flexible, but leads to // a bit of confusion in terminology. This section explains the meaning of // "factory default" values and introduces the various "default" threshold // mechanisms. // // The logger manager is a "factory" for loggers; we therefore define "factory // defaults" to be the default values that the 'ball::LoggerManager' singleton // is aware of at construction. Depending on the values and options in the // 'ball::LoggerManagerConfiguration' object provided to the logger manager on // construction, the factory defaults may be either implementation-defined or // user-defined. // // In either case, the user can *change* the default values during logger // operation via the 'setDefaultThresholdLevels' method. These threshold // levels become the "default" values for new categories, but they do not // affect the "factory defaults" that subsequently can be restored via the // 'resetDefaultThresholdLevels' method. // // A third mechanism, the 'ball::LoggerManager::DefaultThresholdLevelsCallback' // functor, adds even more flexibility. If this callback is installed by the // user at construction, or subsequently via the // 'setDefaultThresholdLevelsCallback' method, the callback is the source of // all default thresholds, and the above mechanisms are not used. The next // section covers category thresholds in more detail. // ///Category Creation, Management, and Threshold Levels ///- - - - - - - - - - - - - - - - - - - - - - - - - - // When the logger manager singleton is created, a unique category known as the // *Default* *Category* is created, and is given "factory-supplied" default // threshold levels. The default values for the default category are each in // the range '[0 .. 255]', but are otherwise unspecified. The user can also // specify default values explicitly when the logger manager singleton is // constructed. This is accomplished by constructing a // 'ball::LoggerManagerDefaults' object, setting the desired values, and then // setting that object as an attribute of the // 'ball::LoggerManagerConfiguration' argument to the // 'ball::LoggerManagerScopedGuard' constructor. // // The default category is issued to the user via the return value of the // 'setCategory(const char *categoryName)' method whenever a new category // cannot be created due to a capacity limitation on the category registry // maintained by the logger manager. The method's normal behavior is to return // the category having 'categoryName'. // // Categories that are added to the registry during logging through calls to // the 'setCategory(const char *)' method are given threshold levels by one of // two means. The "default" mechanism (a slightly overloaded term in 'ball') // is to use the same default thresholds as described above for the default // category. The alternative is to specify a // 'ball::LoggerManager::DefaultThresholdLevelsCallback' functor, either when // the logger manager singleton is initialized or else afterwards via the // 'setDefaultThresholdLevelsCallback' method. This functor, if provided, is // used by the logger manager to supply the four 'int' threshold values; the // functor may generate these values by any means that the user sees fit. See // the 'ball_loggerfunctorpayloads' component for an example payload function // for the functor. // // The default threshold levels can be adjusted ('setDefaultThresholdLevels') // and reset to their original values ('resetDefaultThresholdLevels'). Note // that if factory values are overridden at initialization, a reset will // restore thresholds to the user-specified default values. In addition, there // is a method to set the threshold levels of a given category to the current // default threshold levels ('setCategoryThresholdsToCurrentDefaults') or to // the factory-supplied (or client-overridden) default values // ('setCategoryThresholdsToFactoryDefaults'). // // As a final note regarding categories, a client can optionally supply to the // logger manager on construction a // 'ball::LoggerManager::CategoryNameFilterCallback' functor (via the // 'ball::LoggerManagerConfiguration' object) to translate category names from // an external to an internal representation. For example, a project may allow // programmers to refer to categories using mixed-case within an application, // but provide a 'toLower' 'CategoryNameFilterCallback' to map all external // upper-case letters to lower-case internally. In this scenario, the // (hypothetical) external category names "EQUITY.MARKET.NYSE" and // "equity.market.nyse" would be mapped to the same category internally by the // presumed 'toLower' functor. // ///Log Record Contents ///------------------- // Each log record contains a set of fixed fields and a set of optional, // user-definable fields and attributes. The following table lists the fixed // fields in each log record (see the component-level documentation of // 'ball_recordattributes' for more information on the fixed fields of a log // record): //.. // Field Name Type Description // ----------- -------------- -------------------------------------- // timestamp bdlt::Datetime creation date and time // process ID int process ID of creator // thread ID int thread ID of creator // filename string file where created (i.e., '__FILE__') // line number int line number in file (i.e., '__LINE__') // category string category name // severity int severity of logged record // message string log message text //.. // // The following table lists optional fields and attributes in each log record // (see the component-level documentation of 'ball_userfields' and // 'ball_managedattribute' for more information): //.. // Field Name Type Description // ----------- -------------- ------------------------- // userFields ball::UserFields [!DEPRECATED!] // attributes bsl::vector<ball::ManagedAttribute> user-managed log // attributes //.. // // [!DEPRECATED!] If a 'ball::LoggerManager::UserFieldsPopulatorCallback' // functor is supplied by the client (see 'ball_loggermanagerconfiguration'), // thereafter, every logged record has its user-defined fields (indirectly) // populated by an invocation of the 'UserFieldsPopulatorCallback' functor. // // The log record's attributes are populated by attribute collector functor(s) // registered by the user. // ///Multi-Threaded Usage ///-------------------- // The 'ball' logging toolkit may be used in single-threaded and multi-threaded // library code and applications with equal ease, and with virtually no // difference in coding. In particular, the same use of the // 'ball::LoggerManagerScopedGuard' class to initialize the logger manager // singleton is required in 'main' in both cases, and individual calls to the // 'ball::Logger' instance method 'logMessage' (and logging calls via the // logging macros -- see 'ball_log') are identical, from the user's // perspective. Category threshold administration is also identical in both // cases. // // Differences in logger usage, or, more precisely, additional options for the // multi-threaded user, arise when the user wishes to allocate one or more // loggers beyond the default logger that is owned by the singleton logger // manager. If a user does *not* explicitly allocate a logger (via the logger // manager instance method 'allocateLogger') and install that logger for a // given thread (via the manager instance method 'setLogger'), then all // records from all threads in a program will be logged to the one default // logger. However, since each thread of execution may have its own logger // instance, multi-threaded users may choose to allocate and install multiple // loggers. Note that each thread may have at most one logger, but a single // logger may be used by any number of threads. // // Multi-threaded users of logging may prefer to allocate and install one // logger per thread in order to take advantage of the "trace-back" feature // described above on a per-thread basis. In the event of an error condition // as defined by the programmer, such a logging configuration provides a // trace-back through the record buffer of the thread that caused the error, // without any dilution from records from other threads. Conversely, if // several threads are known to interact closely, it may be advantageous to // have them share a common logger so that the trace-back log *does* include // all relevant records. // ///'bsls::Log' Logging Redirection ///------------------------------- // The 'ball::LoggerManager' singleton, on construction, redirects 'bsls::Log' // messages to 'ball'. Such messages use the logging category "BSLS.LOG". // Upon its destruction, the logger manager singleton redirects 'bsls::Log' // messages back to the 'bsls::Log' message handler that was in effect prior to // the creation of the singleton (see 'bsls_log'). // ///Usage ///----- // This section illustrates instantiation of the logger manager singleton that // is required in 'main', and also shows *direct* use of the logger and logger // manager interfaces, much of which is actually *not* recommended. The most // basic logger functionality has been wrapped in macros defined in the // 'ball_log' component. See the 'ball' package-level documentation and the // 'ball_log' component documentation for recommended real-world usage // examples. // ///Example 1: Initialization #1 /// - - - - - - - - - - - - - - // Clients that perform logging must first instantiate the singleton logger // manager using the 'ball::LoggerManagerScopedGuard' class. This example // shows how to create a logger manager with the most basic "default behavior". // Subsequent examples will show more customized behavior. // // The following snippets of code illustrate the initialization sequence // (typically performed near the top of 'main'). // // First, we create a 'ball::LoggerManagerConfiguration' object, // 'configuration', and set the logging "pass-through" level -- the level at // which log records are published to registered observers -- to 'WARN' (see // {'Categories, Severities, and Threshold Levels'}): //.. // // myApp.cpp // // int main() // { // ball::LoggerManagerConfiguration configuration; // configuration.setDefaultThresholdLevelsIfValid(ball::Severity::e_WARN); //.. // Next, create a 'ball::LoggerManagerScopedGuard' object whose constructor // takes the configuration object just created. The guard will initialize the // logger manager singleton on creation and destroy the singleton upon // destruction. This guarantees that any resources used by the logger manager // will be properly released when they are not needed: //.. // ball::LoggerManagerScopedGuard guard(configuration); //.. // Note that the application is now prepared to log messages using the 'ball' // logging subsystem, but until the application registers an observer, all log // messages will be discarded. // // Finally, we create a 'ball::StreamObserver' object 'observer' that will // publish records to 'stdout' and register it with the logger manager // singleton. Note that observers must be registered by name; this example // simply uses "default" for a name: //.. // bslma::Allocator *alloc = bslma::Default::globalAllocator(0); // // bsl::shared_ptr<ball::StreamObserver> observer( // new(*alloc) ball::StreamObserver(&bsl::cout), // alloc); // ball::LoggerManager::singleton().registerObserver(observer, "default"); //.. // The application is now prepared to log messages using the 'ball' logging // subsystem: //.. // // ... // // return 0; // } //.. // Note that concrete observers that can be configured after their creation // (e.g., as to whether log records are published in UTC or local time) // generally can have their configuration adjusted at any time, either before // or after being registered with a logger manager. For an example of such an // observer, see 'ball_asyncfileobserver'. // ///Example 2: Initialization #2 /// - - - - - - - - - - - - - - // In this example, we demonstrate a more elaborate initial configuration for // the logger manager. In particular, we create the singleton logger manager // with a configuration that has a category name filter functor, a // 'DefaultThresholdLevelsCallback' functor, and user-chosen values for the // "factory default" threshold levels. // // First, we define three 'static' functions that are employed by the two // functors. The 'toLower' function implements our category name filter. It // is wrapped within a functor object and maps category names to lower-case: //.. // static // void toLower(bsl::string *buffer, const char *s) // { // assert(buffer); // assert(s); // // buffer->clear(); // while (*s) { // buffer->push_back(static_cast<char>(bsl::tolower(*s))); // ++s; // } // buffer->push_back(0); // } //.. // The following two functions provide the implementation for our // 'DefaultThresholdLevelsCallback' functor. The 'inheritThresholdLevels' // function is wrapped within a functor object; the 'getDefaultThresholdLevels' // function is a helper that does the hard work. We assume a hierarchical // category naming scheme that uses '.' to delimit the constituents of names. // For example, the three categories named "x", "x.y", and "x.y.z" are related // in the sense that "x" is an ancestor of both "x.y" and "x.y.z", and "x.y" is // an ancestor "x.y.z". Suppose that "x" is added to the registry first. If // "x.y" is then added to the registry by calling 'setCategory(const char *)', // it would "inherit" threshold level values from "x". Similarly, when "x.y.z" // is added to the registry by calling the 1-argument 'setCategory' method, it // inherits threshold level values from "x.y" (i.e., a category inherits from // its nearest ancestor that exists in the registry when it is added). Note // that a category named "xx.y" (for example) is not related to either of "x", // "x.y", or "x.y.z": //.. // static // int getDefaultThresholdLevels(int *recordLevel, // int *passLevel, // int *triggerLevel, // int *triggerAllLevel, // char delimiter, // const ball::LoggerManager& loggerManager, // const char *categoryName) // // Obtain appropriate threshold levels for the category having the // // specified 'categoryName' by searching the registry of the specified // // 'loggerManager', and store the resulting values at the specified // // 'recordLevel', 'passLevel', 'triggerLevel', and 'triggerAllLevel' // // addresses. A hierarchical category naming scheme is assumed that // // employs the specified 'delimiter' to separate the components of // // category names. Return 0 on success, and a non-zero value // // otherwise. The behavior is undefined unless 'recordLevel', // // 'passLevel', 'triggerLevel', and 'triggerAllLevel' are non-null, and // // 'categoryName' is null-terminated. // { // assert(recordLevel); // assert(passLevel); // assert(triggerLevel); // assert(triggerAllLevel); // assert(categoryName); // // enum { SUCCESS = 0, FAILURE = -1 }; // // bsl::string buffer(categoryName); // while (1) { // const ball::Category *category = // loggerManager.lookupCategory(buffer.c_str()); // if (0 != category) { // *recordLevel = category->recordLevel(); // *passLevel = category->passLevel(); // *triggerLevel = category->triggerLevel(); // *triggerAllLevel = category->triggerAllLevel(); // return SUCCESS; // RETURN // } // // const char *newEnd = bsl::strrchr(buffer.c_str(), delimiter); // if (0 == newEnd) { // return FAILURE; // RETURN // } // buffer.resize(newEnd - buffer.data()); // } // } // // static // void inheritThresholdLevels(int *recordLevel, // int *passLevel, // int *triggerLevel, // int *triggerAllLevel, // const char *categoryName) // // Obtain appropriate threshold levels for the category having the // // specified 'categoryName', and store the resulting values at the // // specified 'recordLevel', 'passLevel', 'triggerLevel', and // // 'triggerAllLevel' addresses. The behavior is undefined unless // // 'recordLevel', 'passLevel', 'triggerLevel', and 'triggerAllLevel' // // are non-null, and 'categoryName' is null-terminated. // { // assert(recordLevel); // assert(passLevel); // assert(triggerLevel); // assert(triggerAllLevel); // assert(categoryName); // // const ball::LoggerManager& manager = ball::LoggerManager::singleton(); // if (0 != getDefaultThresholdLevels(recordLevel, // passLevel, // triggerLevel, // triggerAllLevel, // '.', // manager, // categoryName)) { // *recordLevel = manager.defaultRecordThresholdLevel(); // *passLevel = manager.defaultPassThresholdLevel(); // *triggerLevel = manager.defaultTriggerThresholdLevel(); // *triggerAllLevel = manager.defaultTriggerAllThresholdLevel(); // } // } //.. // Then, we create the callback functors that will be supplied to the logger // manager singleton initialization (as in "Example 1" above, we assume that // the initialization sequence occurs somewhere near the top of 'main'): //.. // // myApp2.cpp // // int main() { // // ... // // ball::LoggerManager::CategoryNameFilterCallback nameFilter(&toLower); // // ball::LoggerManager::DefaultThresholdLevelsCallback // thresholdsCallback(&inheritThresholdLevels); //.. // Next, we define four values for our custom "factory default" thresholds. // These values will be stored within the logger manager and will be available // to all users whenever the "factory defaults" are needed, for the life of the // logger manager. In this example, however, we will also be installing the // 'thresholdsCallback' defined above, so unless that functor is un-installed // (by a call to 'setDefaultThresholdLevelsCallback'), these four "factory // defaults" will have no practical effect, since the callback mechanism "steps // in front of" the default values: //.. // int recordLevel = 125; // int passLevel = 100; // int triggerLevel = 75; // int triggerAllLevel = 50; //.. // Then, we can configure a 'ball::LoggerManagerDefaults' object, 'defaults', // with these four threshold values. 'defaults' can then be used to configure // the 'ball::LoggerManagerConfiguration' object that will be passed to the // 'ball::LoggerManagerScopedGuard' constructor (below): //.. // ball::LoggerManagerDefaults defaults; // defaults.setDefaultThresholdLevelsIfValid(recordLevel, // passLevel, // triggerLevel, // triggerAllLevel); //.. // Next, we create and set the 'ball::LoggerManagerConfiguration' object, // 'configuration', that will describe our desired configuration: //.. // ball::LoggerManagerConfiguration configuration; // configuration.setDefaultValues(defaults); // configuration.setCategoryNameFilterCallback(nameFilter); // configuration.setDefaultThresholdLevelsCallback(thresholdsCallback); //.. // Then, we instantiate the singleton logger manager, passing in the // 'configuration' that we have just created: //.. // ball::LoggerManagerScopedGuard guard(configuration); //.. // Note that the application is now prepared to log messages using the 'ball' // logging subsystem, but until the application registers an observer, all log // messages will be discarded. // // Now, we will demonstrate the functors and client-supplied default threshold // overrides. // // First, we obtain a reference to the singleton logger manager: //.. // ball::LoggerManager& manager = ball::LoggerManager::singleton(); //.. // Then, we obtain a reference to the *Default* *Category* and 'assert' that // its threshold levels match the client-supplied values that override the // "factory-supplied" default values: //.. // const ball::Category& defaultCategory = manager.defaultCategory(); // assert(125 == defaultCategory.recordLevel()); // assert(100 == defaultCategory.passLevel()); // assert( 75 == defaultCategory.triggerLevel()); // assert( 50 == defaultCategory.triggerAllLevel()); //.. // Next, we add a category named "BloombergLP" (by calling 'addCategory'). // Note that threshold levels supplied with the category override all defaults // (including thresholds set by the supplied callback). Also note that the // logger manager invokes the supplied category name filter to map the category // name to lower-case before the new category is added to the category // registry. The name filter is also invoked by 'lookupCategory' whenever a // category is searched for (i.e., by name) in the registry: //.. // const ball::Category *blpCategory = // manager.addCategory("BloombergLP", 128, 96, 64, 32); // assert(blpCategory == manager.lookupCategory("BLOOMBERGLP")); // assert( 0 == bsl::strcmp("bloomberglp", blpCategory->categoryName())); // assert(128 == blpCategory->recordLevel()); // assert( 96 == blpCategory->passLevel()); // assert( 64 == blpCategory->triggerLevel()); // assert( 32 == blpCategory->triggerAllLevel()); //.. // Then, we add a second category named "BloombergLP.bal.ball" (by calling // 'setCategory') and 'assert' that the threshold levels are "inherited" from // category "BloombergLP": //.. // const ball::Category *ballCategory = // manager.setCategory("BLOOMbergLP.bal.ball"); // // assert(ballCategory == manager.lookupCategory("bloomberglp.bal.ball")); // assert( 0 == bsl::strcmp("bloomberglp.bal.ball", // ballCategory->categoryName())); // assert(128 == ballCategory->recordLevel()); // assert( 96 == ballCategory->passLevel()); // assert( 64 == ballCategory->triggerLevel()); // assert( 32 == ballCategory->triggerAllLevel()); //.. // Now, we add a third category named "Other.equities", again by calling // 'setCategory'. This category has no ancestor currently in the registry, so // its threshold levels match those of the *Default* *Category*: //.. // const ball::Category *equitiesCategory = // manager.setCategory("Other.equities"); // assert(equitiesCategory == manager.lookupCategory("OTHER.EQUITIES")); // assert( 0 == bsl::strcmp("other.equities", // equitiesCategory->categoryName())); // assert(125 == equitiesCategory->recordLevel()); // assert(100 == equitiesCategory->passLevel()); // assert( 75 == equitiesCategory->triggerLevel()); // assert( 50 == equitiesCategory->triggerAllLevel()); //.. // Finally, we create a 'ball::StreamObserver' object 'observer' that will // publish records to 'stdout' and register it with the logger manager // singleton. Note that observers must be registered by name; this example // simply uses "default" for a name: //.. // bslma::Allocator *alloc = bslma::Default::globalAllocator(0); // // bsl::shared_ptr<ball::StreamObserver> observer( // new(*alloc) ball::StreamObserver(&bsl::cout), // alloc); // // manager.registerObserver(observer, "default"); // // ... // // return 0; // } //.. // ///Example 3: Efficient Logging of 'ostream'-able Objects /// - - - - - - - - - - - - - - - - - - - - - - - - - - - // The following example demonstrates how instances of a class supporting // streaming to 'bsl::ostream' (via overloaded 'operator<<') can be logged. It // also demonstrates how to use the 'logMessage' method to log messages to a // logger. Suppose we want to *efficiently* log instances of the following // class: //.. // class Information { // // This (incomplete) class is a simple aggregate of a "heading" and // // "contents" pertaining to that heading. It serves to illustrate how // // to log the string representation of an object. // // bsl::string d_heading; // bsl::string d_contents; // // public: // Information(const char *heading, const char *contents); // ~Information(); // const bsl::string& heading() const; // const bsl::string& contents() const; // }; //.. // In addition, we define the following free operator for streaming instances // of 'Information' to an 'bsl::ostream': //.. // bsl::ostream& operator<<(bsl::ostream& stream, // const Information& information) // { // stream << information.heading(); // stream << ": "; // stream << information.contents() << bsl::endl; // return stream; // } //.. // The following function logs an 'Information' object to the specified // 'logger': //.. // void logInformation(ball::Logger *logger, // const Information& information, // ball::Severity::Level severity, // const ball::Category& category, // const char *fileName, // int lineNumber) // { //.. // First, obtain a record that has its 'fileName' and 'lineNumber' attributes // set: //.. // ball::Record *record = logger->getRecord(fileName, lineNumber); //.. // Then, we get a non-'const' reference to the fixed fields of 'record': //.. // ball::RecordAttributes& attributes = record->fixedFields(); //.. // Next, we create a 'bsl::ostream' to which the string representation // 'information' can be output. Note that 'stream' is supplied with the stream // buffer of 'attributes': //.. // bsl::ostream stream(&attributes.messageStreamBuf()); //.. // Then, we stream 'information' into our output 'stream'. This will set the // message attribute of 'record' to the streamed data: //.. // stream << information; //.. // Finally, we log 'record' using 'logger': //.. // logger->logMessage(category, severity, record); // } //.. // Notice that we did not need to allocate a scratch buffer to stream the // object contents into. That would have required an extra copy and the cost // of allocation and deallocation, and thus would have been more inefficient. // ///Example 4: Logging using a 'ball::Logger' ///- - - - - - - - - - - - - - - - - - - - - // This example demonstrates using a 'ball::Logger' directly to log messages. // In practice, clients are encouraged to use the logging macros (see // {'ball_log'}. The following example assumes logging has been correctly // initialized (see prior examples). // // The following simple 'factorial' function takes and returns values of type // 'int'. Note that this function has a very limited range of input, namely // integers in the range '[0 .. 13]'. This limited range serves to illustrate // a usage pattern of the logger, namely to log "warnings" whenever a key // function is given bad input. // // For this example, it is sufficient to use the severity levels defined in the // 'ball::Severity::Level' enumeration: //.. // enum Level { // OFF = 0, // disable generation of corresponding message // FATAL = 32, // a condition that will (likely) cause a *crash* // ERROR = 64, // a condition that *will* cause incorrect behavior // WARN = 96, // a *potentially* problematic condition // INFO = 128, // data about the running process // DEBUG = 160, // information useful while debugging // TRACE = 192 // execution trace data // }; //.. // Note that the intervals left between enumerator values allow applications // to define additional values in case there is a desire to log with more // finely-graduated levels of severity. We will not need that granularity // here; 'ball::Severity::e_WARN' is appropriate to log a warning message if // the input argument to our factorial function is not in this range of values. // // We will register a unique category for this function, so that logged // messages from our function will be identified in the published output. // Also, with a unique category name, the logging behavior of this function can // be administered by resetting the various threshold levels for the category. // In this example, we will accept the default thresholds. // // The 'setCategory' method accepts a name and returns the address of a // 'ball::Category' with that name or, in some circumstances, the address of // the *Default* *Category* (see the function-level documentation of // 'setCategory' for details). The address returned by 'setCategory' is stored // in a function-static pointer variable (i.e., it is fetched only once upon // first use). In this example, we assume that we are writing a function for // Equities Graphics that will live in that group's Math library. The dot // "delimiters" ('.') have no particular significance to the logger, but may be // used by the administration methods to "induce" a hierarchical behavior on // our category, should that be useful. See, e.g., the callback functor // 'ball::LoggerManager::DefaultThresholdLevelsCallback' and its documentation, // and Usage Example 2 above for information on how to use category names to // customize logger behavior: //.. // int factorial(int n) // // Return the factorial of the specified value 'n' if the factorial // // can be represented as an 'int', and a negative value otherwise. // { // static const ball::Category *factorialCategory = // ball::LoggerManager::singleton().setCategory( // "equities.graphics.math.factorial", // ball::Severity::e_INFO, // ball::Severity::e_TRACE, // ball::Severity::e_ERROR, // ball::Severity::e_FATAL); //.. // We must also obtain a reference to a logger by calling the logger manager // 'getLogger' method. Note that this logger may not safely be cached as a // function 'static' variable since our function may be called in different // threads having different loggers. Even in a single-threaded program, the // owner of 'main' is free to install new loggers at any point, so a // statically-cached logger would be a problem: //.. // ball::Logger& logger = ball::LoggerManager::singleton().getLogger(); //.. // Now we validate the input value 'n'. If 'n' is either negative or too // large, we will log a warning message (at severity level // 'ball::Severity::e_WARN') and return a negative value. Note that calls to // 'logMessage' have no run-time overhead (beyond the execution of a simple // 'if' test) unless 'ball::Severity::e_WARN' is at least as severe as one of // the threshold levels of 'factorialCategory': //.. // if (0 > n) { // logger.logMessage(*factorialCategory, // ball::Severity::e_WARN, // __FILE__, // __LINE__, // "Attempt to take factorial of negative value."); // return n; // RETURN // } // // enum { MAX_ARGUMENT = 13 }; // maximum value accepted by 'factorial' // // if (MAX_ARGUMENT < n) { // logger.logMessage(*factorialCategory, // ball::Severity::e_WARN, // __FILE__, // __LINE__, // "Result too large for 'int'."); // return -n; // RETURN // } //.. // The remaining code proceeds mostly as expected, but adds one last message // that tracks control flow when 'ball::Severity::e_TRACE' is at least as // severe as one of the threshold levels of 'factorialCategory' (e.g., as // might be the case during debugging): //.. // int product = 1; // while (1 < n) { // product *= n; // --n; // } // // logger.logMessage(*factorialCategory, // ball::Severity::e_TRACE, // __FILE__, // __LINE__, // "Exiting 'factorial' successfully."); // // return product; // } //.. #include <balscm_version.h> #include <ball_attribute.h> #include <ball_attributecollectorregistry.h> #include <ball_broadcastobserver.h> #include <ball_categorymanager.h> #include <ball_loggermanagerconfiguration.h> #include <ball_record.h> #include <ball_recordbuffer.h> #include <ball_thresholdaggregate.h> #include <ball_transmission.h> #include <bdlcc_objectpool.h> #include <bdlcc_sharedobjectpool.h> #include <bdlma_concurrentpool.h> #include <bslma_allocator.h> #include <bslma_managedptr.h> #include <bslmf_util.h> // 'forward(V)' #include <bslmt_mutex.h> #include <bslmt_readerwritermutex.h> #include <bsls_compilerfeatures.h> #include <bsls_util.h> // 'forward<T>(V)' #include <bsl_functional.h> #include <bsl_map.h> #include <bsl_memory.h> #include <bsl_set.h> #include <bsl_string_view.h> namespace BloombergLP { namespace ball { class LoggerManager; class Observer; class RecordBuffer; // ============ // class Logger // ============ class Logger { // This class provides log record management services. Each instance of // 'Logger' receives log records from one or more clients, manages the // storage of those records, and transmits them to a registered recipient // (i.e., an observer) when appropriate. public: // TYPES typedef LoggerManagerConfiguration::UserFieldsPopulatorCallback UserFieldsPopulatorCallback; // 'UserFieldsPopulatorCallback' is the type of a user-supplied // callback functor used to populate the user-defined fields in each // log record. typedef bsl::function<void(Transmission::Cause)> PublishAllTriggerCallback; // 'PublishAllTriggerCallback' is the type of the functor that is // invoked with the publication cause to publish all record buffers of // all loggers that are allocated by the logger manager. private: // DATA bdlcc::SharedObjectPool<Record, bdlcc::ObjectPoolFunctors::DefaultCreator, bdlcc::ObjectPoolFunctors::Clear<Record> > d_recordPool; // pool of records with a // custom 'RESETTER' const bsl::shared_ptr<Observer> d_observer; // holds observer RecordBuffer *d_recordBuffer_p; // holds log record buffer // (not owned) UserFieldsPopulatorCallback d_userFieldsPopulator; // user fields populator // functor const AttributeCollectorRegistry *d_attributeCollectors_p; // pointer to the registry of // attribute collector // callbacks (not owned) PublishAllTriggerCallback d_publishAll; // publishAll callback functor bdlma::ConcurrentPool d_bufferPool; // pool of buffers for // formatting log messages // allowing recursive access char *d_scratchBuffer_p; // buffer for formatting log // messages (owned) bslmt::Mutex d_scratchBufferMutex; // ensure thread-safety of // message buffer int d_scratchBufferSize; // message buffer size (bytes) LoggerManagerConfiguration::LogOrder d_logOrder; // logging order LoggerManagerConfiguration::TriggerMarkers d_triggerMarkers; // trigger markers bslma::Allocator *d_allocator_p; // memory allocator (held, not // owned) // FRIENDS friend class LoggerManager; private: // NOT IMPLEMENTED Logger(const Logger&); Logger& operator=(const Logger&); // PRIVATE CREATORS Logger(const bsl::shared_ptr<Observer>& observer, RecordBuffer *recordBuffer, const UserFieldsPopulatorCallback& userFieldsPopulator, const AttributeCollectorRegistry *attributeCollectors, const PublishAllTriggerCallback& publishAllCallback, int scratchBufferSize, LoggerManagerConfiguration::LogOrder logOrder, LoggerManagerConfiguration::TriggerMarkers triggerMarkers, bslma::Allocator *globalAllocator); // Create a logger having the specified 'observer' that receives // published log records, the specified 'recordBuffer' that stores log // records, the specified 'userFieldsPopulator' that populates the // user-defined fields of log records, the specified // 'attributeCollectors' registry of user-installed attribute // collectors, the specified 'publishAllCallback' that is invoked when // a Trigger-All event occurs, the specified 'scratchBufferSize' for // the internal message buffer accessible via 'obtainMessageBuffer', // and the specified 'globalAllocator' used to supply memory. On a // Trigger or Trigger-All event, the messages are published in the // specified 'logOrder'. Note that this constructor is 'private' since // the creation of instances of 'Logger' is managed by its 'friend' // 'LoggerManager'. ~Logger(); // Destroy this logger. // PRIVATE MANIPULATORS bsl::shared_ptr<Record> getRecordPtr(const char *fileName, int lineNumber); // Return a shared pointer to a modifiable record having the specified // 'fileName' and 'lineNumber' attributes, and retrieved from the // shared object pool managed by this logger. void logMessage(const Category& category, int severity, const bsl::shared_ptr<Record>& record, const ThresholdAggregate& levels); // Log the specified 'record' after setting its category field to the // specified 'category', severity field to the specified 'severity', // and the rest of the fixed fields (except 'fileName', 'lineNumber', // and 'message', all of which are assumed to be already set in // 'record') based on the threshold levels of the specified 'levels'. // (See the component-level documentation of 'ball_record' for more // information on the fields that are logged.) Store the record in the // buffer held by this logger if 'severity' is at least as severe as // the "Record" threshold level of 'levels'. Pass the record directly // to the observers registered with this logger if 'severity' is at // least as severe as the "Pass" threshold level of 'levels'. Publish // the entire contents of the buffer of this logger if 'severity' is at // least as severe as the "Trigger" threshold level of 'levels'. // Publish the entire contents of all buffers of all active loggers if // 'severity' is at least as severe as the "Trigger-All" threshold // level of 'levels' (i.e., via the callback supplied at construction). // Finally, dispose of 'record'. This method has no effect (other than // disposing of 'record') if 'severity' is less severe than each of the // threshold levels of 'levels'. The behavior is undefined unless // 'severity' is in the range '[1 .. 255]' and 'record' was previously // obtained via a call to 'getRecord'. Note that 'record' will be // invalid after this method returns. void publish(Transmission::Cause cause); // Publish to the observer held by this logger all records stored in // the record buffer of this logger and indicate to the observer the // specified publication 'cause'. public: // MANIPULATORS Record *getRecord(const char *fileName, int lineNumber); // Return the address of a modifiable record having the specified // 'fileName' and 'lineNumber' attributes, and retrieved from the // object pool managed by this logger. Note that the returned 'Record' // must subsequently be supplied to a call to the 3-argument // 'logMessage' method on this logger. void logMessage(const Category& category, int severity, const char *fileName, int lineNumber, const char *message); // Log a record containing the specified 'message' text, 'fileName', // 'lineNumber', 'severity', and the name of the specified 'category'. // (See the component-level documentation of 'ball_record' for more // information on the additional fields that are logged.) Store the // record in the buffer held by this logger if 'severity' is at least // as severe as the current "Record" threshold level of 'category'. // Pass the record directly to the observer held by this logger if // 'severity' is at least as severe as the current "Pass" threshold // level of 'category'. Publish the entire contents of the buffer of // this logger if 'severity' is at least as severe as the current // "Trigger" threshold level of 'category'. Publish the entire // contents of all buffers of all active loggers of this logger factory // if 'severity' is at least as severe as the current "Trigger-All" // threshold level of 'category' (i.e., via the callback supplied at // construction). Note that this method will have no effect if // 'severity' is less severe than all of the threshold levels of // 'category'. The behavior is undefined unless 'severity' is in the // range '[1 .. 255]'. void logMessage(const Category& category, int severity, Record *record); // Log the specified '*record' after setting its category attribute to // the name of the specified 'category' and severity attribute to the // specified 'severity'. (See the component-level documentation of // 'ball_record' for more information on the fields that are logged.) // Store the record in the buffer held by this logger if 'severity' is // at least as severe as the current "Record" threshold level of // 'category'. Pass the record directly to the observer held by this // logger if 'severity' is at least as severe as the current "Pass" // threshold level of 'category'. Publish the entire contents of the // buffer of this logger if 'severity' is at least as severe as the // current "Trigger" threshold level of 'category'. Publish the entire // contents of all buffers of all active loggers if 'severity' is at // least as severe as the current "Trigger-All" threshold level of // 'category' (i.e., via the callback supplied at construction). // Finally, dispose of 'record'. This method has no effect (other than // disposing of 'record') if 'severity' is less severe than each of the // threshold levels of 'category'. The behavior is undefined unless // 'severity' is in the range '[1 .. 255]', both 'fileName' and // 'message' are null-terminated, and 'record' was previously obtained // by a call to 'getRecord' on this logger. Note that 'record' will be // invalid after this method returns. #ifndef BDE_OMIT_INTERNAL_DEPRECATED char *messageBuffer(); // Return the address of the modifiable message buffer managed by this // logger. Note that the returned buffer is intended to be used *only* // for formatting log messages immediately before calling 'logMessage'. // // !DEPRECATED!: Use 'obtainMessageBuffer' instead. Do *not* use this // method in multi-threaded code. #endif // BDE_OMIT_INTERNAL_DEPRECATED char *obtainMessageBuffer(bslmt::Mutex **mutex, int *bufferSize); // Block until access to the buffer of this logger used for formatting // messages is available. Return the address of the modifiable buffer // to which this thread of execution has exclusive access, load the // address of the mutex that protects the buffer into the specified // '*mutex' address, and load the size (in bytes) of the buffer into // the specified 'bufferSize' address. The address remains valid, and // the buffer remains locked by this thread of execution, until this // thread calls 'mutex->unlock()'. The behavior is undefined if this // thread of execution currently holds a lock on the buffer. Note that // the buffer is intended to be used *only* for formatting log messages // immediately before calling 'logMessage'; other use may adversely // affect performance for the entire program. bslma::ManagedPtr<char> obtainMessageBuffer(int *bufferSize); // Return a managed pointer that refers to the memory block to which // this thread of execution has exclusive access and load the size (in // bytes) of this buffer into the specified 'bufferSize' address. Note // that this method is intended for *internal* *use* only. void publish(); // Publish to the observer held by this logger all records stored in // the record buffer of this logger and indicate to the observer that // the cause is 'MANUAL_PUBLISH'. void removeAll(); // Remove all log records from the record buffer of this logger. // ACCESSORS int messageBufferSize() const; // Return the size, in bytes, of the message buffer managed by this // logger. int numRecordsInUse() const; // Return a *snapshot* of number of records that have been dispensed by // 'getRecord' but have not yet been supplied (returned) using // 'logRecord'. }; #ifndef BDE_OMIT_INTERNAL_DEPRECATED class LoggerManagerCategoryIter; class LoggerManagerCategoryManip; #endif // BDE_OMIT_INTERNAL_DEPRECATED // =================== // class LoggerManager // =================== class LoggerManager { // This class is *usually* a singleton. It provides a factory for 'Logger' // objects and is also a wrapper for category administration services. // Note that some services provided by this class are available only after // the singleton has been initialized. public: // TYPES typedef LoggerManagerConfiguration::CategoryNameFilterCallback CategoryNameFilterCallback; // 'CategoryNameFilterCallback' is the type of the user-supplied // functor that translates external category names to internal names. typedef LoggerManagerConfiguration::DefaultThresholdLevelsCallback DefaultThresholdLevelsCallback; // 'DefaultThresholdLevelsCallback' is the type of the functor that // determines default threshold levels for categories added to the // registry by the 'setCategory(const char *)' method. typedef Logger::PublishAllTriggerCallback PublishAllTriggerCallback; // 'PublishAllTriggerCallback' is the type of the functor that is // invoked to publish all record buffers of all active loggers (i.e., // loggers allocated by the logger manager that have not yet been // deallocated). typedef LoggerManagerConfiguration::UserFieldsPopulatorCallback UserFieldsPopulatorCallback; // 'UserFieldsPopulatorCallback' is the type of a user-supplied // callback functor used to populate the user-defined fields in each // log record. typedef AttributeCollectorRegistry::Collector AttributeCollector; // 'AttributeCollector' is the type of a user-supplied functor used to // visit a collection of attributes. typedef AttributeCollectorRegistry::Visitor AttributeVisitor; // 'AttributeVisitor' is the type of a user-supplied functor invoked // by an attribute collector for every attribute. typedef BroadcastObserver::ObserverRegistry ObserverRegistry; // This 'typedef' is an alias for the type of the internal broadcast // observer registry. private: // NOT IMPLEMENTED LoggerManager(const LoggerManager&); LoggerManager& operator=(const LoggerManager&); #ifndef BDE_OMIT_INTERNAL_DEPRECATED // FRIENDS friend class LoggerManagerCategoryIter; friend class LoggerManagerCategoryManip; #endif // BDE_OMIT_INTERNAL_DEPRECATED // CLASS DATA static LoggerManager *s_singleton_p; // address of singleton if // initialized; 0 otherwise static bool s_isSingletonOwned; // 'true' by default whereby // 'ball' owns the singleton // and destroys it in // 'shutDownSingleton'; can be // set to 'false' by the // Windows-specific // 'initSingleton' taking an // 'adoptSingleton' flag // DATA const bsl::shared_ptr<BroadcastObserver> d_observer; // internal broadcast // observer CategoryNameFilterCallback d_nameFilter; // category name filter // functor DefaultThresholdLevelsCallback d_defaultThresholds; // functor for obtaining // default threshold levels of // "set" categories mutable bslmt::ReaderWriterMutex d_defaultThresholdsLock; // 'd_defaultThresholdsLock' // protector ThresholdAggregate d_defaultThresholdLevels; // default threshold levels const ThresholdAggregate d_factoryThresholdLevels; // factory default threshold // levels UserFieldsPopulatorCallback d_userFieldsPopulator; // user fields populator // functor ball::AttributeCollectorRegistry d_attributeCollectors;// Registered attribute // collector callbacks Logger *d_logger_p; // holds default logger // (owned) CategoryManager d_categoryManager; // category manager unsigned int d_maxNumCategoriesMinusOne; // one less than the current // capacity of the registry bsl::set<Logger *> d_loggers; // set of *allocated* loggers bslmt::ReaderWriterMutex d_loggersLock; // 'd_loggers' protector RecordBuffer *d_recordBuffer_p; // holds record buffer (owned) PublishAllTriggerCallback d_publishAllCallback; // self-installed callback // functor to publish all // records within process // (always valid) Category *d_defaultCategory_p; // holds *Default* *Category* // (owned) int d_scratchBufferSize; // logger default message // buffer size (bytes) bsl::map<void *, Logger *> d_defaultLoggers; // *registered* loggers bslmt::ReaderWriterMutex d_defaultLoggersLock; // registry lock LoggerManagerConfiguration::LogOrder d_logOrder; // logging order LoggerManagerConfiguration::TriggerMarkers d_triggerMarkers; // trigger markers bslma::Allocator *d_allocator_p; // memory allocator (held, // not owned) // PRIVATE CLASS METHODS static void initSingletonImpl( const LoggerManagerConfiguration& configuration, bslma::Allocator *globalAllocator); // Initialize the logger manager singleton having the specified // 'configuration' of defaults and attributes, and the specified // 'globalAllocator' used to supply memory. If 'globalAllocator' is 0, // the currently installed global allocator is used. This method has // no effect (aside from logging a warning) if the logger manager // singleton already exists. The behavior is undefined unless the lock // guarding the singleton is acquired before calling this method. Note // that 'singletonQLock', an instance of 'bslmt::QLock' local to the // implementation file, enforces exclusive access to 's_singleton_p' // during initialization and shutdown of the logger manager singleton. // PRIVATE CREATORS #ifndef BDE_OMIT_INTERNAL_DEPRECATED LoggerManager(const LoggerManagerConfiguration& configuration, Observer *observer, bslma::Allocator *globalAllocator = 0); // Create a logger manager having the specified 'observer' that // receives published log records and the specified 'configuration' of // defaults and attributes. Optionally specify a 'globalAllocator' // used to supply memory. If 'globalAllocator' is 0, the currently // installed global allocator is used. The behavior is undefined if // 'observer' is 0, goes out of scope, or is otherwise destroyed. Note // that the new logger manager is *not* the singleton logger manager // used by macros of the BALL logging framework. #endif // BDE_OMIT_INTERNAL_DEPRECATED // PRIVATE MANIPULATORS void constructObject(const LoggerManagerConfiguration& configuration); // Construct the default category, default logger members, and record // buffer members of this logger manager based on the specified // 'configuration'. The behavior is undefined if this method is // invoked more than once on this logger manager. void publishAllImp(Transmission::Cause cause); // Transmit to the observers registered with this logger manager all // log records accumulated in the record buffers of all loggers managed // by this logger manager and indicate to the observers the specified // publication 'cause'. public: // CLASS METHODS #ifndef BDE_OMIT_INTERNAL_DEPRECATED static void createLoggerManager( bslma::ManagedPtr<LoggerManager> *manager, Observer *observer, const LoggerManagerConfiguration& configuration, bslma::Allocator *basicAllocator = 0); // Create a logger manager that is *not* the singleton logger manager // having the specified 'observer' that receives published log records // and the specified 'configuration' of defaults and attributes; load // the newly-created logger manager into the specified 'manager' // managed pointer. Optionally specify a 'basicAllocator' used to // supply memory. If 'basicAllocator' is 0, the currently installed // default allocator is used. The behavior is undefined if 'observer' // is 0, goes out of scope, or is otherwise destroyed. Note that this // method does *not* create the singleton logger manager used by the // macros of the BALL logging framework. // // !DEPRECATED!: Use the 'createLoggerManager' method that does not // take a *raw* pointer to an 'observer', together with the // 'registerObserver' method (which takes a *shared* pointer to an // 'observer'), instead. #endif // BDE_OMIT_INTERNAL_DEPRECATED static void createLoggerManager( bslma::ManagedPtr<LoggerManager> *manager, const LoggerManagerConfiguration& configuration, bslma::Allocator *basicAllocator = 0); // Create a logger manager that is *not* the singleton logger manager // having the specified 'configuration' of defaults and attributes; // load the newly-created logger manager into the specified 'manager' // managed pointer. Optionally specify a 'basicAllocator' used to // supply memory. If 'basicAllocator' is 0, the currently installed // default allocator is used. Note that this method does *not* create // the singleton logger manager used by the macros of the BALL logging // framework. static Record *getRecord(const char *fileName, int lineNumber); // Return the address of a modifiable record with the specified // 'fileName' and 'lineNumber' attributes, and whose memory is supplied // by the currently installed default allocator. Note that the // returned 'Record' must subsequently be supplied to a call to the // 'LoggerManager::logMessage' method. #ifndef BDE_OMIT_INTERNAL_DEPRECATED static LoggerManager& initSingleton( Observer *observer, bslma::Allocator *globalAllocator = 0); static LoggerManager& initSingleton( Observer *observer, const LoggerManagerConfiguration& configuration, bslma::Allocator *globalAllocator = 0); // Initialize the logger manager singleton having the specified // 'observer' that receives published log records. Optionally specify // a 'configuration' describing how the singleton should be configured. // If 'configuration' is not specified, a default constructed // 'LoggerManagerConfiguration' object is used. Optionally specify a // 'globalAllocator' used to supply memory. If 'globalAllocator' is 0, // the currently installed global allocator is used. Return a // non-'const' reference to the logger manager singleton. This method // has no effect (aside from logging a warning) if the logger manager // singleton already exists. The behavior is undefined if 'observer' // is 0, goes out of scope, or is otherwise destroyed. // // !DEPRECATED!: Use the 'initSingleton' method that does not take a // *raw* pointer to an 'observer', together with the 'registerObserver' // method (which takes a *shared* pointer to an 'observer'), instead. #endif // BDE_OMIT_INTERNAL_DEPRECATED static LoggerManager& initSingleton(bslma::Allocator *globalAllocator = 0); static LoggerManager& initSingleton( const LoggerManagerConfiguration& configuration, bslma::Allocator *globalAllocator = 0); // Initialize the logger manager singleton. Optionally specify a // 'configuration' describing how the singleton should be configured. // If 'configuration' is not specified, a default constructed // 'LoggerManagerConfiguration' object is used. Optionally specify a // 'globalAllocator' used to supply memory. If 'globalAllocator' is 0, // the currently installed global allocator is used. Return a // non-'const' reference to the logger manager singleton. This method // has no effect (aside from logging a warning) if the logger manager // singleton already exists. static int initSingleton(LoggerManager *singleton, bool adoptSingleton = false); // Initialize the logger manager singleton with the specified // 'singleton'. Optionally specify an 'adoptSingleton' flag indicating // whether this method takes ownership of 'singleton', in which case // 'shutDownSingleton' will destroy 'singleton'. If 'adoptSingleton' // is not specified, this method does *not* take ownership of // 'singleton' (and 'shutDownSingleton' will not destroy it). Return 0 // if the logger manager singleton was successfully initialized, and a // non-zero value otherwise. This method has no effect (aside from // logging a warning) if the logger manager singleton already exists, // in which case this method does *not* take ownership of 'singleton' // regardless of the value of 'adoptSingleton'. Note that this version // of 'initSingleton' is meant for use *only* on Windows to initialize // another dynamically loaded copy of the 'LoggerManager' system. Also // note that a suitable singleton may be obtained by calling // 'createLoggerManager', or from the 'singleton' class method of an // already-initialized 'LoggerManager' system. static bool isInitialized(); // Return 'true' if the logger manager singleton exists, and 'false' // otherwise. static void logMessage(int severity, Record *record); // Publish the specified 'record' using // 'bsls::Log::platformDefaultMessageHandler' after setting its // severity attribute to the specified 'severity', and dispose of // 'record'. The behavior is undefined unless 'record' was obtained by // a call to the 'LoggerManager::getRecord' method. Note that 'record' // will be invalid after this method returns. static char *obtainMessageBuffer(bslmt::Mutex **mutex, int *bufferSize); // Block until access to the static buffer used for formatting messages // is available. Return the address of the modifiable buffer to which // this thread of execution has exclusive access, load the address of // the mutex that protects the buffer into the specified '*mutex' // address, and load the size (in bytes) of the buffer into the // specified 'bufferSize' address. The address remains valid, and the // buffer remains locked by this thread of execution, this thread calls // 'mutex->unlock()'. The behavior is undefined if this thread of // execution currently holds a lock on the buffer. Note that the // buffer is intended to be used *only* for formatting log messages // immediately before calling 'logMessage'; other use may adversely // affect performance for the entire program. static bslma::ManagedPtr<char> obtainMessageBuffer(int *bufferSize); // Return a managed pointer that refers to the memory block to which // this thread of execution has exclusive access and load the size (in // bytes) of this buffer into the specified 'bufferSize' address. Note // that this method is intended for *internal* *use* only. static void shutDownSingleton(); // Destroy the logger manager singleton and release all resources used // by it. This method has no effect if the logger manager singleton // does not exist (i.e., it has not been initialized or has already // been destroyed). The behavior is undefined if this method is called // from one thread while another thread is accessing the logger manager // singleton (i.e., this method is *not* thread-safe). static LoggerManager& singleton(); // Return a non-'const' reference to the logger manager singleton. The // behavior is undefined unless the logger manager singleton exists. // CREATORS explicit LoggerManager( const LoggerManagerConfiguration& configuration, bslma::Allocator *globalAllocator = 0); // Create a logger manager having the specified 'configuration' of // defaults and attributes. Optionally specify a 'globalAllocator' // used to supply memory. If 'globalAllocator' is 0, the currently // installed global allocator is used. Note that the new logger // manager is *not* the singleton logger manager used by macros of the // BALL logging framework. ~LoggerManager(); // Destroy this logger manager. // MANIPULATORS // Logger Management Logger *allocateLogger(RecordBuffer *buffer); Logger *allocateLogger(RecordBuffer *buffer, int scratchBufferSize); // Return the address of a modifiable logger managed by this logger // manager configured with the specified record 'buffer'. Optionally // specify a 'scratchBufferSize' for the logger's user-accessible // message buffer. If 'scratchBufferSize' is not specified, the value // configured at construction is used. Note that this method is // primarily intended for use in multi-threaded applications, but can // be used to partition logging streams even within a single thread. // Also note that ownership of 'buffer' is *not* transferred, and // hence, will *not* be destroyed (or otherwise affected) after the // logger is deallocated. #ifndef BDE_OMIT_INTERNAL_DEPRECATED Logger *allocateLogger(RecordBuffer *buffer, Observer *observer); Logger *allocateLogger(RecordBuffer *buffer, int scratchBufferSize, Observer *observer); // Return the address of a modifiable logger managed by this logger // manager having the specified 'observer' that receives published log // records and configured with the specified record 'buffer'. // Optionally specify a 'scratchBufferSize' for the logger's // user-accessible message buffer. If 'scratchBufferSize' is not // specified, the value configured at construction is used. Note that // this method is primarily intended for use in multi-threaded // applications, but can be used to partition logging streams even // within a single thread. Also note that ownership of 'buffer' and // 'observer' is *not* transferred, and hence, will *not* be destroyed // (or otherwise affected) after the logger is deallocated. // // !DEPRECATED!: Use the 'allocateLogger' method that does not take a // *raw* pointer to an 'observer', together with the 'registerObserver' // method (which takes a *shared* pointer to an 'observer'), instead. #endif // BDE_OMIT_INTERNAL_DEPRECATED Logger *allocateLogger(RecordBuffer *buffer, const bsl::shared_ptr<Observer>& observer); Logger *allocateLogger(RecordBuffer *buffer, int scratchBufferSize, const bsl::shared_ptr<Observer>& observer); // Return the address of a modifiable logger managed by this logger // manager having the specified 'observer' that receives published log // records and configured with the specified record 'buffer'. // Optionally specify a 'scratchBufferSize' for the logger's // user-accessible message buffer. If 'scratchBufferSize' is not // specified, the value configured at construction is used. Note that // this method is primarily intended for use in multi-threaded // applications, but can be used to partition logging streams even // within a single thread. Also note that ownership of 'buffer' and // 'observer' is *not* transferred, and hence, will *not* be destroyed // (or otherwise affected) after the logger is deallocated. void deallocateLogger(Logger *logger); // Deallocate the specified 'logger' and release 'logger' from // management by this logger manager. The behavior is undefined unless // 'logger' was obtained by invoking the 'allocateLogger' method of // this logger manager and 'logger' has not yet been deallocated. Note // that this method is intended primarily for multi-threaded // applications. Logger& getLogger(); // Return a non-'const' reference to a logger managed by this logger // manager suitable for performing logging operations for this thread // of execution. void setLogger(Logger *logger); // Set the default logger used by this thread of execution to the // specified 'logger', or to the global default logger if 'logger' is // 0. The behavior is undefined unless 'logger' was obtained from this // logger manager, and this thread of execution does not hold exclusive // access to the record buffer of its current default logger. // Category Management Category *addCategory(const char *categoryName, int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel); // Add to the category registry of this logger manager a new category // having the specified 'categoryName' and the specified 'recordLevel', // 'passLevel', 'triggerLevel', and 'triggerAllLevel' threshold levels, // respectively, if (1) 'categoryName' is not present in the registry, // (2) the number of categories in the registry is less than the // registry capacity, and (3) each threshold level is in the range // '[0 .. 255]'. Return the address of the new modifiable category on // success, and 0 otherwise. The behavior is undefined unless // 'categoryName' is null-terminated. Category& defaultCategory(); // Return a non-'const' reference to the *Default* *Category* in the // category registry of this logger manager. Category *lookupCategory(const char *categoryName); // Return the address of the modifiable category in the category // registry of this logger manager having the specified 'categoryName', // or 0 if no such category exists. The behavior is undefined unless // 'categoryName' is null-terminated. const Category *setCategory(const char *categoryName); // Add to the category registry of this logger manager a new category // having the specified 'categoryName' and default threshold levels if // 'categoryName' is not present in the registry and the number of // categories in the registry is less than the registry capacity. // Return the address of the (possibly newly-created) non-modifiable // category having 'categoryName', if such a category exists, and the // address of the non-modifiable *Default* *Category* otherwise. The // behavior is undefined unless 'categoryName' is null-terminated. // Note that a valid category address is *always* returned. const Category *setCategory(CategoryHolder *categoryHolder, const char *categoryName); // Add to the category registry of this logger manager a new category // having the specified 'categoryName' and default threshold levels if // 'categoryName' is not present in the registry and the number of // categories in the registry is less than the registry capacity. // Return the address of the (possibly newly-created) non-modifiable // category having 'categoryName', if such a category exists, and the // address of the non-modifiable *Default* *Category* otherwise. If // the specified 'categoryHolder' is non-null, then also load into // 'categoryHolder' the returned category and its maximum level and // link 'categoryHolder' to the category if it has not yet been linked. // The behavior is undefined unless 'categoryName' is null-terminated. // Note that a valid category address is *always* returned. Category *setCategory(const char *categoryName, int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel); // Add to the category registry of this logger manager a new category // having the specified 'categoryName' and 'recordLevel', 'passLevel', // 'triggerLevel', and 'triggerAllLevel' threshold levels, // respectively, if (1) 'categoryName' is not present in the registry, // (2) the number of categories in the registry is less than the // registry capacity, and (3) 'recordLevel', 'passLevel', // 'triggerLevel', and 'triggerAllLevel' are all within the range // '[0 .. 255]'. If 'categoryName' is already present and each // threshold level is within the valid range then reset the threshold // levels of 'categoryName' to the specified values. Return the // address of the (possibly newly-created) modifiable category having // 'categoryName' if 'categoryName' was either created or its // thresholds reset, and 0 otherwise. The behavior is undefined unless // 'categoryName' is null-terminated. Note that 0, and *not* the // *Default* *Category*, is returned on failure. void setMaxNumCategories(int length); // Set the capacity of the category registry of this logger manager to // the specified 'length'. If 'length' is 0, no limit will be imposed. // No categories are removed from the registry if the current number of // categories exceeds 'length'. However, subsequent attempts to add // categories to the registry will fail. The behavior is undefined // unless '0 <= length'. // Observer Management void deregisterAllObservers(); // Remove all observers from the registry of observers maintained by // this logger manager. int deregisterObserver(const bsl::string_view& observerName); // Remove the observer having the specified 'observerName' from the // registry of observers maintained by this logger manager. Return 0 // if the observer having 'observerName' was successfully deregistered // from this logger manager, and a non-zero value (with no effect) // otherwise. Henceforth, the observer that had 'observerName' will no // longer receive log records published by this logger manager. int deregisterAttributeCollector(const bsl::string_view& collectorName); // Remove the attribute collector having the specified 'collectorName' // from the registry of collectors maintained by this logger manager. // Return 0 if the collector having 'collectorName' was successfully // deregistered from this logger manager, and a non-zero value (with no // effect) otherwise. bsl::shared_ptr<Observer> findObserver( const bsl::string_view& observerName); // Return a shared pointer to the observer having the specified // 'observerName' in the registry of this logger manager, and an empty // shared pointer if there is no such observer otherwise. template <class t_OBSERVER> int findObserver(bsl::shared_ptr<t_OBSERVER> *result, const bsl::string_view& observerName); // Load into the specified 'result' a shared pointer to the observer of // (template parameter) 't_OBSERVER' type having the specified // 'observerName' in the registry of this logger manager, and an empty // shared pointer if there is no such observer otherwise. Return 0 if // a non-empty shared pointer was loaded, and a non-zero value // otherwise. Note that an empty shared pointer will be loaded if // either no observer having 'observerName' is in the registry or the // observer registered with that name is not of 't_OBSERVER' type. #ifndef BDE_OMIT_INTERNAL_DEPRECATED Observer *observer(); // Return the address of the modifiable legacy observer registered with // this logger manager. // // !DEPRECATED!: Use 'findObserver' instead. #endif // BDE_OMIT_INTERNAL_DEPRECATED int registerObserver(const bsl::shared_ptr<Observer>& observer, const bsl::string_view& observerName); // Add the specified 'observer' with the specified 'observerName' to // the registry of observers maintained by this logger manager. Return // 0 if 'observer' was successfully registered with this logger // manager, and a non-zero value (with no effect) otherwise. // Henceforth, all log records published by this logger manager are // published to this observer, until 'observer' is deregistered. The // behavior is undefined if a cyclic reference is created among // registered observers. Note that this method will fail if an // observer having 'observerName' is already registered. int registerAttributeCollector(const AttributeCollector& collector, const bsl::string_view& collectorName); // Add the specified 'collector' with the specified 'collectorName' to // the registry of attribute collectors maintained by this logger // manager. Return 0 if 'collector' was successfully registered with // this logger manager, and a non-zero value (with no effect) // otherwise. Note that this method will fail if a collector having // 'collectorName' is already registered. // Threshold Level Management Manipulators void resetDefaultThresholdLevels(); // Reset the default threshold levels of this logger manager to the // original "factory-supplied" default values or the factory overrides // supplied at construction. void setCategoryThresholdsToCurrentDefaults(Category *category); // Set the threshold levels of the specified 'category' in the category // registry of this logger manager to the current default threshold // values. The behavior is undefined unless 'category' is non-null. void setCategoryThresholdsToFactoryDefaults(Category *category); // Set the threshold levels of the specified 'category' in the category // registry of this logger manager to the original "factory-supplied" // default values or the factory overrides supplied at construction. // The behavior is undefined unless 'category' is non-null. int setDefaultThresholdLevels(int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel); // Set the default threshold levels of this logger manager to the // specified 'recordLevel', 'passLevel', 'triggerLevel', and // 'triggerAllLevel' values, respectively, if each threshold level is // in the range '[0 .. 255]'. Return 0 on success, and a non-zero // value otherwise (with no effect on any default threshold level). void setDefaultThresholdLevelsCallback( DefaultThresholdLevelsCallback *callback); // Set the default-thresholds callback of this logger manager to the // specified 'callback'. The default-thresholds callback is used to // determine default threshold levels for categories added to the // registry by the 'setCategory(const char *)' method. // Rule Management int addRule(const Rule& value); // Add a rule having the specified 'value' to the set of (unique) // rules maintained by this object. Return the number of rules added // (i.e., 1 on success and 0 if a rule with the same value is already // present). int addRules(const RuleSet& ruleSet); // Add each rule in the specified 'ruleSet' to the set of (unique) // rules maintained by this object. Return the number of rules added. // Note that each rule having the same value as an existing rule will // be ignored. void removeAllRules(); // Remove every rule from the set of rules maintained by this object. int removeRule(const Rule& value); // Remove the rule having the specified 'value' from this set of rules // maintained by this object. Return the number of rules removed // (i.e., 1 on success and 0 if no rule having the same value is // found.) int removeRules(const RuleSet& ruleSet); // Remove each rule in the specified 'ruleSet' from this set of rules // maintained by this object. Return the number of rules removed. // Miscellaneous void publishAll(); // Transmit to the observers registered with this logger manager all // log records accumulated in the record buffers of all loggers managed // by this logger manager, and indicate the publication cause to be // 'MANUAL_PUBLISH_ALL'. template <class t_CATEGORY_VISITOR> void visitCategories(const t_CATEGORY_VISITOR& visitor); // Invoke the specified 'visitor' functor on each category managed by // this object, providing that functor modifiable access to each // category. 'visitor' must be a functor that can be called as if it // had the following signature: //.. // void operator()(Category *); //.. template <class t_OBSERVER_VISITOR> void visitObservers( BSLS_COMPILERFEATURES_FORWARD_REF(t_OBSERVER_VISITOR) visitor); // Invoke the specified 'visitor' functor of (template parameter) // 't_OBSERVER_VISITOR' type on each element in the registry of this // logger manager, supplying that functor modifiable access to each // observer. 'visitor' must be a functor that can be called as if it // had the following signature: //.. // void operator()(const bsl::shared_ptr<Observer>& observer, // const bsl::string_view& observerName); //.. // ACCESSORS bslma::Allocator *allocator() const; // Return the address of the modifiable allocator held by this logger // manager. const Category& defaultCategory() const; // Return a 'const' reference to the *Default* *Category* in the // category registry of this logger manager. int defaultPassThresholdLevel() const; // Return the default pass threshold level of this logger manager. int defaultRecordThresholdLevel() const; // Return the default record threshold level of this logger manager. int defaultTriggerAllThresholdLevel() const; // Return the default trigger-all threshold level of this logger // manager. int defaultTriggerThresholdLevel() const; // Return the default trigger threshold level of this logger manager. bsl::shared_ptr<const Observer> findObserver( const bsl::string_view& observerName) const; // Return a shared pointer to the observer having the specified // 'observerName' in the registry of this logger manager, and an empty // shared pointer if there is no such observer otherwise. template <class t_OBSERVER> int findObserver(bsl::shared_ptr<const t_OBSERVER> *result, const bsl::string_view& observerName) const; // Load into the specified 'result' a shared pointer to the observer of // (template parameter) 't_OBSERVER' type having the specified // 'observerName' in the registry of this logger manager, and an empty // shared pointer if there is no such observer otherwise. Return 0 if // a non-empty shared pointer was loaded, and a non-zero value // otherwise. Note that an empty shared pointer will be loaded if // either no observer having 'observerName' is in the registry or the // observer registered with that name is not of 't_OBSERVER' type. bool isCategoryEnabled(const Category *category, int severity) const; // Return 'true' if the specified 'severity' is more severe (i.e., is // numerically less than) at least one of the threshold levels of the // specified 'category', and 'false' otherwise. If the returned // 'value' is 'false', then a subsequent call to // 'getLogger().logMessage()' (with 'category' and 'severity') will // have no effect. This method compares 'severity' with the threshold // levels determined by combining 'category->thresholdLevels()' with // the thresholds provided by any relevant and active logging rules (in // 'ruleSet()') that apply to 'category'. Note that a rule applies to // 'category' if the rule's pattern matches 'category->categoryName()', // and a rule is active if all the predicates defined for that rule are // satisfied by the current thread's attributes (i.e., // 'Rule::evaluate()' returns 'true' for the collection of attributes // maintained by the current thread's 'AttributeContext' object). const Category *lookupCategory(const char *categoryName) const; // Return the address of the non-modifiable category in the category // registry of this logger manager having the specified 'categoryName', // or 0 if no such category exists. The behavior is undefined unless // 'categoryName' is null-terminated. int maxNumCategories() const; // Return the current capacity of the category registry of this logger // manager. A capacity of 0 implies that no limit will be imposed; // otherwise, new categories may be added only if // 'numCategories() < maxNumCategories()'. Note that // '0 < maxNumCategories() < numCategories()' *is* a valid state, // implying no new categories may be added. int numCategories() const; // Return the number of categories in the category registry of this // logger manager. #ifndef BDE_OMIT_INTERNAL_DEPRECATED const Observer *observer() const; // Return the address of the non-modifiable observer registered with // this logger manager. // // !DEPRECATED!: Use 'findObserver' instead. #endif // BDE_OMIT_INTERNAL_DEPRECATED const RuleSet& ruleSet() const; // Return a 'const' reference to the rule set maintained by this // object. const UserFieldsPopulatorCallback *userFieldsPopulatorCallback() const; // Return the address of the non-modifiable user populator functor // registered with this logger manager, or 0 if there is no registered // user populator functor. template <class t_CATEGORY_VISITOR> void visitCategories(const t_CATEGORY_VISITOR& visitor) const; // Invoke the specified 'visitor' functor on each category managed by // this object, providing that functor non-modifiable access to each // category. 'visitor' must be a functor that can be called as if it // had the following signature: //.. // void operator()(const Category *); //.. template <class t_OBSERVER_VISITOR> void visitObservers( BSLS_COMPILERFEATURES_FORWARD_REF(t_OBSERVER_VISITOR) visitor) const; // Invoke the specified 'visitor' functor of (template parameter) // 't_OBSERVER_VISITOR' type on each element in the registry of this // logger manager, supplying that functor modifiable access to each // observer. 'visitor' must be a functor that can be called as if it // had the following signature: //.. // void operator()(const bsl::shared_ptr<Observer>& observer, // const bsl::string_view& observerName); //.. // Threshold Level Management Accessors const ThresholdAggregate& defaultThresholdLevels() const; // Return the default threshold levels associated with this logger // manager object. int thresholdLevelsForNewCategory(ThresholdAggregate *levels, const char *categoryName) const; // Load into the specified '*levels' the threshold levels that would be // set for a newly created category, irrespective of whether a category // with the specified 'categoryName' is already in the registry. // Return 0 on success and a non-zero value otherwise. If the client // has configured a default threshold levels callback (see // 'ball::LoggerManager::DefaultThresholdLevelsCallback' in the // component doc), the 'categoryName' will be supplied to that callback // which will set '*levels'. Otherwise, if no default threshold levels // callback has been provided, the default threshold levels are used. // Note that this function will report an error if the callback returns // invalid levels. Also note that if a category named 'categoryName' // is already in the registry, the levels returned by this method may // differ from the levels of that category. }; // ============================== // class LoggerManagerScopedGuard // ============================== class LoggerManagerScopedGuard { // This class implements a scoped guard that, on construction, creates the // logger manager singleton, and, on destruction, destroys the singleton. private: // NOT IMPLEMENTED LoggerManagerScopedGuard(const LoggerManagerScopedGuard&); LoggerManagerScopedGuard& operator=(const LoggerManagerScopedGuard&); public: // CREATORS #ifndef BDE_OMIT_INTERNAL_DEPRECATED LoggerManagerScopedGuard( Observer *observer, const LoggerManagerConfiguration& configuration, bslma::Allocator *globalAllocator = 0); // Create a scoped guard that will create the logger manager singleton // having the specified 'observer' that receives published log records // and the specified 'configuration' of defaults and attributes. // Optionally specify a 'globalAllocator' used to supply memory. If // 'globalAllocator' is 0, the currently installed global allocator is // used. This method has no effect (aside from logging a warning) if // the logger manager singleton already exists. The behavior is // undefined if 'observer' is 0, goes out of scope, or is otherwise // destroyed. Note that on destruction, this scoped guard will destroy // the logger manager singleton, if the singleton exists at that time. // // !DEPRECATED!: Use the 'LoggerManagerScopedGuard' constructor that // does not take a *raw* pointer to an 'observer', together with the // 'registerObserver' method (which takes a *shared* pointer to an // 'observer'), instead. #endif // BDE_OMIT_INTERNAL_DEPRECATED explicit LoggerManagerScopedGuard( const LoggerManagerConfiguration& configuration, bslma::Allocator *globalAllocator = 0); // Create a scoped guard that will create the logger manager singleton // having the specified 'configuration' of defaults and attributes. // Optionally specify a 'globalAllocator' used to supply memory. If // 'globalAllocator' is 0, the currently installed global allocator is // used. This method has no effect (aside from logging a warning) if // the logger manager singleton already exists. Note that on // destruction, this scoped guard will destroy the logger manager // singleton, if the singleton exists at that time. ~LoggerManagerScopedGuard(); // Destroy the logger manager singleton, if the singleton exists, and // destroy this scoped guard. }; #ifndef BDE_OMIT_INTERNAL_DEPRECATED // =============================== // class LoggerManagerCategoryIter // =============================== class LoggerManagerCategoryIter { // This class defines an iterator providing sequential, read-only access to // the categories in the registry of a logger manager's category manager. // The order of the iteration is undefined. // // !DEPRECATED!: Use the 'LoggerManager::visitCategories' accessor instead. // DATA CategoryManagerIter d_iter; // contained category manager iterator private: // NOT IMPLEMENTED LoggerManagerCategoryIter(const LoggerManagerCategoryIter&); LoggerManagerCategoryIter& operator=(const LoggerManagerCategoryIter&); public: // CREATORS explicit LoggerManagerCategoryIter(const LoggerManager& loggerManager); // Create an iterator providing non-modifiable access to the categories // maintained by the specified 'loggerManager' that is initialized to // refer to the first category in the sequence of categories in the // registry of 'loggerManager', if one exists, and is initialized to be // invalid otherwise. The order of iteration is undefined. The // behavior is undefined unless the lifetime of 'loggerManager' is at // least as long as the lifetime of this iterator. //! ~LoggerManagerCategoryIter() = default; // Destroy this iterator. // MANIPULATORS void operator++(); // Advance this iterator to refer to the next unvisited category. If // no such category exists, this iterator becomes invalid. The // behavior is undefined unless this iterator is initially valid. Note // that the order of iteration is undefined. // ACCESSORS operator const void *() const; // Return a non-zero value if this iterator is valid, and 0 otherwise. const Category& operator()() const; // Return a 'const' reference to the category currently referred to by // this iterator. The behavior is undefined unless this iterator is // valid. }; // ================================ // class LoggerManagerCategoryManip // ================================ class LoggerManagerCategoryManip { // This class defines an iterator providing sequential, modifiable access // to the categories in the registry of a logger manager's category // manager. The order of the iteration is undefined. // // !DEPRECATED!: Use the 'LoggerManager::visitCategories' manipulator // instead. // DATA CategoryManagerManip d_manip; // contained category manager iterator private: // NOT IMPLEMENTED LoggerManagerCategoryManip(const LoggerManagerCategoryManip&); LoggerManagerCategoryManip& operator=(const LoggerManagerCategoryManip&); public: // CREATORS explicit LoggerManagerCategoryManip(LoggerManager *loggerManager); // Create an iterator providing modifiable access to the categories // maintained by the specified 'loggerManager' that is initialized to // refer to the first category in the sequence of categories in the // registry of 'loggerManager', if one exists, and is initialized to be // invalid otherwise. The order of iteration is undefined. The // behavior is undefined unless the lifetime of 'loggerManager' is at // least as long as the lifetime of this iterator. //! ~LoggerManagerCategoryManip() = default; // Destroy this iterator. // MANIPULATORS void advance(); // Advance this iterator to refer to the next unvisited category. If // no such category exists, this iterator becomes invalid. The // behavior is undefined unless this iterator is initially valid. Note // that the order of iteration is undefined. Category& operator()(); // Return a non-'const' reference to the category currently referred to // by this iterator. The behavior is undefined unless this iterator is // valid. // ACCESSORS operator const void *() const; // Return a non-zero value if this iterator is valid, and 0 otherwise. }; #endif // BDE_OMIT_INTERNAL_DEPRECATED // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ------------ // class Logger // ------------ // MANIPULATORS #ifndef BDE_OMIT_INTERNAL_DEPRECATED inline char *Logger::messageBuffer() { return d_scratchBuffer_p; } #endif // BDE_OMIT_INTERNAL_DEPRECATED inline void Logger::publish() { publish(Transmission::e_MANUAL_PUBLISH); } inline void Logger::removeAll() { d_recordBuffer_p->removeAll(); } // ACCESSORS inline int Logger::messageBufferSize() const { return d_scratchBufferSize; } inline int Logger::numRecordsInUse() const { return d_recordPool.numObjects() - d_recordPool.numAvailableObjects(); } // ------------------- // class LoggerManager // ------------------- // CLASS METHODS inline bool LoggerManager::isInitialized() { return (LoggerManager *)0 != s_singleton_p; } inline LoggerManager& LoggerManager::singleton() { return *s_singleton_p; } // MANIPULATORS // Category Management inline Category& LoggerManager::defaultCategory() { return *d_defaultCategory_p; } inline const Category *LoggerManager::setCategory(const char *categoryName) { return setCategory(0, categoryName); } // Observer Management inline void LoggerManager::deregisterAllObservers() { d_observer->deregisterAllObservers(); } inline int LoggerManager::deregisterObserver(const bsl::string_view& observerName) { return d_observer->deregisterObserver(observerName); } inline int LoggerManager::deregisterAttributeCollector( const bsl::string_view& collectorName) { return d_attributeCollectors.removeCollector(collectorName); } inline bsl::shared_ptr<Observer> LoggerManager::findObserver(const bsl::string_view& observerName) { return d_observer->findObserver(observerName); } template <class t_OBSERVER> inline int LoggerManager::findObserver(bsl::shared_ptr<t_OBSERVER> *result, const bsl::string_view& observerName) { return d_observer->findObserver(result, observerName); } inline int LoggerManager::registerObserver(const bsl::shared_ptr<Observer>& observer, const bsl::string_view& observerName) { return d_observer->registerObserver(observer, observerName); } inline int LoggerManager::registerAttributeCollector( const AttributeCollector& collector, const bsl::string_view& collectorName) { return d_attributeCollectors.addCollector(collector, collectorName); } // Threshold Level Management inline void LoggerManager::resetDefaultThresholdLevels() { d_defaultThresholdLevels = d_factoryThresholdLevels; } // Rule Management inline int LoggerManager::addRule(const Rule& value) { return d_categoryManager.addRule(value); } inline int LoggerManager::addRules(const RuleSet& ruleSet) { return d_categoryManager.addRules(ruleSet); } inline void LoggerManager::removeAllRules() { d_categoryManager.removeAllRules(); } inline int LoggerManager::removeRule(const Rule& value) { return d_categoryManager.removeRule(value); } inline int LoggerManager::removeRules(const RuleSet& ruleSet) { return d_categoryManager.removeRules(ruleSet); } // Miscellaneous inline void LoggerManager::publishAll() { publishAllImp(Transmission::e_MANUAL_PUBLISH_ALL); } template <class t_CATEGORY_VISITOR> inline void LoggerManager::visitCategories(const t_CATEGORY_VISITOR& visitor) { d_categoryManager.visitCategories(visitor); } template <class t_OBSERVER_VISITOR> inline void LoggerManager::visitObservers( BSLS_COMPILERFEATURES_FORWARD_REF(t_OBSERVER_VISITOR) visitor) { d_observer->visitObservers( BSLS_COMPILERFEATURES_FORWARD(t_OBSERVER_VISITOR, visitor)); } // ACCESSORS inline bslma::Allocator *LoggerManager::allocator() const { return d_allocator_p; } inline const Category& LoggerManager::defaultCategory() const { return *d_defaultCategory_p; } inline int LoggerManager::defaultPassThresholdLevel() const { return d_defaultThresholdLevels.passLevel(); } inline int LoggerManager::defaultRecordThresholdLevel() const { return d_defaultThresholdLevels.recordLevel(); } inline int LoggerManager::defaultTriggerAllThresholdLevel() const { return d_defaultThresholdLevels.triggerAllLevel(); } inline int LoggerManager::defaultTriggerThresholdLevel() const { return d_defaultThresholdLevels.triggerLevel(); } inline bsl::shared_ptr<const Observer> LoggerManager::findObserver(const bsl::string_view& observerName) const { const BroadcastObserver *observerPtr = d_observer.get(); return observerPtr->findObserver(observerName); } template <class t_OBSERVER> inline int LoggerManager::findObserver( bsl::shared_ptr<const t_OBSERVER> *result, const bsl::string_view& observerName) const { const BroadcastObserver *observerPtr = d_observer.get(); return observerPtr->findObserver(result, observerName); } inline int LoggerManager::maxNumCategories() const { return static_cast<int>(d_maxNumCategoriesMinusOne) + 1; } inline int LoggerManager::numCategories() const { return d_categoryManager.length(); } inline const RuleSet& LoggerManager::ruleSet() const { return d_categoryManager.ruleSet(); } template <class t_CATEGORY_VISITOR> inline void LoggerManager::visitCategories(const t_CATEGORY_VISITOR& visitor) const { d_categoryManager.visitCategories(visitor); } template <class t_OBSERVER_VISITOR> inline void LoggerManager::visitObservers( BSLS_COMPILERFEATURES_FORWARD_REF(t_OBSERVER_VISITOR) visitor) const { d_observer->visitObservers( BSLS_COMPILERFEATURES_FORWARD(t_OBSERVER_VISITOR, visitor)); } // ------------------------------ // class LoggerManagerScopedGuard // ------------------------------ // CREATORS #ifndef BDE_OMIT_INTERNAL_DEPRECATED inline LoggerManagerScopedGuard::LoggerManagerScopedGuard( Observer *observer, const LoggerManagerConfiguration& configuration, bslma::Allocator *globalAllocator) { LoggerManager::initSingleton(observer, configuration, globalAllocator); } #endif // BDE_OMIT_INTERNAL_DEPRECATED inline LoggerManagerScopedGuard::LoggerManagerScopedGuard( const LoggerManagerConfiguration& configuration, bslma::Allocator *globalAllocator) { LoggerManager::initSingleton(configuration, globalAllocator); } inline LoggerManagerScopedGuard::~LoggerManagerScopedGuard() { LoggerManager::shutDownSingleton(); } #ifndef BDE_OMIT_INTERNAL_DEPRECATED // ------------------------------- // class LoggerManagerCategoryIter // ------------------------------- // CREATORS inline LoggerManagerCategoryIter::LoggerManagerCategoryIter( const LoggerManager& loggerManager) : d_iter(loggerManager.d_categoryManager) { } // MANIPULATORS inline void LoggerManagerCategoryIter::operator++() { ++d_iter; } // ACCESSORS inline LoggerManagerCategoryIter::operator const void *() const { return d_iter; } inline const Category& LoggerManagerCategoryIter::operator()() const { return d_iter(); } // -------------------------------- // class LoggerManagerCategoryManip // -------------------------------- // CREATORS inline LoggerManagerCategoryManip::LoggerManagerCategoryManip( LoggerManager *loggerManager) : d_manip(&loggerManager->d_categoryManager) { } // MANIPULATORS inline void LoggerManagerCategoryManip::advance() { d_manip.advance(); } inline Category& LoggerManagerCategoryManip::operator()() { return d_manip(); } // ACCESSORS inline LoggerManagerCategoryManip::operator const void *() const { return d_manip; } #endif // BDE_OMIT_INTERNAL_DEPRECATED } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2017 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------