Logging Facility

The logging facility provides you with a mechanism for generating and capturing messages generated by your application. These can be automatically saved to a log file, or intercepted and dealt with in some other way. The majority of functionality for this feature is provided by the "OTC_Logger" class in the OSE C++ class library.

Some of the features of the logging facility are optional and controlled via environment variables. You should consult the manual page for the "OTC_Logger" class and the general OSE C++ class library manual as a number of these features will not be described here or covered only briefly.

Logging a Message

The logging facility provides you with the ability to log a message string with a specified priority or level assigned to it. The level is analogous to that used by the UNIX function called "syslog()".

Level

Purpose

LOG_EMERGENCY

A panic condition.

LOG_ALERT

A condition that should be corrected immediately, such as a corrupted system database.

LOG_CRITICAL

Critical conditions, such as hard device errors.

LOG_ERROR

Errors.

LOG_WARNING

Warning messages.

LOG_NOTICE

Conditions that are not error conditions, but that may require special handling.

LOG_INFO

Informational messages.

LOG_DEBUG

Message that contain information normally of use only when debugging a program.

To log a message, a handle to an instance of the Logger class is acquired and the "notify()" member function is called.

   1 import netsvc
   2 logger = netsvc.Logger()
   3 logger.notify(netsvc.LOG_DEBUG, "message")

The format of a message when displayed will be::

DEBUG: message

The string before the ":" corresponds to the level assigned to the message. The remainder of the line after the ":" is the actual message. If you wish to have the time and process ID appear in the prefix, call the "enableLongFormat()" member function. Whether the longer form of prefix is enabled can be queried using the "longFormatEnabled()" member function. It can be disabled using the "disableLongFormat()" member function.

By default, messages will appear on the standard error output. If you wish to disable the display of messages onto the standard error output, call the "disableStderrOutput()" member function. Conversely, the "enableStderrOutput()" member function can be called to enable display of messages onto the standard error output if previously disabled. Whether messages are currently being displayed onto the standard error output can be queried by calling the member function "stderrOutputEnabled()".

Specifying a Log File

At any time, messages can be captured into a file by specifying the name of a log file using the member function "setLogFile()". If a log file is currently in use, the name of the log file can be queried using the "logFile()" member function.

   1 logger.setLogFile("/var/tmp/application.log")

The string used to specify the name of a log file may incorporate the following special tags.

Tag

Purpose

%h

Will encode the hostname of the machine into the name of the log file.

%p

Will encode the process ID into the name of the log file.

%Y

Will encode the current year as 4 digits into the name of the log file.

%y

Will encode the current year as 2 digits into the name of the log file.

%m

Will encode the current month of the year as a zero padded 2 digit number into the name of the log file.

%d

Will encode the current day of month as a zero padded 2 digit number into the name of the log file.

When the tags corresponding to dates are used, a new log file will automatically be created when the value corresponding to a date component changes. The following will for example result in a new log file being created each day.

   1 logger.setLogFile("/var/tmp/application-%Y-%m-%d.log")

Note that older log files will not be removed automatically, so some other mechanism such as a cron job will need to be employed to remove them.

The name of a log file can also be set using the "OTCLIB_LOGFILE" environment variable instead of calling "setLogFile()". Similarly, output to the standard error output can be disabled using the "OTCLIB_NOLOGSTDERR" environment variable and the inclusion of the time and the process ID in the message prefix enabled using the "OTCLIB_LOGLONGFORMAT" environment variable. If used, these environment variables must be set before the application is run or at least before the "netsvc" module is imported for the first time.

   1 import os
   2 os.putenv("OTCLIB_LOGFILE", "/var/tmp/application-%Y-%m-%d.log")
   3 import netsvc

When an application first attempts to open a log file, if it already exists it will be truncated. If you do not want the log file truncated, but want messages to be appended to an existing log file, the "OTCLIB_APPENDLOGFILE" environment variable must be set. Again, this needs to be set prior to the application being run or at least before the "netsvc" module is imported for the first time.

