First round of logging functionality:

* Adds option group for logging-only configuration settings
* Adds /etc and /etc/logging.cnf.sample as an example of setting
  up logging configuration directly with a config file
* Adds to glance.common.config a couple function useful in
  adding logging options and setting up the logger(s)

Next round will include the addition of a --debug option and
lots more debugging output to the loggers.
This commit is contained in:
jaypipes@gmail.com 2011-02-01 13:18:40 -05:00
parent 087ebf5fec
commit 61711e0378
5 changed files with 162 additions and 22 deletions

View File

@ -22,6 +22,7 @@
Glance API Server
"""
import logging
import optparse
import os
import sys
@ -36,6 +37,7 @@ from glance.common import server
import glance.store
logger = logging.getLogger('glance-api')
DEFAULT_STORE_CHOICES = ['file', 'swift', 's3']
@ -61,15 +63,6 @@ def create_options(parser):
"Default: %default")
parser.add_option('--daemonize', default=False, action="store_true",
help="Daemonize this process")
parser.add_option('--use-syslog', default=True, action="store_true",
help="Output to syslog when daemonizing. "
"Default: %default")
parser.add_option('--logfile', default=None,
metavar="PATH",
help="(Optional) Name of log file to output to.")
parser.add_option("--logdir", default=None,
help="(Optional) The directory to keep log files in "
"(will be prepended to --logfile)")
parser.add_option("--pidfile", default=None,
help="(Optional) Name of pid file for the server")
parser.add_option('--working-directory', '--working-dir',
@ -96,6 +89,7 @@ def create_options(parser):
"virtual machine images to. Choices: ('%s') "
"Default: %%default" % "','".join(DEFAULT_STORE_CHOICES))
glance.store.add_options(parser)
config.add_log_options(parser)
def main(_args):
@ -113,4 +107,10 @@ if __name__ == '__main__':
% version.version_string())
create_options(oparser)
(options, args) = config.parse_options(oparser)
try:
config.setup_logging('glance-api', options)
except RuntimeError, e:
sys.exit("ERROR: %s" % e)
server.serve('glance-api', main, options, args)

View File

@ -57,15 +57,6 @@ def create_options(parser):
"Default: %default")
parser.add_option('--daemonize', default=False, action="store_true",
help="Daemonize this process")
parser.add_option('--use-syslog', default=True, action="store_true",
help="Output to syslog when daemonizing. "
"Default: %default")
parser.add_option('--logfile', default=None,
metavar="PATH",
help="(Optional) Name of log file to output to.")
parser.add_option("--logdir", default=None,
help="(Optional) The directory to keep log files in "
"(will be prepended to --logfile)")
parser.add_option("--pidfile", default=None,
help="(Optional) Name of pid file for the server")
parser.add_option('--working-directory', '--working-dir',
@ -79,6 +70,7 @@ def create_options(parser):
default='sqlite:///glance.sqlite',
help="A valid SQLAlchemy connection string for the "
"registry database. Default: %default")
config.add_log_options(parser)
def main(_args):
@ -96,4 +88,10 @@ if __name__ == '__main__':
% version.version_string())
create_options(oparser)
(options, args) = config.parse_options(oparser)
try:
config.setup_logging('glance-registry', options)
except RuntimeError, e:
sys.exit("ERROR: %s" % e)
server.serve('glance-registry', main, options, args)

54
etc/logging.cnf.sample Normal file
View File

@ -0,0 +1,54 @@
[loggers]
keys=root,api,registry,combined
[formatters]
keys=normal,normal_with_name,debug
[handlers]
keys=production,file,devel
[logger_root]
level=NOTSET
handlers=devel
[logger_api]
level=DEBUG
handlers=devel
qualname=glance-api
[logger_registry]
level=DEBUG
handlers=devel
qualname=glance-registry
[logger_combined]
level=DEBUG
handlers=devel
qualname=glance-combined
[handler_production]
class=handlers.SysLogHandler
level=ERROR
formatter=normal_with_name
args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)
[handler_file]
class=FileHandler
level=DEBUG
formatter=normal_with_name
args=('glance.log', 'w')
[handler_devel]
class=StreamHandler
level=NOTSET
formatter=debug
args=(sys.stdout,)
[formatter_normal]
format=%(asctime)s %(levelname)s %(message)s
[formatter_normal_with_name]
format=(%(name)s): %(asctime)s %(levelname)s %(message)s
[formatter_debug]
format=(%(name)s): %(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s

View File

@ -16,6 +16,20 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
Routines for configuring Glance
"""
import logging
import logging.config
import logging.handlers
import optparse
import os
DEFAULT_LOG_FORMAT = "%(asctime)s (%(name)s): %(levelname)s %(message)s"
LOGGING_HANDLER_CHOICES = ['syslog', 'file', 'stream']
def parse_options(parser, cli_args=None):
"""
@ -52,3 +66,77 @@ def options_to_conf(options):
:params options: Mapping of typed option key/values
"""
return dict([(k, str(v)) for k, v in options.items()])
def add_log_options(parser):
"""
Given a supplied optparse.OptionParser, adds an OptionGroup that
represents all the configuration options around logging.
:param parser: optparse.OptionParser
"""
help_text = "The following configuration options are specific to logging "\
"functionality for this program."
group = optparse.OptionGroup(parser, "Logging Options", help_text)
group.add_option('--log-config', default=None, metavar="PATH",
help="If this option is specified, the logging "
"configuration file specified is used and overrides "
"any other logging options specified. Please see "
"the Python logging module documentation for "
"details on logging configuration files.")
group.add_option('--log-handler', default='stream', metavar="HANDLER",
choices=LOGGING_HANDLER_CHOICES,
help="What logging handler to use? "
"Default: %default")
group.add_option('--log-format', metavar="FORMAT",
default=DEFAULT_LOG_FORMAT,
help="Format string for log records. Default: %default")
group.add_option('--log-file', default=None,
metavar="PATH",
help="(Optional) Name of log file to output to.")
group.add_option("--log-dir", default=None,
help="(Optional) The directory to keep log files in "
"(will be prepended to --logfile)")
parser.add_option_group(group)
def setup_logging(prog_name, options):
"""
Sets up the logging options for a log with supplied name
:param prog_name: Name of the log/program
:param options: Mapping of typed option key/values
"""
if options['log_config']:
# Use a logging configuration file for all settings...
if os.path.exists(options['log_config']):
logging.config.fileConfig(options['log_config'])
else:
raise RuntimeError("Unable to locate specified logging "
"config file: %s" % options['log_config'])
else:
# Set log configuration from options...
logger = logging.getLogger(prog_name)
formatter = logging.Formatter(options['log_format'])
if options['log_handler'] == 'syslog':
syslog = logging.handlers.SysLogHandler(address='/dev/log')
syslog.setFormatter(formatter)
logger.addHandler(syslog)
else:
logfile = options['log_file']
logdir = options['log_dir']
if not logfile:
logfile = '%s.log' % prog_name
if logdir:
logfile = os.path.join(logdir, logfile)
logfile = logging.FileHandler(logfile)
logfile.setFormatter(formatter)
logger.addHandler(logfile)
if options['verbose']:
logging.getLogger(prog_name).setLevel(logging.DEBUG)
else:
logging.getLogger(prog_name).setLevel(logging.WARNING)

View File

@ -87,14 +87,14 @@ def daemonize(args, name, main, options):
"""Does the work of daemonizing the process"""
logging.getLogger('amqplib').setLevel(logging.WARN)
pidfile = options['pidfile']
logfile = options['logfile']
logfile = options['log_file']
if not logfile:
logfile = None
logdir = options['logdir']
logdir = options['log_dir']
if not logdir:
logdir = None
daemonize = options['daemonize']
use_syslog = options['use_syslog']
use_syslog = options['log_handler'] == 'syslog'
files_to_keep = []
if daemonize:
logger = logging.getLogger()
@ -105,7 +105,7 @@ def daemonize(args, name, main, options):
syslog.setFormatter(formatter)
logger.addHandler(syslog)
files_to_keep.append(syslog.socket)
else:
elif options['log_handler'] == 'file':
if not logfile:
logfile = '%s.log' % name
if logdir: