A filter is a specialized client that receives trace messages and, unlike regular clients, also forwards these messages to other clients or filters.
CAUTION: To avoid losing trace messages, always connect all the filters and clients to each other first, before making a final connection from the first filter to the manager (see Section 6.6.1).
The Execution Tracing package provides two concrete filters, RWTraceLevelFilter and RWTraceMultiClientFilter.
The RWTraceLevelFilter filter is used to dynamically filter trace events at runtime. The setLevel() function can be used by your program to reset the filter's cut-off level at any time during your program's execution. This filter and two other ways of filtering trace events are compared in Section 6.10, "Controlling Trace Output."
Example 57 shows how to use the RWTraceLevelFilter class:
#define RW_USER_TRACE_LEVEL 8 //1 #include <rw/trace/trace.h> int main() { // create trace client that logs messages to cerr RWTraceOstreamClient myTraceClient(cerr); //2 // create filter that only lets info and more important events // pass through RWTraceLevelFilter myTraceLevelFilter(RW_TRACE_LEVEL_INFO); //3 // connect client to level filter myTraceClient.connect(myTraceLevelFilter); //4 // connect level filter to manager myTraceLevelFilter.connectToManager(); //5 RW_USER_TRACEABLE_FUNCTION("main"); //6 // generate some trace information RW_USER_TRACE_INFO("This info level message should be seen."); RW_USER_TRACE_WARNING("This warning level message should be seen."); RW_USER_TRACE_ERROR("This error level message should be seen."); // change the level myTraceLevelFilter.setLevel(RW_TRACE_LEVEL_ERROR); //7 // generate some more trace information RW_USER_TRACE_INFO("This info level message should not be seen."); RW_USER_TRACE_WARNING("This warning level message should" "not be seen."); RW_USER_TRACE_ERROR("This error level message should be seen."); return 0; }
The following is a description of each program line:
//1 | Compile in all trace macros by setting the user trace level macro to 8. This line sets the maximum trace level to compile into the code. To compile in all the trace macros, the level is set to the maximum 8 (Entry/Exit). You could also define the trace level on the command line for your compiler, using the -DRW_USER_TRACE_LEVEL=8 flag. |
//2 | Create a normal ostream client to display the trace information. |
//3 | Instantiate an RWTraceLevelFilter. The constructor takes the maximum level of trace messages to pass through. You can pass an integer level number (see Section 6.1.3) or use one of the symbolic constants, as in this line. Pass 0 to filter out all trace messages. |
//4 | Connect the client to the level filter. |
//5 | Then connect the filter to the trace manager. |
//6 | Declare the function as traceable, so you can use trace event generation macros in this function. |
//7 | Change the cutoff level on the level filter, using the setLevel() function. |
The symbolic constants referred to in line //3 are defined in the file rw/trace/RWTraceEventSeverity.h. They cannot be used when setting the RW_USER_TRACE_LEVEL macro, because this macro needs to be set before including any trace header files.
The constants are: RW_TRACE_LEVEL_FATAL, RW_TRACE_LEVEL_ERROR, RW_TRACE_LEVEL_WARNING, RW_TRACE_LEVEL_INFO, RW_TRACE_LEVEL_TEST, RW_TRACE_LEVEL_DEBUG, RW_TRACE_LEVEL_ENTRY and RW_TRACE_LEVEL_NONE. They all map directly to the appropriate severity level. RW_TRACE_LEVEL_NONE filters out all trace messages. It is equivalent to zero.
Sometimes it is desirable to connect multiple clients to the manager or a filter. The RWTraceMultiClientFilter class facilitates this. Example 58 creates two clients; one displays messages on the screen, and the other saves them to a file. This example can be found in examples\thr0200osfam\trace\example2.cpp.
#define RW_USER_TRACE_LEVEL 8 #include <rw/trace/trace.h> #include <fstream.h> int main() { ofstream traceLog("trace.log"); RWTraceOstreamClient myFileTraceClient(traceLog); RWTraceOstreamClient myCerrTraceClient(cerr); RWTraceMultiClientFilter myMultiFilter; // 1 // first: connect two clients to multi client filter myFileTraceClient.connect(myMultiFilter); // 2 myCerrTraceClient.connect(myMultiFilter); // last: connect filter to singleton manager myMultiFilter.connectToManager(); // 3 RW_USER_TRACEABLE_FUNCTION("main"); // 4 RW_USER_TRACE_DEBUG("Picked up pencil."); RW_USER_TRACE_TEST("Visual inspection of pencil complete."); RW_USER_TRACE_INFO("Doodling!"); ... return 0; }
The following is a description of each program line:
//1 | Instantiate the multi-client filter. |
//2 | First connect the two clients to the filter. |
//3 | Then connect the filter to the manager to ensure that no trace messages are lost. |
//4 | Generate some trace messages. |
Instead of using only one filter attached directly to the trace manager, you can chain together several filters. To connect them, use the downstream filter's connect() member function and pass it the upstream filter as an argument, similar to this:
myMultiFilter2.connect(myMultiFilter1);
Creating a user-defined filter follows the same pattern as creating a user-defined client. You must create a derived body class, where you specify the rules for filtering. If your filter's body class has any new public members (in addition to those inherited from its base classes), you must also create a corresponding derived handle class. The predefined Trace Level filter was created in the same way that you would create your own filter, so the source code for RWTraceLevelFilter and RWTraceLevelFilterImp is an example.
The filter's implementation must derive from RWTraceSingleClientFilterImp, for a single-client filter, or RWTraceMultiClientFilterImp, for a multiple-client filter.
The doTrace() function. You specify the rules for filtering by redefining the doTrace() member function, which is inherited from RWTraceEventClientImp. The RWTraceEventClientImp::doTrace() function is declared pure-virtual, so a derived body class must provide an implementation of this function.
For a single-client filter, check that the event meets your filter's criteria and forward it to the connected client, using the trace() member function. Example 59 is taken from the source code for RWTraceLevelFilterImp.
void RWTraceLevelFilterImp::doTrace(const RWTraceEvent& ev) { if (getClient() && (level_ >= ev.getSeverity()) ) getClient()->trace(ev); }
For a multi-client filter, iterate over the connected clients and selectively forward the trace events, based on your filter's criteria. Example 60 is taken from the source code for RWTraceMultiClientFilterImp.
void RWTraceMultiClientFilterImp::doTrace(const RWTraceEvent& ev) { { RWGUARD(getMutex()); for(unsigned i=0; i < clientPs_.length(); i++ ) clientPs_(i)->trace(ev); } }
NOTE: Do not embed trace macros in the filter's trace event processing code. To prevent infinite recursion, the trace manager ignores any trace events generated by a filter.
The static make() function. If you are using the predefined handle class, your body class must supply a static make() function. (If you are creating a derived handle class, you can skip to Section 6.8.3.2.) The make() function constructs a body and returns a handle to it. This example is taken from the source code for RWTraceLevelFilterImp.
RWTraceSingleClientFilter RWTraceLevelFilterImp::make(RWTraceEventSeverity level) { return new RWTraceLevelFilterImp(level); }
NOTE: The returned handle's type is actually the parent class of your derived handle.
If your filter's body class has any new public members (in addition to those inherited from its base classes), you must create a derived handle class that forwards those calls to your body. Derive your handle from RWTraceMultiClientFilter, for a multiple-client filter, or RWTraceSingleClientFilter, for a single-client filter. For an example, see the source code for RWTraceLevelFilter:
If you created your own derived handle class, you can instantiate your derived filter like this:
MyMultiClientFilter myMultiFilter;
If you did not create your own derived handle class, you must explicitly instantiate a body with your body class's make() function and pass the body to a handle of type RWTraceEventFilter, as shown in this example:
RWTraceEventFilter myTraceFilter ( Test2TraceFilterImp::make() );
Use your filter as you would any predefined filter, as discussed in Section 6.8.1.
©Copyright 2000, Rogue Wave Software, Inc.
Contact Rogue Wave about documentation or support issues.