Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component ball_observer
[Package ball]

Define a protocol for receiving and processing log records. More...

Namespaces

namespace  ball

Detailed Description

Outline
Purpose:
Define a protocol for receiving and processing log records.
Classes:
ball::Observer protocol class for receiving and processing log records
See also:
Component ball_record, Component ball_loggermanager
Description:
This component defines the base-level protocol, ball::Observer, for receiving and processing log records. Concrete types derived from this protocol, receive log records, and process them in a manner defined by the derived class author.
Usage:
This example shows the definition and use of a simple concrete observer that writes three of the log record's fields, timestamp, process ID, and thread ID, to an ostream that is provided to the observer at construction. This (trivial) functionality suffices to demonstrate the requisite steps for having a working observer:
  1. Define a concrete class derived from ball::Observer.
  2. Implement the pure virtual publish method.
  3. Instantiate and use an object of the concrete type.
Note that the "publish attributes" object provided to the publish method indicates, among other properties, whether the log record to be published is a "solo" message or whether it is one of a sequence. In general, a useful observer object should incorporate the attributes information as part of the "publication" of the log record. In this example, the attributes information is used to generate an appropriate heading for each log record that is printed to the observer's ostream.
We first define the (derived) my_OstreamObserver class and implement its simple constructor inline (for convenience, directly within the derived-class definition):
     // my_ostreamobserver.h

     class my_OstreamObserver : public ball::Observer {
         ostream& d_stream;

       public:
         my_OstreamObserver(ostream& stream) : d_stream(stream) { }
         virtual ~my_OstreamObserver();
         virtual void publish(const ball::Record&  record,
                              const ball::Context& context);
     };
Note, however, that we always implement a virtual destructor (non-inline) in the .cpp file (to indicate the unique location of the class's virtual table):
     // my_ostreamobserver.cpp

     // ...

     my_OstreamObserver::~my_OstreamObserver() { }
We next implement the (virtual) publish method, which incorporates the "policy" of what it means for this observer to "publish" a log record. In this example, the policy is that three log record fields are written to an ostream, along with an appropriate heading, and the rest of the log record is ignored. Note that, in this implementation, the zero-based index attribute is incremented by one before it is written, which produces a more natural record count:
     // my_ostreamobserver.cpp

     // ...

     my_OstreamObserver::~my_OstreamObserver() { }

     void my_OstreamObserver::publish(const ball::Record&  record,
                                      const ball::Context& context)
     {
         using namespace std;

         d_stream << endl;  // skip a line

         switch (context.transmissionCause()) {
           case ball::Transmission::PASSTHROUGH: {
             d_stream << "Single Pass-through Message:" << endl;
           } break;
           case ball::Transmission::TRIGGER_ALL: {
             d_stream << "Remotely ";      // no 'break'; concatenated output
           }
           case ball::Transmission::TRIGGER: {
             d_stream << "Triggered Publication Sequence: Message "
                      << context.recordIndex() + 1  // Account for 0-based
                                                    // index.
                      << " of " << context.sequenceLength()
                      << ':' << endl;
           } break;
           default: {
             d_stream << "***ERROR*** Unknown Message Cause:" << endl;
             return;
           } break;
         }

         d_stream << "\tTimestamp:  " << record.fixedFields().timestamp()
                  << endl;
         d_stream << "\tProcess ID: " << record.fixedFields().processID()
                  << endl;
         d_stream << "\tThread ID:  " << record.fixedFields().threadID()
                  << endl;
     }
We now want to use the my_OstreamObserver object and its publish method; we illustrate this use in the body of the otherwise-unrealistic function recordPublisher, which generates the relevant fields of four dummy records. The first record is published singly (i.e., as a "Pass-through" record). Note that we call the observer's publish method with a ball::Context object appropriately initialized for a "Pass-through". The last three records are published as a sequence of "Triggered" records. Note that, in the sequenced output, the publish method is called with a zero-based index attribute; in this example, the publish implementation will print a natural-number message count equal to index + 1:
     void recordPublisher()
     {
         my_OstreamObserver     myObserver(bsl::cout);
         bdlt::Datetime         now;
         ball::RecordAttributes fixed;
         ball::UserFields       emptyList;

         bdlt::EpochUtil::convertFromTimeT(&now, time(0));
         fixed.setTimestamp(now);
         fixed.setProcessID(100);
         fixed.setThreadID(0);
         myObserver.publish(ball::Record(fixed, emptyList),
                            ball::Context(ball::Transmission::PASSTHROUGH,
                                         0,
                                         1));

         const int NUM_MESSAGES = 3;
         for (int n = 0; n < NUM_MESSAGES; ++n) {
             bdlt::EpochUtil::convertFromTimeT(&now, time(0));
             fixed.setTimestamp(now);
             fixed.setProcessID(201 + n);
             fixed.setThreadID(31 + n);
             myObserver.publish(ball::Record(fixed, emptyList),
                                ball::Context(ball::Transmission::TRIGGER,
                                             n,
                                             NUM_MESSAGES));
         }
     }
recordPublisher, when invoked, prints the following to stdout:
     Single Pass-through Message:
             Timestamp:  15JAN2004_23:12:59.000
             Process ID: 100
             Thread ID:  0

     Triggered Publication Sequence: Message 1 of 3:
             Timestamp:  15JAN2004_23:12:59.000
             Process ID: 201
             Thread ID:  31

     Triggered Publication Sequence: Message 2 of 3:
             Timestamp:  15JAN2004_23:12:59.000
             Process ID: 202
             Thread ID:  32

     Triggered Publication Sequence: Message 3 of 3:
             Timestamp:  15JAN2004_23:12:59.000
             Process ID: 203
             Thread ID:  33