BDE 4.39.x Production Release
Loading...
Searching...
No Matches
ball_observerformatterimp.h
Go to the documentation of this file.
1/// @file ball_observerformatterimp.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// ball_observerformatterimp.h -*-C++-*-
8#ifndef INCLUDED_BALL_OBSERVERFORMATTERIMP
9#define INCLUDED_BALL_OBSERVERFORMATTERIMP
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup ball_observerformatterimp ball_observerformatterimp
15/// @brief Provide common methods for scheme-based formatters for observers
16/// @addtogroup bal
17/// @{
18/// @addtogroup ball
19/// @{
20/// @addtogroup ball_observerformatterimp
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#ball_observerformatterimp-purpose"> Purpose</a>
25/// * <a href="#ball_observerformatterimp-classes"> Classes </a>
26/// * <a href="#ball_observerformatterimp-description"> Description </a>
27/// * <a href="#ball_observerformatterimp-log-record-formatting"> Log Record Formatting </a>
28/// * <a href="#ball_observerformatterimp-time-zone-default"> Time zone default </a>
29/// * <a href="#ball_observerformatterimp-format-strings"> Format Strings </a>
30/// * <a href="#ball_observerformatterimp-scheme-based-format-specifications"> Scheme-Based Format Specifications (Recommended) </a>
31/// * <a href="#ball_observerformatterimp-legacy-format-specifications"> Legacy Format Specifications </a>
32/// * <a href="#ball_observerformatterimp-legacy-custom-formatter-functor"> Legacy Custom Formatter Functor </a>
33/// * <a href="#ball_observerformatterimp-thread-safety"> Thread Safety </a>
34/// * <a href="#ball_observerformatterimp-usage"> Usage </a>
35/// * <a href="#ball_observerformatterimp-example-1-implementing-a-formatting-observer"> Example 1: Implementing a Formatting Observer </a>
36///
37/// # Purpose {#ball_observerformatterimp-purpose}
38/// Provide common methods for scheme-based formatters for observers
39///
40/// # Classes {#ball_observerformatterimp-classes}
41///
42/// - ball::ObserverFormatterImp: common formatting-related methods for observers
43///
44/// @see ball_cstdioobserver, ball_fileobserver2, ball_streamobserver
45///
46/// # Description {#ball_observerformatterimp-description}
47/// This component provides a common implementation of methods
48/// necessary to support scheme-based formatter configuration.
49///
50/// ## Log Record Formatting {#ball_observerformatterimp-log-record-formatting}
51///
52///
53/// By default, the output format for log records is set by constructor
54/// arguments `format` and `timezoneDefault`. The default format (and the
55/// formatter used) can be changed by calling the `setFormat` method. See
56/// {Scheme-Based Formatters} for more information.
57/// @code
58/// observerFormatterImp.setFormat("qjson://%d %s %m");
59/// @endcode
60/// The above statement will cause subsequent records to be formatted as JSON
61/// objects that contains a timestamp in 'DDMonYYYY_HH:MM:SS.mmm' format, the
62/// severity, and the log message.
63///
64/// ### Time zone default {#ball_observerformatterimp-time-zone-default}
65///
66///
67/// The default time zone (UTC or local) for timestamps may be changed using the
68/// `setTimezoneDefault` method. Note that this method creates and installs a
69/// new formatter that uses the last successfully set format and the new time
70/// zone default.
71///
72/// The time zone default is called default because certain format configuration
73/// syntaxes (JSON:// at the time of writing) allow the user to explicitly
74/// specify the time zone of the timestamp. For fields where it is not
75/// specified the default is used. The `%` formats (at the time of writing) do
76/// not support specifying the time zone, so for such syntaxes the time zone
77/// default is used for all timestamp fields.
78///
79/// ### Format Strings {#ball_observerformatterimp-format-strings}
80///
81///
82/// When using `printf`-style format strings, the respective formats are
83/// specified using `%`-prefixed conversion specifications. (See
84/// @ref ball_recordstringformatter for information on how format specifications
85/// are defined and interpreted.) For example, the following statement will
86/// force subsequent records to be logged in a format that is almost identical
87/// to the default format except that the timestamp attribute will be written in
88/// ISO 8601 format:
89/// @code
90/// observerFormatterImp.setFormat("text://%I %p %t %s %f %l %c %m %a\n");
91/// @endcode
92/// The `setFormat` method uses the "text" scheme by default for format config
93/// strings that do not have a scheme, so the following is an equivalent (though
94/// less expressive and deprecated) method to produce the same configuration:
95/// @code
96/// observerFormatterImp.setFormat("%I %p %t %s %f %l %c %m %a\n");
97/// @endcode
98///
99/// ### Scheme-Based Format Specifications (Recommended) {#ball_observerformatterimp-scheme-based-format-specifications}
100///
101///
102/// The recommended way to specify log record formats is using URI-like
103/// scheme-tagged format configuration strings. A scheme-tagged format string
104/// begins with a scheme identifier followed by `://` and then a
105/// scheme-specific format specification:
106/// @code
107/// <scheme>://<format-specification>
108/// @endcode
109/// The scheme determines which formatter will be used and the syntax of the
110/// format specification. The following schemes are currently supported: text,
111/// json, qjson. See [Scheme-Based Formatters](@ref ball-scheme-based-formatters)
112/// for more details of the supported schemes and their accompanying format
113/// specification syntaxes.
114///
115/// Examples:
116/// @code
117/// // Use text formatter with custom format
118/// observerFormatterImp.setFormat("text://%d %p %t %s %f %l %c %m %a\n");
119///
120/// // Use JSON formatter with selected fields
121/// observerFormatterImp.setFormat(
122/// "json://[\"timestamp\",\"severity\",\"message\"]");
123///
124/// // Use simplified printf-style JSON formatter
125/// observerFormatterImp.setFormat("qjson://%d %s %m");
126/// @endcode
127///
128/// ### Legacy Format Specifications {#ball_observerformatterimp-legacy-format-specifications}
129///
130///
131/// For backward compatibility, format specifications that do not begin with a
132/// scheme tag are treated as legacy `printf`-style format strings. Such
133/// specifications are implicitly treated as if they had a `text://` prefix
134/// and use `ball::RecordStringFormatter`. For example, the following two
135/// calls are equivalent:
136/// @code
137/// observerFormatterImp.setFormat("%d %p:%t %s %f:%l %c %m %a\n");
138/// observerFormatterImp.setFormat("text://%d %p:%t %s %f:%l %c %m %a\n");
139/// @endcode
140/// These `%`-prefixed conversion specifications are defined in
141/// @ref ball_recordstringformatter .
142///
143/// ### Legacy Custom Formatter Functor {#ball_observerformatterimp-legacy-custom-formatter-functor}
144///
145///
146/// There is also a legacy way to change the format by supplying a suitable
147/// formatting functor using `setFormatFunctor`. For example, an instance of
148/// `ball::RecordStringFormatter` conveniently is such a functor:
149/// @code
150/// ObserverFormatterImp.setFormatFunctor(
151/// ball::RecordStringFormatter("%I %p:%t %s %f:%l %c %m %a\n"));
152/// @endcode
153/// The above statement will cause subsequent records to be formatted by that
154/// string formatter. When the formatter is set up this legacy way the
155/// `setTimezoneDefault` method will not affect the formatter and so the return
156/// value of `getTimezoneDefault` is meaningless.
157///
158/// ## Thread Safety {#ball_observerformatterimp-thread-safety}
159///
160///
161/// This class is deliberately only const thread-safe, meaning that the concrete
162/// observer implementation has to provide and lock a mutex before calling the
163/// methods of this class (except for the constructor).
164///
165/// ## Usage {#ball_observerformatterimp-usage}
166///
167///
168/// This section illustrates intended use of this component.
169///
170/// ### Example 1: Implementing a Formatting Observer {#ball_observerformatterimp-example-1-implementing-a-formatting-observer}
171///
172///
173/// In this example, we demonstrate how to use `ObserverFormatterImp` to
174/// implement a simple observer that writes formatted log records to standard
175/// output. First, we define a simple observer class that uses
176/// `ObserverFormatterImp` to manage formatting:
177/// @code
178/// /// This class provides a simple observer implementation that writes
179/// /// formatted log records to 'bsl::cout'.
180/// class MySimpleObserver : public ball::Observer {
181///
182/// public:
183/// // TYPES
184/// typedef bsl::allocator<char> allocator_type;
185///
186/// private:
187/// // DATA
188/// mutable bslmt::Mutex d_mutex; // synchronize access
189/// ball::ObserverFormatterImp d_formatterImp; // formatter manager
190///
191/// public:
192/// // CREATORS
193/// explicit MySimpleObserver(const allocator_type& allocator
194/// = allocator_type())
195/// : d_formatterImp("text://%t %s %m\n",
196/// ball::RecordFormatterTimezone::e_UTC,
197/// allocator)
198/// {
199/// }
200///
201/// // MANIPULATORS
202/// void disablePublishInLocalTime()
203/// {
204/// bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
205/// d_formatterImp.setTimezoneDefault(
206/// ball::RecordFormatterTimezone::e_UTC);
207/// }
208///
209/// void enablePublishInLocalTime()
210/// {
211/// bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
212/// d_formatterImp.setTimezoneDefault(
213/// ball::RecordFormatterTimezone::e_LOCAL);
214/// }
215///
216/// using Observer::publish;
217///
218/// void publish(const bsl::shared_ptr<const ball::Record>& record,
219/// const ball::Context& context)
220/// BSLS_KEYWORD_OVERRIDE
221/// {
222/// bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
223/// d_formatterImp.formatLogRecord(bsl::cout, record);
224/// }
225///
226/// void releaseRecords() BSLS_KEYWORD_OVERRIDE
227/// {
228/// // No-op for this observer
229/// }
230///
231/// int setFormat(const bsl::string_view& format)
232/// {
233/// bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
234/// return d_formatterImp.setFormat(format);
235/// }
236///
237/// void setFormatFunctor(
238/// const ball::RecordFormatterFunctor::Type& formatter)
239/// {
240/// bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
241/// d_formatterImp.setFormatFunctor(formatter);
242/// }
243///
244/// // ACCESSORS
245/// const bsl::string& getFormat() const
246/// {
247/// bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
248/// return d_formatterImp.getFormat();
249/// }
250///
251/// bool isPublishInLocalTimeEnabled() const
252/// {
253/// bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);
254/// return ball::RecordFormatterTimezone::e_LOCAL ==
255/// d_formatterImp.getTimezoneDefault();
256/// }
257/// };
258/// @endcode
259/// Now, we can create an instance of our observer and configure its formatting.
260/// First, we create an observer with default text-based format:
261/// @code
262/// MySimpleObserver observer;
263/// @endcode
264/// Next, we can change the format to JSON format using the `setFormat` method:
265/// @code
266/// const int rc = observer.setFormat("qjson://%t %s %m");
267/// assert(0 == rc);
268/// @endcode
269/// We can also enable local time for timestamps:
270/// @code
271/// observer.enablePublishInLocalTime();
272/// assert(observer.isPublishInLocalTimeEnabled());
273/// @endcode
274/// To revert to UTC time:
275/// @code
276/// observer.disablePublishInLocalTime();
277/// assert(!observer.isPublishInLocalTimeEnabled());
278/// @endcode
279/// We can also retrieve the current format configuration:
280/// @code
281/// const bsl::string& currentFormat = observer.getFormat();
282/// assert("qjson://%t %s %m" == currentFormat);
283/// @endcode
284/// For backwards compatibility, we can also use a custom formatter functor:
285/// @code
286/// observer.setFormatFunctor(
287/// ball::RecordStringFormatter("%I %p:%t %s %f:%l %c %m %a\n"));
288/// @endcode
289/// Now let's demonstrate actually publishing a log record. First, we create a
290/// sample record and context with a fixed timestamp:
291/// @code
292/// const bdlt::Datetime timestamp(2024, 1, 15, 12, 34, 56, 789);
293/// ball::RecordAttributes attributes(
294/// timestamp, // timestamp
295/// 42, // process ID
296/// 123, // thread ID
297/// "example.cpp", // file name
298/// 456, // line number
299/// "EXAMPLE", // category
300/// ball::Severity::e_WARN, // severity
301/// "Sample warning message"); // message
302///
303/// bsl::shared_ptr<ball::Record> record;
304/// record.createInplace(bslma::Default::allocator(),
305/// attributes,
306/// ball::UserFields());
307/// ball::Context context;
308/// @endcode
309/// To capture the output for verification, we redirect 'bsl::cout' to a string
310/// stream:
311/// @code
312/// bsl::ostringstream oss;
313/// bsl::streambuf *originalRdbuf = bsl::cout.rdbuf();
314/// @endcode
315/// When we publish this record with text format, it produces plain text output:
316/// @code
317/// observer.setFormat("text://%d %p:%t %s %f:%l %c %m\n");
318/// bsl::cout.rdbuf(oss.rdbuf());
319/// observer.publish(record, context);
320///
321/// bsl::string output = oss.str();
322/// bsl::cout.rdbuf(originalRdbuf);
323/// assert("15JAN2024_12:34:56.789 42:123 WARN example.cpp:456 EXAMPLE "
324/// "Sample warning message\n" == output);
325///
326/// oss.str(""); // Clear the stream for next test
327/// @endcode
328/// Next, we publish the same record with JSON format that produces structured
329/// JSON output:
330/// @code
331/// observer.setFormat("qjson://%d %p %t %s %F %l %c %m");
332/// bsl::cout.rdbuf(oss.rdbuf());
333/// observer.publish(record, context);
334///
335/// output = oss.str();
336/// bsl::cout.rdbuf(originalRdbuf);
337/// @endcode
338/// Finally, we verify the JSON fields:
339/// @code
340/// assert(bsl::string::npos != output.find("\"timestamp\""));
341/// assert(bsl::string::npos != output.find("\"15JAN2024_12:34:56.789\""));
342/// assert(bsl::string::npos != output.find("\"pid\""));
343/// assert(bsl::string::npos != output.find("42"));
344/// assert(bsl::string::npos != output.find("\"tid\""));
345/// assert(bsl::string::npos != output.find("123"));
346/// assert(bsl::string::npos != output.find("\"severity\""));
347/// assert(bsl::string::npos != output.find("\"WARN\""));
348/// assert(bsl::string::npos != output.find("\"message\""));
349/// assert(bsl::string::npos != output.find("\"Sample warning message\""));
350/// @endcode
351/// @}
352/** @} */
353/** @} */
354
355/** @addtogroup bal
356 * @{
357 */
358/** @addtogroup ball
359 * @{
360 */
361/** @addtogroup ball_observerformatterimp
362 * @{
363 */
364
365#include <balscm_version.h>
366
371
372#include <bsla_deprecated.h>
373
374#include <bslma_bslallocator.h>
375
376#include <bsls_assert.h>
377
378#include <bsl_iosfwd.h>
379#include <bsl_memory.h>
380#include <bsl_string.h>
381#include <bsl_string_view.h>
382
383
384namespace ball {
385
386 // ==========================
387 // class ObserverFormatterImp
388 // ==========================
389
390/// This class provides a common implementation of formatting-related methods
391/// for observer components that support scheme-based log record formatting.
392/// It manages a log record formatter, format configuration strings, and time
393/// zone preferences. This implementation is intended to be used as a data
394/// member in concrete observer classes to provide consistent formatting
395/// behavior across different observer types.
396///
397/// See @ref ball_observerformatterimp
399 public:
400 // TYPES
401
402 /// `RecordFormatter` is an alias for the type of the functor used for
403 /// formatting log records to a stream.
405
407
408 private:
409 // PRIVATE TYPES
411
412 private:
413 // DATA
414 bsl::string d_format; // canonical format that
415 // resulted in `d_formatter` or
416 // an empty string if it was set
417 // using `setFormatFunctor`
418
419 TimezoneEnum d_timezoneDefault; // Local, or UTC.
420
421 RecordFormatter d_formatter; // formatting functor used when
422 // writing to log
423 private:
424 // NOT IMPLEMENTED
427
428 private:
429 // PRIVATE MANIPULATORS
430
431 public:
432 // CREATORS
433
434 /// Create a `ObserverFormatterImp` instance that formats log records
435 /// using the specified `format` and `timezoneDefault`. It is strongly
436 /// advised to specify UTC for timezoneDefault. Optionally specify an
437 /// `allocator` (e.g., the address of a `bslma::Allocator` object) to
438 /// supply memory; otherwise, the default allocator is used.
439 ///
440 /// \pre The behavior is undefined unless `format` is a valid format string
441 /// (either one that starts with a scheme or a "text" scheme format).
442 ///
443 /// \note Note that not giving a valid default format string by the observer
444 /// will result in no logging output if no valid format is set
445 /// afterwards, so it is a serious error.
446 explicit
448 TimezoneEnum timezoneDefault,
449 const allocator_type& allocator = allocator_type());
450
451 // MANIPULATORS
452
453 /// Set the default time zone used for timestamps that have no timezone
454 /// preference specified in the format config string. This method
455 /// recreates the formatter used with the new timezone setting and the last successfully-set format config string.
456 ///
457 /// \note Note that (at the time of
458 /// writing) the printf-style format strings do not have syntax to specify
459 /// the time zone so for such-configured formatters all timestamps will use
460 /// this time zone default.
461 void setTimezoneDefault(TimezoneEnum timezoneDefault);
462
463 /// Set the formatting functor used when writing records to the specified `formatter` functor.
464 ///
465 /// \note Note that this
466 /// method is not able to communicate the timezone
467 /// default settings to the `formatter`, prefer `setFormat`.
468 void setFormatFunctor(const RecordFormatter& formatter);
469
470 /// Set the log record format specifications for this object according to
471 /// the specified `format`. Return 0 on success, and a non-zero value
472 /// otherwise. This method has no effect on format if `format` is not a
473 /// valid format specification. If `format` does not start with a scheme
474 /// (it has no "://" in it) use the default "text" scheme.
475 int setFormat(const bsl::string_view& format);
476
477 // ACCESSORS
478
479 /// Format the specified log `record` into the specified output `stream`
480 /// using the current record formatter functor.
481 void formatLogRecord(bsl::ostream& stream,
482 const bsl::shared_ptr<const Record>& record) const;
483
484 /// Return the currently active format configuration string of this object.
485 const bsl::string& getFormat() const;
486
487 /// Return the currently active time zone default of this object.
489
490 // Aspects
491
492 /// Return the allocator used by this object to supply memory.
493 ///
494 /// \note Note that if no allocator was supplied at construction the default
495 /// allocator in effect at construction is used.
497};
498
499// ============================================================================
500// INLINE DEFINITIONS
501// ============================================================================
502
503 // --------------------------
504 // class ObserverFormatterImp
505 // --------------------------
506
507// CREATORS
508inline
509ObserverFormatterImp::ObserverFormatterImp(
510 const bsl::string_view& format,
511 TimezoneEnum timezoneDefault,
512 const allocator_type& allocator)
513: d_format(allocator)
514, d_timezoneDefault(timezoneDefault)
515, d_formatter(bsl::allocator_arg, allocator)
516{
517 const int setFormatResult = setFormat(format);
518 BSLS_ASSERT(0 == setFormatResult); (void)setFormatResult;
519}
520
521// MANIPULATORS
522inline
524{
526 &d_formatter,
527 format,
528 RecordFormatterOptions(d_timezoneDefault));
529
530 if (0 == rc) {
531 // Update format string on success
532 d_format = format;
533 }
534 return rc;
535}
536
537inline
539{
540 d_formatter = formatter;
541 d_format.clear();
542}
543
544inline
546{
547 if (d_timezoneDefault == timezoneDefault) {
548 // No change, nothing to do
549 return; // RETURN
550 }
551
552 d_timezoneDefault = timezoneDefault;
553
554 // If the formatter was created from a format string, recreate it with
555 // the new timezone setting.
556 if (!d_format.empty()) {
558 &d_formatter,
559 d_format,
560 RecordFormatterOptions(d_timezoneDefault));
561 }
562}
563
564// ACCESSORS
565inline
567{
568 return d_format;
569}
570
571inline
573 bsl::ostream& stream,
574 const bsl::shared_ptr<const Record>& record) const
575{
576 d_formatter(stream, *record);
577}
578
579inline
582{
583 return d_timezoneDefault;
584}
585
586 // Aspects
587inline
590{
591 return d_format.get_allocator();
592}
593
594} // close package namespace
595
596
597#endif
598
599// ----------------------------------------------------------------------------
600// Copyright 2025 Bloomberg Finance L.P.
601//
602// Licensed under the Apache License, Version 2.0 (the "License");
603// you may not use this file except in compliance with the License.
604// You may obtain a copy of the License at
605//
606// http://www.apache.org/licenses/LICENSE-2.0
607//
608// Unless required by applicable law or agreed to in writing, software
609// distributed under the License is distributed on an "AS IS" BASIS,
610// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
611// See the License for the specific language governing permissions and
612// limitations under the License.
613// ----------------------------- END-OF-FILE ----------------------------------
614
615/** @} */
616/** @} */
617/** @} */
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
bsl::allocator< char > allocator_type
Definition ball_observerformatterimp.h:406
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
allocator_type get_allocator() const
Definition ball_observerformatterimp.h:589
RecordFormatterFunctor::Type RecordFormatter
Definition ball_observerformatterimp.h:404
Definition ball_recordformatteroptions.h:112
Definition bslma_bslallocator.h:588
Definition bslstl_stringview.h:471
Definition bslstl_string.h:1252
allocator_type get_allocator() const BSLS_KEYWORD_NOEXCEPT
Return the allocator used by this string to supply memory.
Definition bslstl_string.h:7423
bool empty() const BSLS_KEYWORD_NOEXCEPT
Return true if this string has length 0, and false otherwise.
Definition bslstl_string.h:7331
void clear() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_string.h:6043
Forward declaration.
Definition bslstl_function.h:946
Definition bslstl_sharedptr.h:1838
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition ball_administration.h:214
Definition bdlat_valuetypefunctions.h:939
static int createRecordFormatter(RecordFormatterFunctor::Type *result, const bsl::string_view &format, const RecordFormatterOptions &formatOptions)
Enum
Timezone setting for timestamps in log record formatters.
Definition ball_recordformattertimezone.h:114