Files
weewx/bin/weewxd
Rich Bell b4db1782be log configuration errors (#637)
Looks good! Thanks, Rich.
2021-01-12 17:51:46 -08:00

265 lines
9.8 KiB
Python
Executable File

#!/usr/bin/env python
#
# Copyright (c) 2009-2020 Tom Keffer <tkeffer@gmail.com>
#
# See the file LICENSE.txt for your rights.
#
"""Entry point to the weewx weather system."""
from __future__ import absolute_import
from __future__ import print_function
import locale
import logging
import os
import platform
import signal
import sys
import time
from optparse import OptionParser
import configobj
import daemon
import weecfg
import weedb
from weeutil.weeutil import to_bool
import weeutil.logger
# First import any user extensions...
# noinspection PyUnresolvedReferences
import user.extensions
# ...then import the engine
import weewx.engine
log = logging.getLogger(__name__)
usagestr = """Usage: weewxd --help
weewxd --version
weewxd [CONFIG_FILE|--config=CONFIG_FILE]
[--daemon] [--pidfile=PIDFILE]
[--exit] [--loop-on-init]
[--log-label=LABEL]
Entry point to the weewx weather program. Can be run directly, or as a daemon
by specifying the '--daemon' option.
Arguments:
CONFIG_FILE: The weewx configuration file to be used. Optional.
"""
# ===============================================================================
# Main entry point
# ===============================================================================
def main():
parser = OptionParser(usage=usagestr)
parser.add_option("--config", dest="config_path", type=str,
metavar="CONFIG_FILE",
help="Use configuration file CONFIG_FILE.")
parser.add_option("-d", "--daemon", action="store_true", dest="daemon", help="Run as a daemon")
parser.add_option("-p", "--pidfile", type="string", dest="pidfile",
help="Store the process ID in PIDFILE",
default="/var/run/weewx.pid", metavar="PIDFILE")
parser.add_option("-v", "--version", action="store_true", dest="version",
help="Display version number then exit")
parser.add_option("-x", "--exit", action="store_true", dest="exit",
help="Exit on I/O and database errors instead of restarting")
parser.add_option("-r", "--loop-on-init", action="store_true", dest="loop_on_init",
help="Retry forever if device is not ready on startup")
parser.add_option("-n", "--log-label", type="string", dest="log_label",
help="Label to use in syslog entries",
default="weewx", metavar="LABEL")
# Get the command line options and arguments:
(options, args) = parser.parse_args()
if options.version:
print(weewx.__version__)
sys.exit(0)
# Set up a minimal logging facility to be used until we read the config file.
weeutil.logger.setup(options.log_label, {})
if args and options.config_path:
print("Specify CONFIG_PATH as an argument, or by using --config, not both",
file=sys.stderr)
sys.exit(weewx.CMD_ERROR)
log.info("Initializing weewx version %s", weewx.__version__)
log.info("Using Python %s", sys.version)
log.info("Platform %s", platform.platform())
log.info("Locale is '%s'", locale.setlocale(locale.LC_ALL))
# Save the current working directory. A service might
# change it. In case of a restart, we need to change it back.
cwd = os.getcwd()
# Make sure the system time is not out of date (a common problem with the Raspberry Pi).
# Do this by making sure the system time is later than the creation time of this file.
sane = os.stat(__file__).st_ctime
n = 0
while weewx.launchtime_ts < sane:
# Log any problems every minute.
if n % 120 == 0:
log.info("Waiting for sane time. Current time is %s",
weeutil.weeutil.timestamp_to_string(weewx.launchtime_ts))
n += 1
time.sleep(0.5)
weewx.launchtime_ts = time.time()
# Set up the signal handlers.
signal.signal(signal.SIGHUP, sigHUPhandler)
signal.signal(signal.SIGTERM, sigTERMhandler)
if options.daemon:
log.info("PID file is %s", options.pidfile)
daemon.daemonize(pidfile=options.pidfile)
# Main restart loop
while True:
os.chdir(cwd)
# Read (or, possibly, re-read) the configuration file
try:
# Pass in a copy of the command line arguments. read_config() will change it.
config_path, config_dict = weecfg.read_config(options.config_path, list(args))
except (IOError, configobj.ConfigObjError) as e:
log.error("Error parsing config file: %s" % e)
weeutil.logger.log_traceback(log.critical, " **** ")
print(e, file=sys.stderr)
sys.exit(weewx.CMD_ERROR)
log.info("Using configuration file %s", config_path)
weewx.debug = int(config_dict.get('debug', 0))
log.info("Debug is %s", weewx.debug)
# Now that we have the config_dict and debug setting, we can customize the
# logging with user additions
weeutil.logger.setup(options.log_label, config_dict)
# If no command line --loop-on-init was specified, look in the config file.
if options.loop_on_init is None:
loop_on_init = to_bool(config_dict.get('loop_on_init', False))
else:
loop_on_init = options.loop_on_init
try:
log.debug("Initializing engine")
# Create and initialize the engine
engine = weewx.engine.StdEngine(config_dict)
log.info("Starting up weewx version %s", weewx.__version__)
# Start the engine. It should run forever unless an exception
# occurs. Log it if the function returns.
engine.run()
log.critical("Unexpected exit from main loop. Program exiting.")
# Catch any console initialization error:
except weewx.engine.InitializationError as e:
# Log it:
log.critical("Unable to load driver: %s", e)
# See if we should loop, waiting for the console to be ready.
# Otherwise, just exit.
if loop_on_init:
log.critical(" **** Waiting 60 seconds then retrying...")
time.sleep(60)
log.info("retrying...")
else:
log.critical(" **** Exiting...")
sys.exit(weewx.IO_ERROR)
# Catch any recoverable weewx I/O errors:
except weewx.WeeWxIOError as e:
# Caught an I/O error. Log it, wait 60 seconds, then try again
log.critical("Caught WeeWxIOError: %s", e)
if options.exit:
log.critical(" **** Exiting...")
sys.exit(weewx.IO_ERROR)
log.critical(" **** Waiting 60 seconds then retrying...")
time.sleep(60)
log.info("retrying...")
# Catch any database connection errors:
except (weedb.CannotConnectError, weedb.DisconnectError) as e:
# No connection to the database server. Log it, wait 60 seconds, then try again
log.critical("Database connection exception: %s", e)
if options.exit:
log.critical(" **** Exiting...")
sys.exit(weewx.DB_ERROR)
log.critical(" **** Waiting 60 seconds then retrying...")
time.sleep(60)
log.info("retrying...")
except weedb.OperationalError as e:
# Caught a database error. Log it, wait 120 seconds, then try again
log.critical("Database OperationalError exception: %s", e)
if options.exit:
log.critical(" **** Exiting...")
sys.exit(weewx.DB_ERROR)
log.critical(" **** Waiting 2 minutes then retrying...")
time.sleep(120)
log.info("retrying...")
except OSError as e:
# Caught an OS error. Log it, wait 10 seconds, then try again
log.critical("Caught OSError: %s", e)
weeutil.logger.log_traceback(log.critical, " **** ")
log.critical(" **** Waiting 10 seconds then retrying...")
time.sleep(10)
log.info("retrying...")
except Restart:
log.info("Received signal HUP. Restarting.")
except Terminate:
log.info("Terminating weewx version %s", weewx.__version__)
weeutil.logger.log_traceback(log.debug, " **** ")
signal.signal(signal.SIGTERM, signal.SIG_DFL)
os.kill(0, signal.SIGTERM)
# Catch any keyboard interrupts and log them
except KeyboardInterrupt:
log.critical("Keyboard interrupt.")
# Reraise the exception (this should cause the program to exit)
raise
# Catch any non-recoverable errors. Log them, exit
except Exception as ex:
# Caught unrecoverable error. Log it, exit
log.critical("Caught unrecoverable exception:")
log.critical(" **** %s" % ex)
# Include a stack traceback in the log:
weeutil.logger.log_traceback(log.critical, " **** ")
log.critical(" **** Exiting.")
# Reraise the exception (this should cause the program to exit)
raise
# ==============================================================================
# Signal handlers
# ==============================================================================
class Restart(Exception):
"""Exception raised when restarting the engine is desired."""
def sigHUPhandler(_signum, _frame):
log.info("Received signal HUP. Initiating restart.")
raise Restart
class Terminate(Exception):
"""Exception raised when terminating the engine."""
def sigTERMhandler(signum, _frame):
log.info("Received signal TERM (%s).", signum)
raise Terminate
# Start up the program
main()