Symbian
Symbian Developer Library

SYMBIAN OS V9.4

Feedback

[Index] [Previous] [Next]


How To Use the Log Engine

[Top]


Purpose

The log engine stores events generated by operating system components, particularly telephony and messaging. This document explains how to use the log engine to create a record of events and how to create views of the events for display.

[Top]


Introduction

The log engine uses a client-server architecture. The log server is a permanent process maintaining event data and the log clients attach to it to add and retrieve data. When writing an application which uses the log engine you create a log client wrapped in a customised component. This component has two purposes. Firstly it causes the server to log events of a specified kind. Secondly it uses log filter and log view classes to extract and present the data appropriately.

The log engine is designed to abstract from the details of how events are notified, stored and displayed. You do not need to know exactly how this functionality is implemented.

The log engine is an interface to a database holding records of communication events. You do not need to know the implementation of the database, merely that the log engine has functionality to add, read, modify and delete records.

Not all devices are equipped with logging functionality. You need to determine whether your application will only run on platforms with logging functionality guaranteed or else write the application so as to fail gracefully. For this reason the class CLogClient and its parent class CLogBase are often wrapped in a class CLogWrapper which handles cases where logging functionality is absent.

Logging calls are asynchronous. The application developer is responsible for maintaining the integrity of the log client session. A typical way of doing this is to maintain a queue of log events.

The log engine provides limited configuration functionality. If you are involved in device creation and want to tune performance you need to know how to manage memory and configure the logging component explicitly: the topic is not covered in this document.

The log engine models standard types of communication event by default. If your application involves non-standard functionality you need to know how to create user defined data types to represent non-standard events.

The log engine uses the Symbian OS capabilities model of security. This means that your application must police its own security and you must therefore know the security capabilities required by each item of functionality.

The purpose of the log engine is to provide data for a viewer application which displays information such as missed calls to the end user of the device. You do not need to know anything about the viewer, merely that it requires data views to be provided in certain data structures.

The log engine gets updated by an observer component which notifies it of new communication events affecting either the data tables or the views created from them. You do not need to know the details of the interprocess communication, merely that an observer component exists and performs notifications.

[Top]


Log clients and log views

The log engine API consists of two types of classes, the client classes and the view classes. The client classes exist to create logs and maintain and modify them. They are CLogClient, CLogBase and CLogWrapper. It is CLogClient which contains the actual functionality: the purpose of CLogBase and CLogWrapper is to provide default functionality when logging functionality is not available. The view classes exist to filter the log data and prepare it for presentation in a viewer application. They are CLogViewEvent and CLogViewRecent, which inherit from a class CLogView which cannot be instantiated.

[Top]


Events and event types

The log engine is designed to log events having to do with communications: messaging, telephony, email and so on. The class CLogEvent represents events of this kind: its member objects mainly have to do with types of communication. The class CLogEventType contains information about the event type which a particular event belongs to.


Log events

The CLogEvent class encapsulates data about communication events.


Log event types

The log engine API defines constants of type TUid representing the standard types of communication event which can be logged.

The log engine API provides the class CLogEventType which acts on all events of a specified type. Its functions are:

You may need an event type not supplied with the log engine API, for instance if you are developing a new kind of messaging application. A new event type requires a new unique Id which must be allocated by Symbian Signed. Once you have that Id you can create a CLogEventType and pass the Id to SetUid() just like the standard event type constants.

The main use of CLogEventType is to enable or disable logging for all events of a particular type. You do this by creating a CLogEventType object with its UID set to the relevant event type and calling its SetLoggingEnabled() function with ETrue or EFalse as argument.

It is important to know that only an explicit call to the SetLoggingEnabled() function of a CLogEventType object will cause logging to take place. For instance if you create a new event type myEventType and want such events to be logged, you have to call its SetLoggingEnabled() function before logging will occur.

    CLogEventType* myEventType = CLogEventType::NewL();myEventType->SetLoggingEnabled(ETrue);

[Top]


Log engine and platform security

