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:
-
Define a concrete class derived from
ball::Observer
.
-
Implement the pure virtual
publish
method.
-
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):
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::~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::~my_OstreamObserver() { }
void my_OstreamObserver::publish(const ball::Record& record,
const ball::Context& context)
{
using namespace std;
d_stream << endl;
switch (context.transmissionCause()) {
case ball::Transmission::PASSTHROUGH: {
d_stream << "Single Pass-through Message:" << endl;
} break;
case ball::Transmission::TRIGGER_ALL: {
d_stream << "Remotely ";
}
case ball::Transmission::TRIGGER: {
d_stream << "Triggered Publication Sequence: Message "
<< context.recordIndex() + 1
<< " 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