Logging¶
The best way to log traces in iPOPO is to use the logging module from the Python Standad Library. Pelix/iPOPO relies on this module for its own logs, using a module level constant providing a logger with the name of the module, like this:
import logging
_logger = logging.getLogger(__name__)
That being said, Pelix/iPOPO provides a utility log service matching the OSGi LogService specification, which logs to and reads traces from the standard Python logging system.
The log service is provided by the pelix.misc.log
bundle. It handles
LogEntry
object keeping track of the log timestamp, source bundle and
message. It also registers as a handler to the Python logging system, which
means it can also keep track of all traces logged with the logging
module.
API¶
Once install and started, the pelix.misc.log
bundle provides two services:
pelix.log
: The main log service, which allows to log entries;pelix.log.reader
: The log reader service, which gives a read-only access to previous log entries. Those entries can be stored using either the log service or the Python logging system.
Log Service¶
The log service provides the following method:
-
class
pelix.misc.log.
LogServiceInstance
(reader, bundle)¶ Instance of the log service given to a bundle by the factory
Parameters: - reader – The Log Reader service
- bundle – Bundle associated to this instance
-
log
(level, message, exc_info=None, reference=None)¶ Logs a message, possibly with an exception
Parameters: - level – Severity of the message (Python logging level)
- message – Human readable message
- exc_info – The exception context (sys.exc_info()), if any
- reference – The ServiceReference associated to the log
Log Reader Service¶
The log reader provides the following methods:
-
class
pelix.misc.log.
LogReaderService
(context, max_entries)¶ The LogReader service
Parameters: - context – The bundle context
- max_entries – Maximum stored entries
-
add_log_listener
(listener)¶ Subscribes a listener to log events.
A log listener is an object providing with a
logged
method, with the following signature:def logged(self, log_entry): ''' A log entry (LogEntry) has been added to the log service ''' # ...
Parameters: listener – A new listener
-
get_log
()¶ Returns the logs events kept by the service
Returns: A tuple of log entries
-
remove_log_listener
(listener)¶ Unsubscribes a listener from log events.
Parameters: listener – The listener to remove
The result of get_log()
and the argument to listeners
registered with add_log_listener()
is a
LogEntry
object, giving read-only access to the following properties:
-
class
pelix.misc.log.
LogEntry
(level, message, exception, bundle, reference)¶ Represents a log entry
Parameters: - level – The Python log level of the entry
- message – A human readable message
- exception – The exception associated to the entry
- bundle – The bundle that created the entry
- reference – The service reference associated to the entry
-
bundle
¶ The bundle that created this entry
-
exception
¶ The exception associated to this entry
-
level
¶ The log level of this entry (Python constant)
-
message
¶ The message associated to this entry
-
osgi_level
¶ The log level of this entry (OSGi constant)
-
reference
¶ The reference to the service associated to this entry
-
time
¶ The timestamp of this entry
Note
LogEntry
is a read-only bean which can’t be un-marshalled by
Pelix Remote Services transport providers. As a consequence, it is not
possible to get the content of a remote log service as is.
Sample Usage¶
Using the shell is pretty straightforward, as it can be seen in the
pelix.shell.log
bundle.
import logging
from pelix.ipopo.decorators import ComponentFactory, Requires, Instantiate, \
Validate, Invalidate
from pelix.misc import LOG_SERVICE, LOG_READER_SERVICE
@ComponentFactory("log-sample-factory")
@Requires("_logger", LOG_SERVICE)
@Requires("_reader", LOG_READER_SERVICE)
@Instantiate("log-sample")
class SampleLog(object):
"""
Provides shell commands to print the content of the log service
"""
def __init__(self):
self._logger = None
self._reader = None
@Validate
def _validate(self, context):
self._reader.add_log_listener(self)
self._logger.log(logging.INFO, "Component validated")
@Invalidate
def _invalidate(self, context):
self._logger.log(logging.WARNING, "Component invalidated")
self._reader.remove_log_listener(self)
def logged(self, entry):
print("Got a log:", entry.message, "at level", entry.level)
The log service is provided by a service factory, therefore the components of a same bundle share the same service, and each bundle has a different instance of the logger. The log reader service is a singleton service.
Shell Commands¶
The pelix.shell.log
bundle provides a set of commands in the log
shell
namespace, to interact with the log services:
Command | Description |
---|---|
log | Prints the last N entries with level higher than the given one (WARNING by default) |
debug | Logs a message at DEBUG level |
info | Logs a message at INFO level |
warning | Logs a message at WARNING level |
warn | An alias of the warning command |
error | Logs a message at ERROR level |
$ install pelix.misc.log
Bundle ID: 12
$ start $?
Starting bundle 12 (pelix.misc.log)...
$ install pelix.shell.log
Bundle ID: 13
$ start $?
Starting bundle 13 (pelix.shell.log)...
$ debug "Some debug log"
$ info "..INFO.."
$ warning !!WARN!!
$ error oops
$ log 3
WARNING :: 2017-03-10 12:06:29.131131 :: pelix.shell.log :: !!WARN!!
ERROR :: 2017-03-10 12:06:31.884023 :: pelix.shell.log :: oops
$ log info
INFO :: 2017-03-10 12:06:26.331350 :: pelix.shell.log :: ..INFO..
WARNING :: 2017-03-10 12:06:29.131131 :: pelix.shell.log :: !!WARN!!
ERROR :: 2017-03-10 12:06:31.884023 :: pelix.shell.log :: oops
$ log info 2
WARNING :: 2017-03-10 12:06:29.131131 :: pelix.shell.log :: !!WARN!!
ERROR :: 2017-03-10 12:06:31.884023 :: pelix.shell.log :: oops
$