|
||
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.
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.
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.
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.
The CLogEvent
class encapsulates data about
communication events.
The ID is the unique identifier of the event.
The remote party is the device originating or receiving the communication.
The direction indicates whether the communication is incoming or outgoing.
The time of the event is the time at which it began.
The duration type is one of three constants:
KLogDurationNone
, KLogDurationData
or
KLogDurationValid
. You typically use them where the event
being represented either has no duration, or has a duration of no relevance
(such as a data transfer), or has a valid duration (such as a voice call).
The duration is the duration in seconds from the start of the event to the end.
The delivery status is one of a set of flags indicating whether the communication was successfully delivered.
The subject is a string to be displayed in the subject line of the viewer application.
The log link is the ID of the event in the calling application, as distinct from its ID in the log.
The description is a human readable description of the event type.
There is only one flag, KLogEventRead
, which
is set to true if the event has been read by a client. It can be cleared either
by passing the constant KLogFlagsMask
to the function
SetFlags()
or by calling the function
ClearFlags()
.
The log engine API defines constants of type
TUid
representing the standard types of communication
event which can be logged.
KLogCallEventType
: voice call
KLogDataEventType
: data call
KLogFaxEventType
: fax call
KLogShortMessageEventType
: SMS call
KLogMailEventType
: email
KLogTaskSchedulerEventType
: task scheduler
event
The log engine API provides the class
CLogEventType
which acts on all events of a specified
type. Its functions are:
Uid()
and SetUid()
get
and set the UID identifying the event type.
Description()
and
SetDescription()
get and set the description in words of
the event type.
LoggingEnabled()
and
SetLoggingEnabled()
get and set a flag which enables
events of this type to be logged.
Copy()
copies an existing event type.
InternaliseL()
and
ExternaliseL()
implement streaming input and output of an
event type object.
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);
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.
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.
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.
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.
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:
HandleLogClientChangeEventL
Its arguments are
TUid
aChangeType the UID of the type of event
which occurred
TInt
aChangeParameter1
TInt
aChangeParameter2
TInt
aChangeParameter3
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:
HandleLogViewChangeEventAddedL
HandleLogViewChangeEventChangedL
HandleLogViewChangeEventDeletedL
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
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:
Event()
FirstL()
LastL()
NextL()
PreviousL()
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.
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.
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:
iMaxLogSize
, the maximum size of an event log,
iMaxRecentLogSize
, the maximum size of a recent list,
and
iMaxEventAge
, the maximum age of a logged event.
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.