WeeWX v4 and logging - weewx/weewx GitHub Wiki
Overview
WeeWX V3 uses Python's syslog
module to log events. It has
several disadvantages:
- It works only under versions of *nix. No Windows.
- It can log only to the system log.
- It is inflexible in the formatting it uses.
WeeWX V4 and later use Python's
logging
package. It has
several advantages:
- It abstracts out logging destinations, formatting, and filtering, allowing all to be changed at runtime.
- It has destinations that work on Windows.
- It can support logging to email, sockets, and rotating files.
In short, it is a much more flexible facility. Plus, it's easy to use.
Although the logging
module provides many more options and
abilities than the syslog
module, in its default configuration,
WeeWX still sends log messages to the system logger so that the
system will handle log file rotation, filtering of log messages,
sending messages to remote log servers, etc.
This is a guide to how logging
is implemented within WeeWX.
Configuring logging
The big advantage of the logging
module is its ability to be extensively
customized. By default, WeeWX makes sensible choices, but you may want to change
them. This section is about how to do this.
First, read, or, at least, attempt to read, the section on the schema of the
configuration dictionary
that logging
uses. It's dense and hard to follow, but it will eventually sink
in.
Defaults
With that in mind, here is the default configuration that WeeWX uses:
[Logging]
version = 1
disable_existing_loggers = False
# Root logger
[root](/weewx/weewx/wiki/root)
level = {log_level}
handlers = syslog,
# Additional loggers would go in the following section. This is useful for
# tailoring logging for individual modules.
[loggers](/weewx/weewx/wiki/loggers)
# Definitions of possible logging destinations
[handlers](/weewx/weewx/wiki/handlers)
# System logger
[[syslog](/weewx/weewx/wiki/[syslog)]
level = DEBUG
formatter = standard
class = logging.handlers.SysLogHandler
address = {address}
facility = {facility}
# Log to console
[[console](/weewx/weewx/wiki/[console)]
level = DEBUG
formatter = verbose
class = logging.StreamHandler
# Alternate choice is 'ext://sys.stderr'
stream = ext://sys.stdout
# How to format log messages
[formatters](/weewx/weewx/wiki/formatters)
[[simple](/weewx/weewx/wiki/[simple)]
format = "%(levelname)s %(message)s"
[[standard](/weewx/weewx/wiki/[standard)]
format = "{process_name}[%(process)d]: %(levelname)s %(name)s: %(message)s"
[[verbose](/weewx/weewx/wiki/[verbose)]
format = "%(asctime)s {process_name}[%(process)d]: %(levelname)s %(name)s: %(message)s"
# Format to use for dates and times:
datefmt = %Y-%m-%d %H:%M:%S
This configures three different facilities:
- Loggers, which expose the interface that application code uses directly. Most
importantly, it determines which handler(s) to use. Note that the
root
logger is in its own section. - Handlers, which send the log records (created by loggers) to an appropriate
destination. There are two handlers that are included in the default
configuration:
syslog
andconsole
. - Formatters, which specify the layout of log records in the final output. A
number of attributes are available to the formatter (such as
%(levelname)s
; see the documentation LogRecord attributes for a complete list).
The value for {log_level}
depends on whether the debug
option has
been set. If it has not (the default), then log_level
is INFO
, otherwise,
DEBUG
.
The value for {process_name}
is passed in when setting up the logging
facility. For the WeeWX main program, it is weewxd
.
Logging to rotating files
While WeeWX comes with two handlers (syslog
and console
) there are
many others. See the Python documentation
Logging handlers.
One handler that is particularly useful, especially on macOS, is the
TimedRotatingFileHandler
,
which writes to a specified file. At a specified time, a new
file is opened and 'rotated' into place. Here's how to specify and use it.
Simply add this to your weewx.conf
file:
[Logging]
# Root logger
[root](/weewx/weewx/wiki/root)
handlers = timed_rotate, # NOTE 1
[handlers](/weewx/weewx/wiki/handlers)
# Log to a set of rotating files
[[timed_rotate](/weewx/weewx/wiki/[timed_rotate)] # NOTE 2
level = DEBUG
formatter = verbose # NOTE 3
class = logging.handlers.TimedRotatingFileHandler # NOTE 4
# File to log to:
filename = /var/tmp/{process_name}.log # NOTE 5
# When to rotate:
when = midnight # NOTE 6
# How many log files to save
backupCount = 7 # NOTE 7
-
This reconfigures the
root
logger to send log messages to the handertimed_rotate
, instead of to the defaultsyslog
. The trailing comma is important: it tells Python that optionhandlers
is a list, and not a single item. Because it is a list, you can actually specify multiple handlers. For example, to log totimed_rotate
as well as theconsole
, the option becomes:handlers = timed_rotate, console
-
This is where we define the new handler. It will be named
timed_rotate
. -
The
verbose
formatter is useful when doing file rotations because it includes a timestamp. -
Handler
timed_rotate
will use classTimedRotatingFileHandler
for the actual implementation. -
The option
filename
specifies the path to the rotating file. The variable{process_name}
can be used, in which case the name of the process (usuallyweewxd
) will be used. So, in this example, for the main programweewxd
, logs will be sent to/var/tmp/weewxd.log
. In WeeWX V5,filename
can be a relative path, in which case it will be relative toWEEWX_ROOT
. For example, specifyingfilename = log/{process_name}.log
in Version 5 will cause the
weewxd
log to go to~/weewx-data/log/weewxd.log
. -
Do the rotation at midnight.
-
Save seven (7) backup files, that is, a week's worth.
Customizing what gets logged
Another example. When the debug
option is on, the default root
logger will
show DEBUG
messages and above — basically everything. This is fine, but
say you've decided that the weewx.restx
module is too chatty for your liking.
For it, you want to see only INFO
messages and above — nothing else.
The solution is to tailor the logger used by the weewx.restx
module, so it no
longer inherits properties from the root logger. For this logger, we will
specify that it logs only INFO
and above. To do this, the [Logging]
section
in weewx.conf
would look like:
[Logging]
[loggers](/weewx/weewx/wiki/loggers)
[[weewx.restx](/weewx/weewx/wiki/[weewx.restx)]
level = INFO
Another example. Suppose you are debugging the weewx.restx
module. In this
case it would be desirable to set its logging level
to DEBUG
while other
loggers continue to log INFO
or higher. Again, the solution is to customize
the logger for the weewx.restx
module. In this case, along with setting the
logging level
, we need to define a handler
. Because the weewx.restx
logger
will handle the logging for the weewx.restx
module, we will also want to turn
propagate
off. Otherwise INFO
and higher messages will be logged twice: once
by the weewx.restx
logger and also by any parent handlers. Lastly, debug
should be left at 0, so that the default root
logging is INFO
.
The configuration would look something like this.
debug = 0
.
.
.
[Logging]
[loggers](/weewx/weewx/wiki/loggers)
[[weewx.restx](/weewx/weewx/wiki/[weewx.restx)]
level = DEBUG
handlers = syslog,
propagate = 0
Disabling logging
This example essentially disables WeeWX logging completely, resulting in no log messages at all related to WeeWX other than those perhaps written by the operating system itself.
[Logging]
version = 1
disable_existing_loggers = True
# Root logger
[root](/weewx/weewx/wiki/root)
level = CRITICAL
For developers
What follows is intended for developers who are extending WeeWX.
In modules
It's very easy to use logging
from within a module, such as a new service,
search list extension, etc. At the top of your code, include
import logging
log = logging.getLogger(__name__)
Then whenever you want to log an event, simply call one of
log.debug("This is a debug message")
log.info("This is an informational message")
log.warning("This is a warning message")
log.error("This is an error message")
log.critical("This is a critical message")
That's it.
Throttling logging events
The Python logging
module makes it very easy to temporarily reduce the number
of uninteresting messages going into a log.
Say you're about to call a function which will insert hundreds of records into
the database using the addRecord()
method of the Manager
class. This method
logs an INFO
event for every insertion, resulting in hundreds of
not-terribly-interesting entries. You'd like to temporarily avoid this. Here's
how:
import logging
# Temporarily disable logging for events at or below INFO
logging.disable(logging.INFO)
generate_zillions_of_records()
# Remove the temporary restriction
logging.disable(logging.NOTSET)
See the Python docs for more details about
logging.disable()
.
Maintaining backwards compatibility
If you are an extension writer, you will want to support not only the new style
logging, but also the old syslog
style. Here's how you can do it.
try:
# Test for new-style weewx logging by trying to import weeutil.logger
import weeutil.logger
import logging
log = logging.getLogger(__name__)
def logdbg(msg):
log.debug(msg)
def loginf(msg):
log.info(msg)
def logerr(msg):
log.error(msg)
except ImportError:
# Old-style weewx logging
import syslog
def logmsg(level, msg):
# Replace '__name__' with something to identify your application.
syslog.syslog(level, '__name__: %s:' % msg)
def logdbg(msg):
logmsg(syslog.LOG_DEBUG, msg)
def loginf(msg):
logmsg(syslog.LOG_INFO, msg)
def logerr(msg):
logmsg(syslog.LOG_ERR, msg)
Now you can just use the functions logdbg()
, loginf()
, and logerr()
in
your code, secure in the knowledge that it will work under WeeWX V3, as
well as V4 and V5.