Add syslog options and worker support.

Adds options for logging to syslog. As a consequence of this, we
can no longer log BEFORE we become a daemon. It doesn't work correctly
otherwise. Worker, statsd and pool manager support this new behavior
with this change.

Note that instead of logging exceptions that occur before becoming a
daemon, we simply let them propogate out.

Change-Id: If27da7fc1f7a51dba84bb5ecb074a657c3785164
This commit is contained in:
David Shrewsbury 2013-05-08 18:22:53 -04:00
parent 1a6c5ba8d9
commit c5de551c37
5 changed files with 109 additions and 108 deletions

View File

@ -137,6 +137,18 @@ Worker Command Line Options
Used to specify the Gearman job server hostname and port. This option Used to specify the Gearman job server hostname and port. This option
can be used multiple times to specify multiple job servers. can be used multiple times to specify multiple job servers.
.. option:: --syslog
Send log events to syslog.
.. option:: --syslog-socket
Socket to use for the syslog connection. Default is */dev/log*.
.. option:: --syslog-facility
Syslog logging facility. Default is *LOCAL7*.
.. option:: --user <USER> .. option:: --user <USER>
Specifies the user for the process when in daemon mode. Default is the Specifies the user for the process when in daemon mode. Default is the
@ -263,6 +275,18 @@ Pool Manager Command Line Options
Name of the PID file to use. Default is: Name of the PID file to use. Default is:
*/var/run/libra/libra_worker.pid* */var/run/libra/libra_worker.pid*
.. option:: --syslog
Send log events to syslog.
.. option:: --syslog-socket
Socket to use for the syslog connection. Default is */dev/log*.
.. option:: --syslog-facility
Syslog logging facility. Default is *LOCAL7*.
.. option:: --user <USER> .. option:: --user <USER>
Specifies the user for the process when in daemon mode. Default is the Specifies the user for the process when in daemon mode. Default is the
@ -320,3 +344,16 @@ Statsd Command Line Options
.. option:: --datadog_tags <TAGS> .. option:: --datadog_tags <TAGS>
A list of tags to be used for the datadog driver A list of tags to be used for the datadog driver
.. option:: --syslog
Send log events to syslog.
.. option:: --syslog-socket
Socket to use for the syslog connection. Default is */dev/log*.
.. option:: --syslog-facility
Syslog logging facility. Default is *LOCAL7*.

View File

@ -14,7 +14,9 @@
import argparse import argparse
import logging import logging
import logging.handlers import logging.handlers
import os
import os.path import os.path
import pwd
import sys import sys
import ConfigParser import ConfigParser
@ -122,6 +124,20 @@ class Options(object):
'-n', '--nodaemon', dest='nodaemon', action='store_true', '-n', '--nodaemon', dest='nodaemon', action='store_true',
help='do not run in daemon mode' help='do not run in daemon mode'
) )
self.parser.add_argument(
'--syslog', dest='syslog', action='store_true',
help='use syslog for logging output'
)
self.parser.add_argument(
'--syslog-socket', dest='syslog_socket',
default='/dev/log',
help='socket to use for syslog connection (default: /dev/log)'
)
self.parser.add_argument(
'--syslog-facility', dest='syslog_facility',
default='local7',
help='syslog logging facility (default: local7)'
)
self.parser.add_argument( self.parser.add_argument(
'-d', '--debug', dest='debug', action='store_true', '-d', '--debug', dest='debug', action='store_true',
help='log debugging output' help='log debugging output'
@ -184,7 +200,16 @@ def setup_logging(name, args):
'%(asctime)-6s: %(name)s - %(levelname)s - %(message)s' '%(asctime)-6s: %(name)s - %(levelname)s - %(message)s'
) )
if logfile: # No timestamp, used with syslog
simple_formatter = logging.Formatter(
'%(name)s - %(levelname)s - %(message)s'
)
if args.syslog and not args.nodaemon:
handler = logging.handlers.SysLogHandler(address=args.syslog_socket,
facility=args.syslog_facility)
handler.setFormatter(simple_formatter)
elif logfile:
handler = CompressedTimedRotatingFileHandler( handler = CompressedTimedRotatingFileHandler(
logfile, when='D', interval=1, backupCount=7 logfile, when='D', interval=1, backupCount=7
) )
@ -201,4 +226,10 @@ def setup_logging(name, args):
elif args.verbose: elif args.verbose:
logger.setLevel(level=logging.INFO) logger.setLevel(level=logging.INFO)
if logfile and not args.syslog and args.user:
# NOTE(LinuxJedi): we are switching user so need to switch
# the ownership of the log file for rotation
os.chown(logger.handlers[0].baseFilename,
pwd.getpwnam(args.user).pw_uid, -1)
return logger return logger

View File

