diff --git a/bin/glance-api b/bin/glance-api index de5158556d..dccdcc689a 100755 --- a/bin/glance-api +++ b/bin/glance-api @@ -49,6 +49,9 @@ def create_options(parser): parser.add_option('-v', '--verbose', default=False, dest="verbose", action="store_true", help="Print more verbose output") + parser.add_option('-d', '--debug', default=False, dest="debug", + action="store_true", + help="Print debugging output") parser.add_option('-H', '--host', dest="host", metavar="ADDRESS", default="0.0.0.0", @@ -61,15 +64,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 +90,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('glance-api', parser) def main(_args): @@ -113,4 +108,9 @@ if __name__ == '__main__': % version.version_string()) create_options(oparser) (options, args) = config.parse_options(oparser) - server.serve('glance-api', main, options, args) + + try: + config.setup_logging(options) + server.serve('glance-api', main, options, args) + except RuntimeError, e: + sys.exit("ERROR: %s" % e) diff --git a/bin/glance-combined b/bin/glance-combined index 3fb8f711af..fb2507904f 100755 --- a/bin/glance-combined +++ b/bin/glance-combined @@ -52,6 +52,9 @@ def create_options(parser): parser.add_option('-v', '--verbose', default=False, dest="verbose", action="store_true", help="Print more verbose output") + parser.add_option('-d', '--debug', default=False, dest="debug", + action="store_true", + help="Print debugging output") parser.add_option('--api-host', dest="api_host", metavar="ADDRESS", default="0.0.0.0", @@ -74,15 +77,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 to output tho") parser.add_option('--working-directory', '--working-dir', @@ -100,6 +94,7 @@ def create_options(parser): "Default: %%default" % "','".join(DEFAULT_STORE_CHOICES)) glance.store.add_options(parser) glance.registry.db.add_options(parser) + config.add_log_options('glance-combined', parser) def main(_args): @@ -120,4 +115,9 @@ if __name__ == '__main__': % version.version_string()) create_options(oparser) (options, args) = config.parse_options(oparser) - server.serve('glance-combined', main, options, args) + + try: + config.setup_logging(options) + server.serve('glance-combined', main, options, args) + except RuntimeError, e: + sys.exit("ERROR: %s" % e) diff --git a/bin/glance-registry b/bin/glance-registry index 032386b59d..efe698feb4 100755 --- a/bin/glance-registry +++ b/bin/glance-registry @@ -46,6 +46,9 @@ def create_options(parser): parser.add_option('-v', '--verbose', default=False, dest="verbose", action="store_true", help="Print more verbose output") + parser.add_option('-d', '--debug', default=False, dest="debug", + action="store_true", + help="Print debugging output") parser.add_option('-H', '--host', dest="host", metavar="ADDRESS", default="0.0.0.0", @@ -58,15 +61,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', @@ -76,7 +70,12 @@ def create_options(parser): help="uid under which to run. Default: %default") parser.add_option("--gid", type=int, default=os.getgid(), help="gid under which to run. Default: %default") + parser.add_option('--sql-connection', metavar="CONNECTION", + default='sqlite:///glance.sqlite', + help="A valid SQLAlchemy connection string for the " + "registry database. Default: %default") glance.registry.db.add_options(parser) + config.add_log_options('glance-registry', parser) def main(_args): @@ -94,4 +93,9 @@ if __name__ == '__main__': % version.version_string()) create_options(oparser) (options, args) = config.parse_options(oparser) - server.serve('glance-registry', main, options, args) + + try: + config.setup_logging(options) + server.serve('glance-registry', main, options, args) + except RuntimeError, e: + sys.exit("ERROR: %s" % e) diff --git a/etc/logging.cnf.sample b/etc/logging.cnf.sample new file mode 100644 index 0000000000..7e7f31f0b7 --- /dev/null +++ b/etc/logging.cnf.sample @@ -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 diff --git a/glance/common/config.py b/glance/common/config.py index 8eec5f86a9..101ec4223b 100644 --- a/glance/common/config.py +++ b/glance/common/config.py @@ -16,6 +16,22 @@ # 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 +import sys + + +DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" +DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" +LOGGING_HANDLER_CHOICES = ['syslog', 'file', 'stream'] + def parse_options(parser, cli_args=None): """ @@ -52,3 +68,98 @@ 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(prog_name, 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-date-format', metavar="FORMAT", + default=DEFAULT_LOG_DATE_FORMAT, + help="Format string for %(asctime)s in log records. " + "Default: %default") + group.add_option('--log-file', default="%s.log" % prog_name, + 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(options): + """ + Sets up the logging options for a log with supplied name + + :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']) + return + else: + raise RuntimeError("Unable to locate specified logging " + "config file: %s" % options['log_config']) + + debug = options.get('debug', False) + verbose = options.get('verbose', False) + root_logger = logging.root + if debug: + root_logger.setLevel(logging.DEBUG) + elif verbose: + root_logger.setLevel(logging.INFO) + else: + root_logger.setLevel(logging.WARNING) + + # Set log configuration from options... + formatter = logging.Formatter(options['log_format'], + options['log_date_format']) + + if options['log_handler'] == 'syslog': + syslog = logging.handlers.SysLogHandler(address='/dev/log') + syslog.setFormatter(formatter) + root_logger.addHandler(syslog) + elif options['log_handler'] == 'file': + logfile = options['log_file'] + logdir = options['log_dir'] + if logdir: + logfile = os.path.join(logdir, logfile) + logfile = logging.FileHandler(logfile) + logfile.setFormatter(formatter) + logfile.setFormatter(formatter) + root_logger.addHandler(logfile) + else: + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(formatter) + root_logger.addHandler(handler) + + # Log the options used when starting if we're in debug mode... + if debug: + root_logger.debug("*" * 80) + root_logger.debug("Options:") + root_logger.debug("========") + for key, value in sorted(options.items()): + root_logger.debug("%(key)-30s %(value)s" % locals()) + root_logger.debug("*" * 80) diff --git a/glance/common/server.py b/glance/common/server.py index 63af0c594e..d3063ac760 100644 --- a/glance/common/server.py +++ b/glance/common/server.py @@ -87,17 +87,13 @@ 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'] - if not logfile: - logfile = None - logdir = options['logdir'] - if not logdir: - logdir = None + logfile = options['log_file'] + logdir = options['log_dir'] daemonize = options['daemonize'] - use_syslog = options['use_syslog'] + use_syslog = options['log_handler'] == 'syslog' files_to_keep = [] if daemonize: - logger = logging.getLogger() + logger = logging.getLogger(name) formatter = logging.Formatter( name + '(%(name)s): %(levelname)s %(message)s') if use_syslog and not logfile: @@ -105,7 +101,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: @@ -118,11 +114,6 @@ def daemonize(args, name, main, options): else: stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr - if options['verbose']: - logging.getLogger().setLevel(logging.DEBUG) - else: - logging.getLogger().setLevel(logging.WARNING) - with daemon.DaemonContext( detach_process=daemonize, working_directory=options['working_directory'], diff --git a/glance/registry/server.py b/glance/registry/server.py index 23b55642d0..f5fb8434ad 100644 --- a/glance/registry/server.py +++ b/glance/registry/server.py @@ -19,6 +19,7 @@ """ import json + import routes from webob import exc