// SampleFramework-- Sample of using the ErrorLogger mechanism. // // This is part of the principal sample of how to use the ErrorLogger // mechanism in a realistic setting. // // The sample is meant to be taken as a starting point, and modified to // meet an experiment's needs. Or it can be viewed as a fairly complete // illustration of most of the commonly used features in ErrorLogger. // // The overall structure assumed is that some programmer(s) provide a // "framework" including the main program, various setups and event-flow // controls, and so forth. Then each group of physicists provide "modules" // (or in D0 language "packages") which do steps of physics calculation -- // the framework will pass an event from package to package. // // This sample is composed of five files: // SampleFramework * // SampleModule // SampleEvent // SampleModuleA // SampleModuleB // // The strange comments like // $$ 9:3 refer to the fact that this line // exercises something in section 9 (for example) of the design document. // // --------------------------- // Include ErrorLogger Headers // --------------------------- #include "ErrorLogger/ELadministrator.h" #include "ErrorLogger/ELdestControl.h" #include "ErrorLogger/ELoutput.h" #include "ErrorLogger/ELstatistics.h" // These includes cover ErrorLogger classes that frameworks // would normally use. //#include "ErrorLogger/ErrorObj.h" ZM_USING_NAMESPACE( zmel ) /* using zmel, the ErrorLogger */ // --------------------- // Usual I/O and strings // --------------------- #include "ZMutility/iostream" USING( std::cout ) USING( std::endl ) #include USING( std::string ) // --------------------------- // Headers for experiment code // --------------------------- #include "SampleFramework.h" #include "SampleModule.h" #include "SampleEvent.h" #include "SampleModuleA.h" #include "SampleModuleB.h" // --------------- // RunEventContext // --------------- #include "ZMutility/sstream" USING( std::ostringstream ) // The various context methods will use an ostringstream to // create the string describing the context. string SampleFramework::RunEventContext::summaryContext() const { ostringstream ev; ev << f.getRun() << "/" << f.getEventNum(); return ev.str(); } string SampleFramework::RunEventContext::context() const { ostringstream ev; ev << " Run = " << f.getRun() << "; Event = " << f.getEventNum(); return ev.str(); } string SampleFramework::RunEventContext::fullContext() const { return context(); } SampleFramework::RunEventContext::RunEventContext ( SampleFramework & ff ) : f(ff) { } ZM_COVARIANT_TYPE(ELcontextSupplier *, SampleFramework::RunEventContext *) SampleFramework::RunEventContext::clone() const { return new SampleFramework::RunEventContext( *this ); // Just follow this form exactly; there is no // need to customize clone(). } // ------ // main() // ------ int main() { SampleFramework framework; framework.setup(); framework.eventLoop(); return 0; } // ------------------------------------------ // SampleFramework constructor and destructor // ------------------------------------------ SampleFramework::SampleFramework() : reContext(*this) // The context supplier has to know where // to find the framework. This is how it knows. { // Fill the eventSteps list with each module to be applied. eventSteps.push_back (new SampleModuleA("A")); // delete is in destructor eventSteps.push_back (new SampleModuleB("B")); // delete is in destructor } // SampleFramework() SampleFramework::~SampleFramework() { // Having new-ed an instance of each Moudule, we must now delete them. // Since the Module base class has a virtual destructor, we can delete them // just by the Module* pointers in the eventSteps list. while ( !eventSteps.empty() ) { delete ( eventSteps.back() ); eventSteps.pop_back(); } } // SampleFramework() // ------- // setup() // ------- void SampleFramework::setup() { // Step 1: Instantiate the ELadministrator, and establish its process, // context supplier, and a severity threshold - if a message has // this severity or higher, the program will abort. logger = ELadministrator::instance(); // $$ 9:2 logger->setProcess ("CFD0 Job 1"); // $$ 9:12 logger->setContextSupplier( reContext ); // $$ 9:3 logger->setAbortThreshold( ELfatal ); // $$ 9:4 // Step 2: Instantiate whatever destinations you require for the log // messages. These typically would be one or more ELoutput // to go to files and/or streams like cout, plus an ELstatistics. // Each destination is attached to the ELadministrator, and the // framework gets an ELdestControl which will be used later to do // things involving the destination. // // The ELstatistics should be attached last so that it knows about // which ELoutputs have responded to a given message. // Because these destinations might be used by framework methods // other than this setup() method -- for instance, eventLoop() // occasionally does stats.summary() -- the ELdestControl's // should be (private) variables in the framework class. The // way to set that up is as shown here. Notice that the destinations // themselves (e.g., ELoutput ("logfile.sample")) can be quite // temporary because when each is attached to logger, a copy is made // which is owned by the logger. logfile = logger->attach(ELoutput ( "logfile.sample" )); // $$ 9:6 // This logs to a file. problemLog = logger->attach(ELoutput ( "problemlog.sample" )); // This logs to another file. We will (below) // set the threshold for problemLog higher than // for logfile. // enable the cout, false form to test `no established message' // output = logger->attach(ELoutput ( cout, false )); output = logger->attach(ELoutput ( cout )); // $$ 9:8 // This logs to the cout stream. stats = logger->attach(ELstatistics ( cout )); // $$ 9:10 // This keeps statistics on the messages logged. // Step 3: Establish thresholds for each destination. If a message is // logged with severity less than that threshold, the destination // will ignore that message. output.setThreshold ( ELwarning ); problemLog.setThreshold ( ELerror ); logfile.setThreshold ( ELsuccess ); stats.setThreshold ( ELwarning ); // Step 4: Establish limits to throttle the number of times a given // type of message will be recorded by each destination. // If a no limit is set, every above-threshold message is logged // at that destination. For example, problemLog will have no // limits here. logfile.setLimit ( "*", 20 ); // $$ 9:11 output.setLimit ( "*", 10 ); output.setLimit ( ELerror, 100 ); logger->setLimits (ELnextEvent, -1); // $$ 13:9 // This says that errors of nextEvent severity have no limit // on how many times they are displayed, at any destination. logger->setTimespans ( "*", 600 ); // $$ 20:1 // For every destination, if it has been 600 seconds // since the last error message of some type, then // set the count to the limit back to zero (and thus // start logging any new messages that come up). // Step 5: (Optional) Establish an ErrorLog for use by the framework itself. // This was declared as a member of the SampleFramework class; here // we simply tell it which module to say its errors come from. // Different instances of ErrorLog all go to the same destinations; // they differ only in what module name is used when something is // logged to one of them or the other. errlog.setModule( "Sample framework" ); // $$ 11:1 // Step 6: (Optional) Affect format control for any destination where the // default format is inappropriate. logfile.setLineLength (72); // default was 80. You may make it bigger // or smaller. errlog.setModule( "Sample framework" ); // $$ 11:1 // Step X: Any other setup work needed by the framework can be done after // (or before if no error messages could be logged) the ErrorLogger // control is set up. errlog ( ELsuccess, "Setup Completed" ) << endmsg; // ELsuccess is a very low severity level, suitable for marking // progress milestones in logs that want to pay attention to // those. } // SampleFramework::setup() // ----------- // eventLoop() // ----------- void SampleFramework::eventLoop() { const int Nruns = 6; const int Nevents [7] = { 0, 100, 50, 75, 60, 40, 50 }; // Loop over Runs. Summarize statistics every other run. eventN = 0; for ( runR = 1; runR <= Nruns; runR++) { // Normally here there is code to start processing for a run -- maybe // open a file, do calibration, or whatever. Here, the work is trivial. int Nev = Nevents[runR]; errlog ( ELinfo, "Starting Run " ) << runR << " with " << Nev << " events" << endmsg; // Loop over Events int ev; for ( ev = 0; ev < Nev; ev++, eventN++ ) { // Normally a production program would not output anything here, but // just to see where this sample is up to ... cout << ">>>>>>> Up to Run " << runR << " event " << ev << " (" << eventN << ")\n"; // Normally, you read in an Event here. Instead, in this sample we just // fill in the Event object. Event event; event.datum = ev * ev + 100000 * runR; // Now do every step in the eventSteps. But if there is a serious error // in a step, then break out of those steps and move to the next event. // Actually, a severity level a bit more severe than just ELerror is // provided to mean just that: ELnextEvent. logger->checkSeverity(); // Just to clear it out. std::list::iterator m; ELseverityLevel sv; errlog( ELsuccess, "Event Started" ) << "datum= " << event.datum << endmsg; //ErrorObj hereWeGo( ELsuccess, "Duplicate Start" ); //hereWeGo << "datum= " << event.datum; //errlog( hereWeGo ); for ( m = eventSteps.begin(); m != eventSteps.end(); m++ ) { (*m)->invoke(event); sv = logger->checkSeverity(); if ( sv >= ELnextEvent ) { errlog (ELnextEvent, "Next Event") << "Framework is skipping the rest of processing event " << eventN << "due to error of severity" << sv.getName() << endmsg; break; // Break out of the loop doing event steps } } // for (m) } // Loop over events // Emit statistics every other run. if ( (runR%2) == 0 ) { stats.summary (logfile, "Even Run Statistics"); // $$ 15:1 } } // Loop over runR stats.summary (cout, "End of Job Statistics"); // $$ 15:2 } // SampleFramework::eventLoop() // ---------------------------------- // Methods to obtain run/event number // ---------------------------------- int SampleFramework::getRun() const { return runR; } int SampleFramework::getEventNum() const { return eventN; }