Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

202 lines (177 sloc) 7.0 kB
#! /usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import re
import inspect
import logging
import logging.handlers
import functools
from configman import ConfigurationManager, Namespace, RequiredConfig
from configman.converters import class_converter
#==============================================================================
class AppDetailMissingError(AttributeError):
pass
#==============================================================================
class App(RequiredConfig):
"""The base class from which Socorro apps are based"""
#--------------------------------------------------------------------------
def __init__(self, config):
self.config = config
#--------------------------------------------------------------------------
def main(self): # pragma: no cover
"""derived classes must override this function with business logic"""
raise NotImplementedError(
"A definition of 'main' in a derived class is required"
)
#------------------------------------------------------------------------------
def logging_required_config(app_name):
lc = Namespace()
lc.namespace('logging')
lc.logging.add_option(
'syslog_host',
doc='syslog hostname',
default='localhost'
)
lc.logging.add_option(
'syslog_port',
doc='syslog port',
default=514
)
lc.logging.add_option(
'syslog_facility_string',
doc='syslog facility string ("user", "local0", etc)',
default='user'
)
lc.logging.add_option(
'syslog_line_format_string',
doc='python logging system format for syslog entries',
default='%s (pid {process}): '
'{asctime} {levelname} - {threadName} - '
'{message}' % app_name
)
lc.logging.add_option(
'syslog_error_logging_level',
doc='logging level for the log file (10 - DEBUG, 20 '
'- INFO, 30 - WARNING, 40 - ERROR, 50 - CRITICAL)',
default=40
)
lc.logging.add_option(
'stderr_line_format_string',
doc='python logging system format for logging to stderr',
default='{asctime} {levelname} - {threadName} - '
'{message}'
)
lc.logging.add_option(
'stderr_error_logging_level',
doc='logging level for the logging to stderr (10 - '
'DEBUG, 20 - INFO, 30 - WARNING, 40 - ERROR, '
'50 - CRITICAL)',
default=10
)
return lc
#------------------------------------------------------------------------------
def setup_logger(app_name, config, local_unused, args_unused):
logger = logging.getLogger(app_name)
logger.setLevel(logging.DEBUG)
stderr_log = logging.StreamHandler()
stderr_log.setLevel(config.logging.stderr_error_logging_level)
stderr_format = config.logging.stderr_line_format_string.replace(
'{app_name}',
app_name
)
stderr_log_formatter = logging.Formatter(
_convert_format_string(stderr_format)
)
stderr_log.setFormatter(stderr_log_formatter)
logger.addHandler(stderr_log)
syslog = logging.handlers.SysLogHandler(
facility=config.logging.syslog_facility_string
)
syslog.setLevel(config.logging.syslog_error_logging_level)
syslog_format = config.logging.syslog_line_format_string.replace(
'{app_name}',
app_name
)
syslog_formatter = logging.Formatter(
_convert_format_string(syslog_format)
)
syslog.setFormatter(syslog_formatter)
logger.addHandler(syslog)
return logger
#------------------------------------------------------------------------------
def _convert_format_string(s):
"""return '%(foo)s %(bar)s' if the input is '{foo} {bar}'"""
return re.sub('{(\w+)}', r'%(\1)s', s)
#------------------------------------------------------------------------------
# This main function will load an application object, initialize it and then
# call its 'main' function
def main(initial_app, values_source_list=None, config_path=None):
if isinstance(initial_app, basestring):
initial_app = class_converter(initial_app)
if config_path is None:
default = './config'
config_path = os.environ.get(
'DEFAULT_SOCORRO_CONFIG_PATH',
default
)
if config_path != default:
# you tried to set it, then it must be a valid directory
if not os.path.isdir(config_path):
raise IOError('%s is not a valid directory' % config_path)
# the only config parameter is a special one that refers to a class or
# module that defines an application. In order to qualify, a class must
# have a constructor that accepts a DotDict derivative as the sole
# input parameter. It must also have a 'main' function that accepts no
# parameters. For a module to be acceptable, it must have a main
# function that accepts a DotDict derivative as its input parameter.
app_definition = Namespace()
app_definition.add_option(
'application',
doc='the fully qualified module or class of the application',
default=initial_app,
from_string_converter=class_converter
)
try:
app_name = initial_app.app_name # this will be used as the default
# b
app_version = initial_app.app_version
app_description = initial_app.app_description
except AttributeError, x:
raise AppDetailMissingError(str(x))
app_definition.add_aggregation(
'logger',
functools.partial(setup_logger, app_name)
)
definitions = (
app_definition,
logging_required_config(app_name)
)
config_manager = ConfigurationManager(
definitions,
app_name=app_name,
app_version=app_version,
app_description=app_description,
values_source_list=values_source_list,
config_pathname=config_path
)
with config_manager.context() as config:
config_manager.log_config(config.logger)
# get the app class from configman. Why bother since we have it aleady
# with the 'initial_app' name? In most cases initial_app == app,
# it might not always be that way. The user always has the ability
# to specify on the command line a new app class that will override
# 'initial_app'.
app = config.application
if isinstance(app, type):
# invocation of the app if the app_object was a class
instance = app(config)
instance.main()
elif inspect.ismodule(app):
# invocation of the app if the app_object was a module
app.main(config)
elif inspect.isfunction(app):
# invocation of the app if the app_object was a function
app(config)
return 0
Jump to Line
Something went wrong with that request. Please try again.