The Symbian OS uses a capabilities model of security in which an application being called verifies that the calling application has the required security policies before returning data. This is important in connection with message logging which involves the end user's confidential information. When writing an application which incorporates logging functionality you must ensure that it has the capabilities to call the log engine APIs.

To call a log client, a calling application must have the capabilities required by the event type of the event being accessed. This means that a call to CLogClient::GetEvent() must have the capability specified in the read policy of the event type, while calls to AddEvent(), ChangeEvent() and DeleteEvent() must have the capability specified in the write policy. To modify the event type by calling AddEventType(), ChangeEventType() and DeleteEventType() a calling application requires the WriteDeviceData capability. No capability is required to call GetEventType().

No security capability is required to access the view classes.

[Top]


How to set up a log engine

If you are certain that you are coding for a platform with logging functionality you create a custom class with some such name as CMyLogEngine. It should have members of the classes CLogClient, CLogViewEvent, CLogViewRecent and CLogFilter and an observer of some kind. The functions of your custom class will use the functionality of these classes by calls to the member objects.

void CMyLogEngine::ConstructL()
    {
    
    // Establish connection to log engine
    iLogClient = CLogClient::NewL(iFs);
 
    // Log view and view for recent events events with standard priority
    iLogViewEvent = CLogViewEvent::NewL(*iLogClient);
    iLogViewRecent = CLogViewRecent::NewL(*iLogClient);

    // Filter for events
    iLogFilter = CLogFilter::NewL();

If you are not certain that your platform will provide logging functionality you must use a log wrapper framework. Write a custom class to use the log wrapper functionality with some such name as myLogWrapper. It should have two members which own CLogWrapper and CLogClient objects. In the constructor function of myLogWrapper call the ClientAvailable() function of CLogWrapper to determine whether logging functionality is present. If so, construct the CLogClient member: if not, leave. The effect of this is that calls to the log client functionality of myLogWrapper will access an actual log client if the functionality is available. However, if the functionality is not available calls to the log client functionality will either have no effect or else leave as in this example code.

void CMyLogWrapper::ConstructL(RFs& aFs)
     {
     
     // create the CLogWrapper to forward requests to.
     iLogWrapper = CLogWrapper::NewL(aFs);
 
     if (iLogWrapper->ClientAvailable())
         {
         iLogClient = static_cast<CLogClient*>(&iLogWrapper->Log());
         }
     else
         {
         User::Leave(KErrNotSupported);
         }
     }

You can now use myLogWrapper as if it was a log client.

[Top]


How to maintain an event log

You maintain a log by adding, changing and deleting events with the appropriate calls to the CLogClient member of your application. When you are adding or changing events, the arguments are a CLogEvent and a TRequestStatus object to hold the return value. When you are deleting, they are a CLogEvent and a TLogId.

You typically declare an event as a member of your log client class

CLogEvent* iCurrentEvent;

and initialise it using its member functions.

iCurrentLogEvent = CLogEvent::NewL();
...
iCurrentLogEvent->SetTime(time);

You add an event to a log with the AddEvent() function of a CLogClient object.

iLogClient->AddEvent(*iLogEvent, iStatus);

AddEvent() creates an asynchronous request and no other asynchronous request must be outstanding when you call it. For this reason, the CLogEvent argument is typically read off a queue of events waiting to be processed.

You change an event record with the ChangeEvent() function.

iLogClient->ChangeEvent(*iLogEvent, iStatus);

Note that it is not possible to change the event type of an event after it has been added to the log.

You do not normally need to delete an event. A log has a maximum size which the log engine maintains automatically by deleting the oldest events when newly added events would cause the maximum to be exceeded. If you want to delete an event for other reasons you call the DeleteEvent() function. iLogClient->DeleteEvent(aLogId, iStatus);

It is important to know that deleting an event type and deleting the actual events of that type are two separate actions. If you just delete the type and not the events, the events will remain in the log but have no type.

[Top]


How to add an event type

We saw that it is sometimes necessary to create a custom event type. If you want to log events of a custom created event type, you need to initialise a CLogEventType object and add it to the log. You add the event type when you install your application, and delete it if you uninstall the application. You add, change and delete custom event types by calling the appropriate functions of a CLogClient object:

iLogClient->AddEventType(*iLogEventType, iStatus);

iLogClient->ChangeEventType(*iLogEventType, iStatus);

iLogClient->DeleteEventType(aId, iStatus);

The arguments are a CLogEventType (or TUid when deleting) and a TRequestStatus object.

[Top]


How to request notifications of change on a log

The log engine records events so that other applications can react to them in ways which are not part of its own specification. A typical reaction to an event might be refreshing the screen of the device or causing it to beep, but that is not log engine functionality. What the log engine does is notify other applications of a change to its database. It provides two interfaces which you implement to set up notification of changes:

MLogClientChangeObserver concerns changes which are global to the database and it has a single member function to be implemented:

Its arguments are

The last three arguments are context-specific: their meaning is specific to the type of event that occurred.

MLogViewChangeObserver concerns changes which are specific to a database view and it has three member functions to be implemented:

Each of them has the same arguments:

A change to a view typically occurs as part of a sequence of changes, and the position of the change event record in the view typically reflects this fact.

The argument aId is the Id of the log event in the database. The argument aViewIndex is the position in the view where the event record was added, changed or deleted. The argument aChangeIndex is the index of the event in the sequence of changes. The argument aTotalChangeCount is the total number of events in the sequence of changes

[Top]


How to display a log

You create a view to be displayed in the viewer application by creating a CLogViewEvent or a CLogViewRecent. Both classes select entries from the log, usually subject to the constraint of a filter.

    iLogViewEvent->SetFilterL(*iLogFilter,iStatus)

A CLogViewRecent has additional functionality to identify (and therefore filter out) duplicate events. A view is a purely virtual table and its add and delete functions have no effect on the log itself.

CLogViewEvent and CLogViewRecent inherit functions from CLogView which let you navigate through the items in the view:

A view is a list of events ordered chronologically, earliest first. A view always has a current event, which you retrieve with Event(). When you create a view the current event is the first event, and you navigate through the list using the other functions which take a TRequestStatus as argument.

To get the first event in the view call

if(CViewEvent.FirstL(aStatus))
  {
  User::WaitForRequest(aStatus); 
  CLogEvent e = iViewEvent.Event(); 
  }

To get the last event in the view call

if(CViewEvent.LastL(aStatus))
  {
  User::WaitForRequest(aStatus); 
  CLogEvent e = iViewEvent.Event(); 
  };

To get all the events in the view call

if(CViewEvent.FirstL(aStatus))
  {
  User::WaitForRequest(aStatus); 
  CLogEvent e = iViewEvent.Event(); 
  }

and then inside a loop call

while(CViewEvent.NextL(aStatus))
  {
  User::WaitForRequest(aStatus); 
  CLogEvent e = iViewEvent.Event(); 
  }

A recent view is similar to an event view but is designed specifically for the purpose of displaying such recent events as the distinct voice calls a device user has received since the last time of checking. In a recent view, the first event is the most recent.

[Top]


Using filters when displaying a log

An event view typically contains only a selection of items from the main log which satisfy the constraints of a filter. A CLogFilter has members representing the same information as a CLogEvent object but is used to select on data not modify it. (In the database implementation, a filter corresponds to the WHERE clause of a SELECT query.) An event view is empty when created. To initialise it you first create and initialise a CLogFilter object and then pass this as a parameter of the SetFilterL() function of the event view. You may also want to initialise a view to satisfy any one of several filters, in which case you pass in a CLogFilterList: this is an array of several CLogFilter objects. You would do this, for instance, if you wanted to create a view to display events of several different event types such as voice mail, SMS and email, since a single CLogFilter only has one event type.

[Top]


How to configure the log engine

There are three parameters which you can configure at run time using a log client. They are implemented as the three members of a TLogConfig class:

To change their values you create a TLogConfig object, setting its members to the required values, and pass it to the ChangeConfig() function of a log client.

The TLogConfig class also has the two functions InternalizeL() and ExternalizeL() which implement streaming input and output of a TLogConfig object.