@ -22,7 +22,6 @@ import time
import sys import sys
import os import os
import threading import threading
import lockfile
from novaclient import exceptions from novaclient import exceptions
from libra.openstack.common import importutils from libra.openstack.common import importutils
@ -33,8 +32,7 @@ from libra.mgm.node_list import NodeList, AccessDenied
class Server(object): class Server(object):
def __init__(self, logger, args): def __init__(self, args):
self.logger = logger
self.args = args self.args = args
self.ct = None self.ct = None
self.ft = None self.ft = None
@ -43,10 +41,12 @@ class Server(object):
try: try:
self.node_list = NodeList(self.args.datadir) self.node_list = NodeList(self.args.datadir)
except AccessDenied as exc: except AccessDenied as exc:
self.logger.error(exc) print(str(exc))
self.shutdown(True) self.shutdown(True)
def main(self): def main(self):
self.logger = setup_logging('libra_mgm', self.args)
self.logger.info( self.logger.info(
'Libra Pool Manager started with a float of {nodes} nodes' 'Libra Pool Manager started with a float of {nodes} nodes'
.format(nodes=self.args.nodes) .format(nodes=self.args.nodes)
@ -429,46 +429,25 @@ def main():
svr_list = args.api_server.split() svr_list = args.api_server.split()
args.api_server = svr_list args.api_server = svr_list
logger = setup_logging('libra_mgm', args) server = Server(args)
server = Server(logger, args)
if args.nodaemon: if args.nodaemon:
server.main() server.main()
else: else:
pidfile = daemon.pidfile.TimeoutPIDLockFile(args.pid, 10) pidfile = daemon.pidfile.TimeoutPIDLockFile(args.pid, 10)
if daemon.runner.is_pidfile_stale(pidfile): if daemon.runner.is_pidfile_stale(pidfile):
logger.warning("Cleaning up stale PID file")
pidfile.break_lock() pidfile.break_lock()
context = daemon.DaemonContext( context = daemon.DaemonContext(
working_directory='/', working_directory='/',
umask=0o022, umask=0o022,
pidfile=pidfile, pidfile=pidfile
files_preserve=[logger.handlers[0].stream]
) )
if args.user: if args.user:
try: context.uid = pwd.getpwnam(args.user).pw_uid
context.uid = pwd.getpwnam(args.user).pw_uid
except KeyError:
logger.critical("Invalid user: %s" % args.user)
return 1
# NOTE(LinuxJedi): we are switching user so need to switch
# the ownership of the log file for rotation
os.chown(logger.handlers[0].baseFilename, context.uid, -1)
if args.group: if args.group:
try: context.gid = grp.getgrnam(args.group).gr_gid
context.gid = grp.getgrnam(args.group).gr_gid
except KeyError:
logger.critical("Invalid group: %s" % args.group)
return 1
try:
context.open()
except lockfile.LockTimeout:
logger.critical(
"Failed to lock pidfile %s, another instance running?",
args.pid
)
return 1
context.open()
server.main() server.main()
return 0 return 0

View File

@ -15,9 +15,7 @@
import daemon import daemon
import daemon.pidfile import daemon.pidfile
import daemon.runner import daemon.runner
import lockfile
import grp import grp
import os
import pwd import pwd
import time import time
@ -27,9 +25,14 @@ from libra.statsd.drivers.base import known_drivers
from libra.statsd.scheduler import Sched from libra.statsd.scheduler import Sched
def start(logger, args, drivers): def start(args, drivers):
""" Start the main server processing. """ """ Start the main server processing. """
logger = setup_logging('libra_statsd', args)
logger.info("Job server list: %s" % args.server)
logger.info("Selected drivers: {0}".format(args.driver))
scheduler = Sched(logger, args, drivers) scheduler = Sched(logger, args, drivers)
scheduler.start() scheduler.start()
while True: while True:
@ -82,8 +85,6 @@ def main():
args = options.run() args = options.run()
logger = setup_logging('libra_statsd', args)
if not args.server: if not args.server:
# NOTE(shrews): Can't set a default in argparse method because the # NOTE(shrews): Can't set a default in argparse method because the
# value is appended to the specified default. # value is appended to the specified default.
@ -106,8 +107,6 @@ def main():
svr_list = args.api_server.split() svr_list = args.api_server.split()
args.api_server = svr_list args.api_server = svr_list
logger.info("Job server list: %s" % args.server)
logger.info("Selected drivers: {0}".format(args.driver))
if not isinstance(args.driver, list): if not isinstance(args.driver, list):
args.driver = args.driver.split() args.driver = args.driver.split()
for driver in args.driver: for driver in args.driver:
@ -116,39 +115,19 @@ def main():
)) ))
if args.nodaemon: if args.nodaemon:
start(logger, args, drivers) start(args, drivers)
else: else:
pidfile = daemon.pidfile.TimeoutPIDLockFile(args.pid, 10) pidfile = daemon.pidfile.TimeoutPIDLockFile(args.pid, 10)
if daemon.runner.is_pidfile_stale(pidfile): if daemon.runner.is_pidfile_stale(pidfile):
logger.warning("Cleaning up stale PID file")
pidfile.break_lock() pidfile.break_lock()
context = daemon.DaemonContext( context = daemon.DaemonContext(
umask=0o022, umask=0o022,
pidfile=pidfile, pidfile=pidfile
files_preserve=[logger.handlers[0].stream]
) )
if args.user: if args.user:
try: context.uid = pwd.getpwnam(args.user).pw_uid
context.uid = pwd.getpwnam(args.user).pw_uid
except KeyError:
logger.critical("Invalid user: %s" % args.user)
return 1
# NOTE(LinuxJedi): we are switching user so need to switch
# the ownership of the log file for rotation
os.chown(logger.handlers[0].baseFilename, context.uid, -1)
if args.group: if args.group:
try: context.gid = grp.getgrnam(args.group).gr_gid
context.gid = grp.getgrnam(args.group).gr_gid
except KeyError:
logger.critical("Invalid group: %s" % args.group)
return 1
try: context.open()
context.open() start(args, drivers)
except lockfile.LockTimeout:
logger.critical(
"Failed to lock pidfile %s, another instance running?",
args.pid
)
start(logger, args, drivers)