Note that if any of these environment variables are used, but calls are subsequently made to the corresponding member functions of the "Logger" class from within the application, the values of the environment variables will effectively be overridden from that point onwards.

Specifying a Log Channel

When logging a message, a log channel may also be specified. If the name of a log channel starts with a character other than an alphanumeric character, the message will not be displayed on the standard error output or appear in the log file. If it is displayed or captured in the log file, the name of the log channel does not appear anywhere in the message. The intent of the log channel is to allow one part of an application to capture specific messages produced by another part of the application and deal with them in a special way.

To log a message against a specific log channel, the member function "notifyChannel()" is used. The name of the log channel is supplied as the first argument.

   1 logger.notifyChannel("VISIBLE", netsvc.LOG_DEBUG, "message")
   2 logger.notifyChannel("#HIDDEN", netsvc.LOG_DEBUG, "message")

Messages logged against a specific log channel, can be captured by calling the member function "monitorChannel()", supplying the name of the log channel and a callback function.

   1 def callback(channel, level, message):
   2     print (channel, level, message)
   3     logger.monitorChannel("#HIDDEN", callback)

The message supplied to the callback function is the original message and does not contain the prefix describing the priority or level assigned to the message, nor does it contain any details relating to the current time or process ID. If you are going to subsequently log the message to your own file, you would need to add these details yourself if you require them.

Only one callback can be associated with a particular log channel. If multiple callbacks are required for a particular log channel, separate instances of the Logger class should be used. To stop monitoring a specific log channel, the member function "monitorChannel()" is called again but with "None" supplied in place of the callback function.

If the callback function was a member of a class, it is important to deregister the callback, else a reference to the instance of the class will be maintained and it may not get deleted. You can also deregister all of the callbacks associated with a particular instance of the "Logger" class by calling the member function "destroyCallbacks()". This would be necessary if the class containing the callbacks also held a reference to the instance of the "Logger" class. In this case, a circular reference would exist and neither object would ever be destroyed.

Logging Python Exceptions

To make the task of logging details of a Python exception easier, the "logException()" function is provided by the "netsvc" module. This function should only be called from within the context of a Python "except" clause. The information logged is similar to that displayed by Python when an exception is not caught and includes details of the exception and a stack trace.

   1 try:
   2     function()
   3 except SystemExit:
   4     raise
   5 except:
   6     netsvc.logException()
   7     sys.exit()

The details of the exception are logged with level "LOG_ERROR" and a specific log channel is not specified. If you wanted to log the details of the exception to a specific log channel, or vary the level, you can use the "exceptionDetails()" function of the "netsvc" module to obtain the same information that would be logged by the "logException()" function and then call the "notify()" member function of an instance of the "Logger" class yourself.

   1 try:
   2     function()
   3 except SystemExit:
   4     raise
   5 except:
   6     details = netsvc.exceptionDetails()
   7     logger.notifyChannel("WARNING", netsvc.LOG_WARNING, details)
   8     pass

If you don't want the stack trace and only want the description of the exception, use the function "exceptionDescription()" instead. The result of calling either of these functions need not be used with the logger, but could be displayed using any other available mechanism as well.

Note that the "exceptionDetails()" and "exceptionDescription()" functions are also available in the "netrpc" module if you are using that in a standalone client application.

Exceptions in a Callback

Whenever a callback is executed, it occurs as a result of a call from C++ code into Python code. Because of the mix of C++ code and Python code, if an exception occurs within the callback function, Python can't by itself properly shutdown the application. This is further complicated by the fact that a callback can be called within the context of a callback from the event dispatcher.

As a consequence, when any callback into Python code from C++ occurs, if a Python exception occurs and the callback itself doesn't catch it and deal with it, it will be caught with the details of the exception being logged. The event dispatcher will then be stopped if it is running and the "SystemExit" exception raised in order to prevent Python from running any further code. The outcome is the same as when only Python code is being used, except that the details of the exception are displayed using the logging facility rather than being dumped directly onto the standard error output.


CategoryOSE

OSE/Python/LoggingFacility (last edited 2006-09-05 05:59:19 by GrahamDumpleton)