Provide a thread-safe observer that emits log records to a file.
More...
Namespaces |
namespace | ball |
Detailed Description
- Outline
-
-
- Purpose:
- Provide a thread-safe observer that emits log records to a file.
-
- Classes:
-
- See also:
- Component ball_record, Component ball_context, Component ball_observer, Component ball_recordstringformatter
-
- Description:
- This component provides a concrete implementation of the
ball::Observer
protocol, ball::FileObserver2
, for publishing log records to a user-specified file. The following inheritance hierarchy diagram shows the classes involved and their methods: ,-------------------.
( ball::FileObserver2 )
`-------------------'
| ctor
| disableFileLogging
| disableTimeIntervalRotation
| disableSizeRotation
| disablePublishInLocalTime
| enableFileLogging
| enablePublishInLocalTime
| forceRotation
| rotateOnSize
| rotateOnTimeInterval
| setLogFileFunctor
| setOnFileRotationCallback
| suppressUniqueFileNameOnRotation
| isFileLoggingEnabled
| isPublishInLocalTimeEnabled
| isSuppressUniqueFileNameOnRotation
| rotationLifetime
| rotationSize
V
,--------------.
( ball::Observer )
`--------------'
dtor
publish
releaseRecords
A ball::FileObserver2
object processes the log records received through its publish
method by writing them to a user-specified file. Note that the enableFileLogging
method must be called to enable logging since logging to a file is initially disabled following construction. The format of published log records is user-configurable (see Log Record Formatting below). In addition, a file observer may be configured to perform automatic log file rotation (see Log File Rotation below).
-
- File Observer Configuration Synopsis:
ball::FileObserver2
offers several manipulators that may be used to configure various aspects of a file observer object. These are summarized in the following table along with the accessors that can be used to query the current state of the configuration. Further details are provided in the following sections and the function-level documentation. +-------------+------------------------------------+
| Aspect | Related Methods |
+=============+====================================+
| Log Record | setLogFileFunctor |
| Formatting | |
+-------------+------------------------------------+
| Log Record | enablePublishInLocalTime |
| Timestamps | disablePublishInLocalTime |
| | isPublishInLocalTimeEnabled |
+-------------+------------------------------------+
| File | enableFileLogging |
| Logging | disableFileLogging |
| | isFileLoggingEnabled |
+-------------+------------------------------------+
| Log File | rotateOnSize |
| Rotation | rotateOnTimeInterval |
| | disableSizeRotation |
| | disableTimeIntervalRotation |
| | setOnFileRotationCallback |
| | suppressUniqueFileNameOnRotation |
| | rotationSize |
| | rotationLifetime |
| | isSuppressUniqueFileNameOnRotation |
+-------------+------------------------------------+
In general, a ball::FileObserver2
object can be dynamically configured throughout its lifetime (in particular, before or after being registered with a logger manager). However, note that for ball::FileObserver2
, configuration changes that affect how records are logged (e.g., enablePublishInLocalTime
and disablePublishInLocalTime
) impact only those records that are published subsequent to making the configuration change.
-
- Log Record Formatting:
- By default, the output format of published log records is:
DATE_TIME PID:THREAD-ID SEVERITY FILE:LINE CATEGORY MESSAGE USER-FIELDS
where DATE
and TIME
are of the form DDMonYYYY
and HH:MM:SS.mmm
, respectively (Mon
being the 3-letter abbreviation for the month). For example, assuming that no user-defined fields are present, a log record will have the following appearance when the default format is in effect: 18MAY2005_18:58:12.076 7959:1 WARN ball_fileobserver2.t.cpp:404 TEST hello!
The default format can be overridden by supplying a suitable formatting functor to setLogFileFunctor
. For example, an instance of ball::RecordStringFormatter
conveniently provides such a functor: The above statement will cause subsequent records to be logged in a format that is almost identical to the default format except that the timestamp attribute will be written in ISO 8601 format. See ball_recordstringformatter
for information on how format specifications are defined and interpreted.
- Note that the observer emits newline characters at the beginning and at the end of a log record by default, so the user needs to add them explicitly to the format string to preserve this behavior.
- Also note that in the sample message above the timestamp has millisecond precision (
18MAY2005_18:58:12.076
). If microsecond precision is desired instead, consider using either the D
or O
format specification supported by ball_recordstringformatter
.
-
- Log Record Timestamps:
- By default, the timestamp attributes of published records are written in UTC time. To write timestamps in local time instead, call the
enablePublishInLocalTime
method. Note that the local time offset is calculated using the UTC timestamp of each record. To revert to UTC time, call the disablePublishInLocalTime
method. Whether UTC time or local time is in effect can be queried via isPublishInLocalTimeEnabled
. However, note that if the user installs a log record formatting functor via setLogFileFunctor
that is not a ball::RecordStringFormatter
, then the supplied functor determines how record timestamps are rendered to the log.
-
- Local Time Offset Calculations:
- The calculation of the local time offset adds some overhead to the publication of each log record. If this overhead is an issue, it can be mitigated by installing a high-performance local-time offset callback for
bdlt::CurrentTime
in main
. See bsls_systemtime
for the details of installing such a callback and see baltzo_localtimeoffsetutil
for an example facility. Note that such callbacks can improve performance for all users of bdlt::CurrentTime
, not just the ball
logger.
-
- Log Filename Patterns:
- The
enableFileLogging
method supports the use of %
-escape sequences to specify log filenames. The recognized sequences are as follows: %Y - current year (4 digits with leading zeros)
%M - current month (2 digits with leading zeros)
%D - current day (2 digits with leading zeros)
%h - current hour (2 digits with leading zeros)
%m - current minute (2 digits with leading zeros)
%s - current second (2 digits with leading zeros)
%T - current datetime, equivalent to "%Y%M%D_%h%m%s"
%p - process ID
The date and time elements of the derived filename are based on the time when the log file is created. Furthermore, these elements are based on either UTC time or local time depending on the value returned by isPublishInLocalTimeEnabled
. (See Log Record Timestamps for the similarity with the treatment of record timestamps.)
- For example, a log filename pattern of "task.log.%Y%M%D_%h%m%s" will yield the filename
task.log.20110501_123000
if the file is created on 01-May-2011 at 12:30:00 local time (assuming enablePublishInLocalTime
was called).
-
- Log File Rotation:
- A
ball::FileObserver2
may be configured to perform automatic rotation of log files based on simple file rotation conditions (or rules).
-
- File Rotation Conditions:
- Rotation rules may be established based on the size of the log file (i.e., a "rotation-on-size" rule), and a periodic time interval (i.e., a "rotation-on-time-interval" rule). These rules are independently enabled by the
rotateOnSize
and rotateOnTimeInterval
methods, respectively. If both rules are in effect, log file rotation is performed whenever either rule applies.
-
- Rotated File Naming:
- When a log file is rotated, a new filename is generated using the pattern supplied to
enableFileLogging
. If the file having the new name does not exist, the current log file is closed, and the logging continues to the new file.
- If the file having the new name already exits, then the behavior of the file rotation is further controlled by the flag set with
suppressUniqueFileNameOnRotation
:
-
suppressUniqueFileNameOnRotation(false)
(default behavior) The current log filename is renamed by appending a timestamp in the form ".%Y%M%D_%h%m%s" where the timestamp indicates when the file being rotated was last opened (the time of either the last file rotation or the last call to enableFileLogging
, whichever is most recent). As with the timestamps of logged records, the timestamps appended to log filenames upon rotation will be in UTC time or local time depending on the value returned by isPublishInLocalTimeEnabled
.
-
suppressUniqueFileNameOnRotation(true)
The logging continues to the current log file, effectively suppressing log filename rotation. This may happen when the log file pattern does not contain -escape sequences indicating a time period, or the rotation interval is less than the time period encoded by -escape sequences. In order to rotate log files in this mode, the log file pattern MUST contain -escape sequences that specify date and (optionally) time. For example, the log filename pattern "app_%Y%M%D.log" will produce a single log file per calendar day (assuming, the rotation on time is enabled and the rotation happens at least once a day).
- The two tables below illustrate the names of old and new log files when a file rotation occurs. We assume that the log file is rotated on 2011-May-21 at 12:29:59 local time and that the last rotation occurred at 12:30:00 on the previous day. We further assume that
enablePublishInLocalTime
was called, so that all date and time elements are rendered in local time.
- The first table shows the name change (if any) of the (old) log file being rotated when the flag controlled by
suppressUniqueFileNameOnRotation
is set to false
: Disabled: 'suppressUniqueFileNameOnRotation'
For brevity:
<TS1> = 20210520_123000
<TS2> = 20210521_122959 (aka next day, about the same time)
+----------------+-----------------+----------------+----------------------
| Pattern | Filename Before | Filename After | Rotated Filename
| | Rotation | Rotation |
+----------------+-----------------+----------------+----------------------
| "a.log" | a.log | a.log | a.log.<TS1>
| "a.log.%T" | a.log.<TS1> | a.log.<TS2> | a.log.<TS1>
| "a.log.%Y%M" | a.log.202105 | a.log.202105 | a.log.202105.<TS1>
| "a.log.%Y%M%D" | a.log.20210520 | a.log.20110521 | a.log.20210520
+----------------+-----------------+----------------+----------------------
Note that upon rotation a timestamp was appended to the name of the rotated file when the log pattern does not contain -escape sequences indicating a time period ("a.log"), or the rotation period (in our case, one day) is less than the time period encoded in the pattern (in case of "a.log.%Y%M" the period is one month).
- The next table shows the rotated name when the flag controlled by
suppressUniqueFileNameOnRotation
is set to true
, and (possibly new) name of the (new) log file following rotation: Enabled: 'suppressUniqueFileNameOnRotation'
+----------------+-----------------+----------------+----------------------
| Pattern | Filename Before | Filename After | Rotated Filename
| | Rotation | Rotation |
+----------------+-----------------+----------------+----------------------
| "a.log" | a.log | a.log | none
| "a.log.%T" | a.log.<TS1> | a.log.<TS2> | a.log.<TS1>
| "a.log.%Y%M" | a.log.202105 | a.log.202105 | none
| "a.log.%Y%M%D" | a.log.20210520 | a.log.20110521 | a.log.20210520
+----------------+-----------------+----------------+----------------------
Note that the original filename is reused when the log pattern does not contain -escape sequences indicating a time period ("a.log"), or the rotation period (in our case, one day) is less than the time period encoded in the pattern (in case of "a.log.%Y%M" the period is one month).
- Also note, that in any cases, when the log pattern includes "%T", or encodes a time period that coincides the rotation period (in case of "a.log.%Y%M%D" the period is one day), then a unique name on each rotation is produced with the (local) time at which file rotation occurred embedded in the filename.
-
- Thread Safety:
- All methods of
ball::FileObserver2
are thread-safe, and can be called concurrently by multiple threads.
-
- Usage:
- This section illustrates intended use of this component.
-
- Example: Basic Usage:
- First, we create a
ball::LoggerManagerConfiguration
object, lmConfig
, and set the logging "pass-through" level -- the level at which log records are published to registered observers -- to DEBUG
: 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: Next, we create a ball::FileObserver2
object and register it with the ball
logging system; Next, we configure the log file rotation rules: Note that in this configuration the user may end up with multiple log files for a specific day (because of the rotation-on-size rule).
- Then, we enable logging to a file:
observer->enableFileLogging("/var/log/task/task.log");
Finally, we register the file observer with the logger manager. Upon successful registration, the observer will start to receive log records via the publish
method: int rc = manager.registerObserver(observer, "default");
assert(0 == rc);
return 0;
}