View File

@ -16,8 +16,6 @@ import eventlet
eventlet.monkey_patch() eventlet.monkey_patch()
import daemon import daemon
import lockfile
import os
import daemon.pidfile import daemon.pidfile
import daemon.runner import daemon.runner
import grp import grp
@ -37,10 +35,7 @@ class EventServer(object):
non-daemon mode. non-daemon mode.
""" """
def __init__(self, logger): def main(self, args, tasks):
self.logger = logger
def main(self, tasks):
""" """
Main method of the server. Main method of the server.
@ -48,16 +43,22 @@ class EventServer(object):
A tuple with two items: a function name, and a tuple with A tuple with two items: a function name, and a tuple with
that function's arguments. that function's arguments.
""" """
thread_list = [] thread_list = []
logger = setup_logging('libra_worker', args)
for task, args in tasks: logger.info("Selected driver: %s" % args.driver)
thread_list.append(eventlet.spawn(task, *args)) if args.driver == 'haproxy':
logger.info("Selected HAProxy service: %s" % args.haproxy_service)
logger.info("Job server list: %s" % args.server)
for task, task_args in tasks:
task_args = (logger,) + task_args # Make the logger the first arg
thread_list.append(eventlet.spawn(task, *task_args))
for thd in thread_list: for thd in thread_list:
thd.wait() thd.wait()
self.logger.info("Shutting down") logger.info("Shutting down")
def main(): def main():
@ -90,8 +91,6 @@ def main():
) )
args = options.run() args = options.run()
logger = setup_logging('libra_worker', args)
if not args.server: if not args.server:
# NOTE(shrews): Can't set a default in argparse method because the # NOTE(shrews): Can't set a default in argparse method because the
# value is appended to the specified default. # value is appended to the specified default.
@ -107,11 +106,9 @@ def main():
# along to the Gearman task that will use it to communicate with # along to the Gearman task that will use it to communicate with
# the device. # the device.
logger.info("Selected driver: %s" % args.driver)
driver_class = importutils.import_class(known_drivers[args.driver]) driver_class = importutils.import_class(known_drivers[args.driver])
if args.driver == 'haproxy': if args.driver == 'haproxy':
logger.info("Selected HAProxy service: %s" % args.haproxy_service)
if args.user: if args.user:
user = args.user user = args.user
else: else:
@ -127,52 +124,30 @@ def main():
else: else:
driver = driver_class() driver = driver_class()
logger.info("Job server list: %s" % args.server) server = EventServer()
server = EventServer(logger)
# Tasks to execute in parallel # Tasks to execute in parallel
task_list = [ task_list = [
(config_thread, (logger, driver, args.server, args.reconnect_sleep)) (config_thread, (driver, args.server, args.reconnect_sleep))
] ]
if args.nodaemon: if args.nodaemon:
server.main(task_list) server.main(args, task_list)
else: else:
pidfile = daemon.pidfile.TimeoutPIDLockFile(args.pid, 10) pidfile = daemon.pidfile.TimeoutPIDLockFile(args.pid, 10)
if daemon.runner.is_pidfile_stale(pidfile): if daemon.runner.is_pidfile_stale(pidfile):
logger.warning("Cleaning up stale PID file")
pidfile.break_lock() pidfile.break_lock()
context = daemon.DaemonContext( context = daemon.DaemonContext(
working_directory='/etc/haproxy', working_directory='/etc/haproxy',
umask=0o022, umask=0o022,
pidfile=pidfile, pidfile=pidfile
files_preserve=[logger.handlers[0].stream]
) )
if args.user: if args.user:
try: context.uid = pwd.getpwnam(args.user).pw_uid
context.uid = pwd.getpwnam(args.user).pw_uid
except KeyError:
logger.critical("Invalid user: %s" % args.user)
return 1
# NOTE(LinuxJedi): we are switching user so need to switch
# the ownership of the log file for rotation
os.chown(logger.handlers[0].baseFilename, context.uid, -1)
if args.group: if args.group:
try: context.gid = grp.getgrnam(args.group).gr_gid
context.gid = grp.getgrnam(args.group).gr_gid
except KeyError:
logger.critical("Invalid group: %s" % args.group)
return 1
try: context.open()
context.open() server.main(args, task_list)
except lockfile.LockTimeout:
logger.critical(
"Failed to lock pidfile %s, another instance running?",
args.pid
)
return 1
server.main(task_list)
return 0 return 0