1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
import logging
import os
import structlog
import traceback
import warnings
import yaml
from flask import make_response
from tzlocal import get_localzone
from importlib import import_module
from structlog.processors import (format_exc_info, JSONRenderer,
KeyValueRenderer)
from .middleware import CORS, TrailingSlash
from .search import IndexSearcher
from .storage import Store
from . import DEBUG
try:
from logging.config import dictConfig
except ImportError:
from logutils.dictconfig import dictConfig
if DEBUG:
processors = (format_exc_info, KeyValueRenderer())
else:
processors = (format_exc_info, JSONRenderer())
logger = structlog.get_logger()
default_conf = {
'search_index': '/srv/graphite/index',
'finders': [
'graphite_api.finders.whisper.WhisperFinder',
],
'functions': [
'graphite_api.functions.SeriesFunctions',
'graphite_api.functions.PieFunctions',
],
'whisper': {
'directories': [
'/srv/graphite/whisper',
],
},
'time_zone': get_localzone().zone,
}
if default_conf['time_zone'] == 'local': # tzlocal didn't find anything
default_conf['time_zone'] = 'UTC'
# attributes of a classical log record
NON_EXTRA = set(['module', 'filename', 'levelno', 'exc_text', 'pathname',
'lineno', 'msg', 'funcName', 'relativeCreated',
'levelname', 'msecs', 'threadName', 'name', 'created',
'process', 'processName', 'thread'])
class StructlogFormatter(logging.Formatter):
def __init__(self, *args, **kwargs):
self._bound = structlog.BoundLoggerBase(None, processors, {})
def format(self, record):
if not record.name.startswith('graphite_api'):
kw = dict(((k, v) for k, v in record.__dict__.items()
if k not in NON_EXTRA))
kw['logger'] = record.name
return self._bound._process_event(
record.levelname.lower(), record.getMessage(), kw)[0]
return record.getMessage()
def load_by_path(path):
module, klass = path.rsplit('.', 1)
finder = import_module(module)
return getattr(finder, klass)
def error_handler(e):
return make_response(traceback.format_exc(), 500,
{'Content-Type': 'text/plain'})
def configure(app):
config_file = os.environ.get('GRAPHITE_API_CONFIG',
'/etc/graphite-api.yaml')
if os.path.exists(config_file):
with open(config_file) as f:
config = yaml.safe_load(f)
config['path'] = config_file
else:
warnings.warn("Unable to find configuration file at {0}, using "
"default config.".format(config_file))
config = {}
configure_logging(config)
for key, value in list(default_conf.items()):
config.setdefault(key, value)
app.statsd = None
if 'statsd' in config:
try:
from statsd import StatsClient
except ImportError:
warnings.warn("'statsd' is provided in the configuration but "
"the statsd client is not installed. Please `pip "
"install statsd`.")
else:
c = config['statsd']
app.statsd = StatsClient(c['host'], c.get('port', 8125))
app.cache = None
if 'cache' in config:
try:
from flask.ext.cache import Cache
except ImportError:
warnings.warn("'cache' is provided in the configuration but "
"Flask-Cache is not installed. Please `pip install "
"Flask-Cache`.")
else:
cache_conf = {'CACHE_DEFAULT_TIMEOUT': 60,
'CACHE_KEY_PREFIX': 'graphite-api:'}
for key, value in config['cache'].items():
cache_conf['CACHE_{0}'.format(key.upper())] = value
app.cache = Cache(app, config=cache_conf)
loaded_config = {'functions': {}}
for functions in config['functions']:
loaded_config['functions'].update(load_by_path(functions))
if 'carbon' in config:
if 'hashing_keyfunc' in config['carbon']:
config['carbon']['hashing_keyfunc'] = load_by_path(
config['carbon']['hashing_keyfunc'])
else:
config['carbon']['hashing_keyfunc'] = lambda x: x
loaded_config['carbon'] = config.get('carbon', None)
finders = []
for finder in config['finders']:
finders.append(load_by_path(finder)(config))
loaded_config['store'] = Store(finders)
loaded_config['searcher'] = IndexSearcher(config['search_index'])
app.config['GRAPHITE'] = loaded_config
app.config['TIME_ZONE'] = config['time_zone']
logger.info("configured timezone", timezone=app.config['TIME_ZONE'])
if 'sentry_dsn' in config:
try:
from raven.contrib.flask import Sentry
except ImportError:
warnings.warn("'sentry_dsn' is provided in the configuration but "
"the sentry client is not installed. Please `pip "
"install raven[flask]`.")
else:
Sentry(app, dsn=config['sentry_dsn'])
app.wsgi_app = TrailingSlash(CORS(app.wsgi_app,
config.get('allowed_origins')))
if config.get('render_errors', True):
app.errorhandler(500)(error_handler)
def configure_logging(config):
structlog.configure(processors=processors,
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True)
config.setdefault('logging', {})
config['logging'].setdefault('version', 1)
config['logging'].setdefault('handlers', {})
config['logging'].setdefault('formatters', {})
config['logging'].setdefault('loggers', {})
config['logging']['handlers'].setdefault('raw', {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'raw',
})
config['logging']['loggers'].setdefault('root', {
'handlers': ['raw'],
'level': 'DEBUG',
'propagate': False,
})
config['logging']['loggers'].setdefault('graphite_api', {
'handlers': ['raw'],
'level': 'DEBUG',
})
config['logging']['formatters']['raw'] = {'()': StructlogFormatter}
dictConfig(config['logging'])
if 'path' in config:
logger.info("loading configuration", path=config['path'])
else:
logger.info("loading default configuration")
|