BDE 4.39.x Production Release
Loading...
Searching...
No Matches
ball_observerformatterimp

Detailed Description

Provide common methods for scheme-based formatters for observers.

Outline

Purpose

Provide common methods for scheme-based formatters for observers

Classes

See also
ball_cstdioobserver, ball_fileobserver2, ball_streamobserver

Description

This component provides a common implementation of methods necessary to support scheme-based formatter configuration.

Log Record Formatting

By default, the output format for log records is set by constructor arguments format and timezoneDefault. The default format (and the formatter used) can be changed by calling the setFormat method. See {Scheme-Based Formatters} for more information.

observerFormatterImp.setFormat("qjson://%d %s %m");

The above statement will cause subsequent records to be formatted as JSON objects that contains a timestamp in 'DDMonYYYY_HH:MM:SS.mmm' format, the severity, and the log message.

Time zone default

The default time zone (UTC or local) for timestamps may be changed using the setTimezoneDefault method. Note that this method creates and installs a new formatter that uses the last successfully set format and the new time zone default.

The time zone default is called default because certain format configuration syntaxes (JSON:// at the time of writing) allow the user to explicitly specify the time zone of the timestamp. For fields where it is not specified the default is used. The % formats (at the time of writing) do not support specifying the time zone, so for such syntaxes the time zone default is used for all timestamp fields.

Format Strings

When using printf-style format strings, the respective formats are specified using %-prefixed conversion specifications. (See ball_recordstringformatter for information on how format specifications are defined and interpreted.) For example, the following statement will force 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:

observerFormatterImp.setFormat("text://%I %p %t %s %f %l %c %m %a\n");

The setFormat method uses the "text" scheme by default for format config strings that do not have a scheme, so the following is an equivalent (though less expressive and deprecated) method to produce the same configuration:

observerFormatterImp.setFormat("%I %p %t %s %f %l %c %m %a\n");

Scheme-Based Format Specifications (Recommended)

The recommended way to specify log record formats is using URI-like scheme-tagged format configuration strings. A scheme-tagged format string begins with a scheme identifier followed by :// and then a scheme-specific format specification:

<scheme>://<format-specification>

The scheme determines which formatter will be used and the syntax of the format specification. The following schemes are currently supported: text, json, qjson. See Scheme-Based Formatters for more details of the supported schemes and their accompanying format specification syntaxes.

Examples:

// Use text formatter with custom format
observerFormatterImp.setFormat("text://%d %p %t %s %f %l %c %m %a\n");
// Use JSON formatter with selected fields
observerFormatterImp.setFormat(
"json://[\"timestamp\",\"severity\",\"message\"]");
// Use simplified printf-style JSON formatter
observerFormatterImp.setFormat("qjson://%d %s %m");

Legacy Format Specifications

For backward compatibility, format specifications that do not begin with a scheme tag are treated as legacy printf-style format strings. Such specifications are implicitly treated as if they had a text:// prefix and use ball::RecordStringFormatter. For example, the following two calls are equivalent:

observerFormatterImp.setFormat("%d %p:%t %s %f:%l %c %m %a\n");
observerFormatterImp.setFormat("text://%d %p:%t %s %f:%l %c %m %a\n");

These %-prefixed conversion specifications are defined in ball_recordstringformatter .

Legacy Custom Formatter Functor

There is also a legacy way to change the format by supplying a suitable formatting functor using setFormatFunctor. For example, an instance of ball::RecordStringFormatter conveniently is such a functor:

ObserverFormatterImp.setFormatFunctor(
ball::RecordStringFormatter("%I %p:%t %s %f:%l %c %m %a\n"));
Definition ball_recordstringformatter.h:220

The above statement will cause subsequent records to be formatted by that string formatter. When the formatter is set up this legacy way the setTimezoneDefault method will not affect the formatter and so the return value of getTimezoneDefault is meaningless.

Thread Safety

This class is deliberately only const thread-safe, meaning that the concrete observer implementation has to provide and lock a mutex before calling the methods of this class (except for the constructor).

Usage

This section illustrates intended use of this component.

Example 1: Implementing a Formatting Observer

In this example, we demonstrate how to use ObserverFormatterImp to implement a simple observer that writes formatted log records to standard output. First, we define a simple observer class that uses ObserverFormatterImp to manage formatting:

/// This class provides a simple observer implementation that writes
/// formatted log records to 'bsl::cout'.
class MySimpleObserver : public ball::Observer {
public:
// TYPES
typedef bsl::allocator<char> allocator_type;
private:
// DATA
mutable bslmt::Mutex d_mutex; // synchronize access
ball::ObserverFormatterImp d_formatterImp; // formatter manager
public:
// CREATORS
explicit MySimpleObserver(const allocator_type& allocator
= allocator_type())
: d_formatterImp("text://%t %s %m\n",
ball::RecordFormatterTimezone::e_UTC,
allocator)
{
}
// MANIPULATORS
void disablePublishInLocalTime()
{
d_formatterImp.setTimezoneDefault(
}
void enablePublishInLocalTime()
{
d_formatterImp.setTimezoneDefault(
}
using Observer::publish;
const ball::Context& context)
{
d_formatterImp.formatLogRecord(bsl::cout, record);
}
{
// No-op for this observer
}
int setFormat(const bsl::string_view& format)
{
return d_formatterImp.setFormat(format);
}
void setFormatFunctor(
{
d_formatterImp.setFormatFunctor(formatter);
}
// ACCESSORS
const bsl::string& getFormat() const
{
return d_formatterImp.getFormat();
}
bool isPublishInLocalTimeEnabled() const
{
d_formatterImp.getTimezoneDefault();
}
};
Definition ball_context.h:297
Definition ball_observerformatterimp.h:398
const bsl::string & getFormat() const
Return the currently active format configuration string of this object.
Definition ball_observerformatterimp.h:566
int setFormat(const bsl::string_view &format)
Definition ball_observerformatterimp.h:523
void formatLogRecord(bsl::ostream &stream, const bsl::shared_ptr< const Record > &record) const
Definition ball_observerformatterimp.h:572
void setFormatFunctor(const RecordFormatter &formatter)
Definition ball_observerformatterimp.h:538
void setTimezoneDefault(TimezoneEnum timezoneDefault)
Definition ball_observerformatterimp.h:545
TimezoneEnum getTimezoneDefault() const
Return the currently active time zone default of this object.
Definition ball_observerformatterimp.h:581
Definition ball_observer.h:235
virtual void releaseRecords()
virtual void publish(const Record &record, const Context &context)
Definition bslma_bslallocator.h:588
Definition bslstl_stringview.h:471
Definition bslstl_string.h:1252
Forward declaration.
Definition bslstl_function.h:946
Definition bslstl_sharedptr.h:1838
Definition bslmt_lockguard.h:234
Definition bslmt_mutex.h:317
#define BSLS_KEYWORD_OVERRIDE
Definition bsls_keyword.h:695
Definition ball_administration.h:214
@ e_UTC
Definition ball_recordformattertimezone.h:115
@ e_LOCAL
Definition ball_recordformattertimezone.h:116

Now, we can create an instance of our observer and configure its formatting. First, we create an observer with default text-based format:

MySimpleObserver observer;

Next, we can change the format to JSON format using the setFormat method:

const int rc = observer.setFormat("qjson://%t %s %m");
assert(0 == rc);

We can also enable local time for timestamps:

observer.enablePublishInLocalTime();
assert(observer.isPublishInLocalTimeEnabled());

To revert to UTC time:

observer.disablePublishInLocalTime();
assert(!observer.isPublishInLocalTimeEnabled());

We can also retrieve the current format configuration:

const bsl::string& currentFormat = observer.getFormat();
assert("qjson://%t %s %m" == currentFormat);

For backwards compatibility, we can also use a custom formatter functor:

observer.setFormatFunctor(
ball::RecordStringFormatter("%I %p:%t %s %f:%l %c %m %a\n"));

Now let's demonstrate actually publishing a log record. First, we create a sample record and context with a fixed timestamp:

const bdlt::Datetime timestamp(2024, 1, 15, 12, 34, 56, 789);
timestamp, // timestamp
42, // process ID
123, // thread ID
"example.cpp", // file name
456, // line number
"EXAMPLE", // category
"Sample warning message"); // message
attributes,
ball::Context context;
Definition ball_recordattributes.h:275
Definition ball_userfields.h:136
Definition bdlt_datetime.h:330
void createInplace()
Definition bslstl_sharedptr.h:5539
@ e_WARN
Definition ball_severity.h:176
static Allocator * allocator(Allocator *basicAllocator=0)
Definition bslma_default.h:913

To capture the output for verification, we redirect 'bsl::cout' to a string stream:

bsl::streambuf *originalRdbuf = bsl::cout.rdbuf();
basic_ostringstream< char, char_traits< char >, allocator< char > > ostringstream
Definition bslstl_iosfwd.h:97

When we publish this record with text format, it produces plain text output:

observer.setFormat("text://%d %p:%t %s %f:%l %c %m\n");
bsl::cout.rdbuf(oss.rdbuf());
observer.publish(record, context);
bsl::string output = oss.str();
bsl::cout.rdbuf(originalRdbuf);
assert("15JAN2024_12:34:56.789 42:123 WARN example.cpp:456 EXAMPLE "
"Sample warning message\n" == output);
oss.str(""); // Clear the stream for next test

Next, we publish the same record with JSON format that produces structured JSON output:

observer.setFormat("qjson://%d %p %t %s %F %l %c %m");
bsl::cout.rdbuf(oss.rdbuf());
observer.publish(record, context);
output = oss.str();
bsl::cout.rdbuf(originalRdbuf);

Finally, we verify the JSON fields:

assert(bsl::string::npos != output.find("\"timestamp\""));
assert(bsl::string::npos != output.find("\"15JAN2024_12:34:56.789\""));
assert(bsl::string::npos != output.find("\"pid\""));
assert(bsl::string::npos != output.find("42"));
assert(bsl::string::npos != output.find("\"tid\""));
assert(bsl::string::npos != output.find("123"));
assert(bsl::string::npos != output.find("\"severity\""));
assert(bsl::string::npos != output.find("\"WARN\""));
assert(bsl::string::npos != output.find("\"message\""));
assert(bsl::string::npos != output.find("\"Sample warning message\""));
size_type find(const basic_string &substring, size_type position=0) const BSLS_KEYWORD_NOEXCEPT
Definition bslstl_string.h:7432
static const size_type npos
Definition bslstl_string.h:1793