config(minor): CONFIG.get -> CONFIG.y

This commit is contained in:
Langhammer, Jens 2019-09-30 18:04:04 +02:00
parent 9cddab8fd5
commit c2c5ff6912
11 changed files with 167 additions and 199 deletions

View File

@ -41,6 +41,7 @@ service_identity = "*"
signxml = "*" signxml = "*"
urllib3 = {extras = ["secure"],version = "*"} urllib3 = {extras = ["secure"],version = "*"}
websocket_client = "*" websocket_client = "*"
structlog = "*"
[requires] [requires]
python_version = "3.7" python_version = "3.7"
@ -57,3 +58,4 @@ unittest-xml-reporting = "*"
autopep8 = "*" autopep8 = "*"
bandit = "*" bandit = "*"
twine = "*" twine = "*"
colorama = "*"

18
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "cd82871d9aca8cfd548a6a62856196b2211524f12fbd416dfe5218aad9471e44" "sha256": "f8694b0ee03f99560e853fd24e9cd7ac987c757cd50249398346e42cdd98cbbb"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -777,6 +777,14 @@
], ],
"version": "==0.3.0" "version": "==0.3.0"
}, },
"structlog": {
"hashes": [
"sha256:5feae03167620824d3ae3e8915ea8589fc28d1ad6f3edf3cc90ed7c7cb33fab5",
"sha256:db441b81c65b0f104a7ce5d86c5432be099956b98b8a2c8be0b3fb3a7a0b1536"
],
"index": "pypi",
"version": "==19.1.0"
},
"tempora": { "tempora": {
"hashes": [ "hashes": [
"sha256:cb60b1d2b1664104e307f8e5269d7f4acdb077c82e35cd57246ae14a3427d2d6", "sha256:cb60b1d2b1664104e307f8e5269d7f4acdb077c82e35cd57246ae14a3427d2d6",
@ -950,6 +958,14 @@
], ],
"version": "==3.0.4" "version": "==3.0.4"
}, },
"colorama": {
"hashes": [
"sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d",
"sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"
],
"index": "pypi",
"version": "==0.4.1"
},
"coverage": { "coverage": {
"hashes": [ "hashes": [
"sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6", "sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6",

View File

@ -19,7 +19,7 @@ class AuthenticationFactor(TemplateView):
self.authenticator = authenticator self.authenticator = authenticator
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['config'] = CONFIG.get('passbook') kwargs['config'] = CONFIG.y('passbook')
kwargs['is_login'] = True kwargs['is_login'] = True
kwargs['title'] = _('Log in to your account') kwargs['title'] = _('Log in to your account')
kwargs['primary_action'] = _('Log in') kwargs['primary_action'] = _('Log in')

View File

@ -17,7 +17,7 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
"""passbook cherrypy server""" """passbook cherrypy server"""
cherrypy.config.update(CONFIG.get('web')) cherrypy.config.update(CONFIG.y('web'))
cherrypy.tree.graft(application, '/') cherrypy.tree.graft(application, '/')
# Mount NullObject to serve static files # Mount NullObject to serve static files
cherrypy.tree.mount(None, settings.STATIC_URL, config={ cherrypy.tree.mount(None, settings.STATIC_URL, config={

View File

@ -40,7 +40,7 @@ class LoginView(UserPassesTestMixin, FormView):
return redirect(reverse('passbook_core:overview')) return redirect(reverse('passbook_core:overview'))
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['config'] = CONFIG.get('passbook') kwargs['config'] = CONFIG.y('passbook')
kwargs['is_login'] = True kwargs['is_login'] = True
kwargs['title'] = _('Log in to your account') kwargs['title'] = _('Log in to your account')
kwargs['primary_action'] = _('Log in') kwargs['primary_action'] = _('Log in')
@ -135,7 +135,7 @@ class SignUpView(UserPassesTestMixin, FormView):
return super().get_initial() return super().get_initial()
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['config'] = CONFIG.get('passbook') kwargs['config'] = CONFIG.y('passbook')
kwargs['is_login'] = True kwargs['is_login'] = True
kwargs['title'] = _('Sign Up') kwargs['title'] = _('Sign Up')
kwargs['primary_action'] = _('Sign up') kwargs['primary_action'] = _('Sign up')

View File

@ -66,7 +66,7 @@ class UserChangePasswordView(LoginRequiredMixin, FormView):
return redirect('passbook_core:overview') return redirect('passbook_core:overview')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['config'] = CONFIG.get('passbook') kwargs['config'] = CONFIG.y('passbook')
kwargs['is_login'] = True kwargs['is_login'] = True
kwargs['title'] = _('Change Password') kwargs['title'] = _('Change Password')
kwargs['primary_action'] = _('Change') kwargs['primary_action'] = _('Change')

View File

@ -166,7 +166,7 @@ class LDAPConnector:
if not self._source.enabled: if not self._source.enabled:
return None return None
# FIXME: Adapt user_uid # FIXME: Adapt user_uid
# email = filters.pop(CONFIG.get('passport').get('ldap').get, '') # email = filters.pop(CONFIG.y('passport').get('ldap').get, '')
email = filters.pop('email') email = filters.pop('email')
user_dn = self.lookup(self.generate_filter(**{LOGIN_FIELD: email})) user_dn = self.lookup(self.generate_filter(**{LOGIN_FIELD: email}))
if not user_dn: if not user_dn:

View File

@ -1,31 +1,35 @@
"""passbook lib config loader""" """passbook core config loader"""
import os import os
from collections.abc import Mapping from collections import Mapping
from contextlib import contextmanager from contextlib import contextmanager
from glob import glob from glob import glob
from logging import getLogger
from typing import Any from typing import Any
from urllib.parse import urlparse
import yaml import yaml
from django.conf import ImproperlyConfigured from django.conf import ImproperlyConfigured
from django.utils.autoreload import autoreload_started from django.utils.autoreload import autoreload_started
from structlog import get_logger
SEARCH_PATHS = [ SEARCH_PATHS = [
'passbook/lib/default.yml', 'passbook/lib/default.yml',
'/etc/passbook/config.yml', '/etc/passbook/config.yml',
'.', '',
] + glob('/etc/passbook/config.d/*.yml', recursive=True) ] + glob('/etc/passbook/config.d/*.yml', recursive=True)
LOGGER = getLogger(__name__) LOGGER = get_logger()
ENVIRONMENT = os.getenv('PASSBOOK_ENV', 'local') ENV_PREFIX = 'PASSBOOK'
ENVIRONMENT = os.getenv(f'{ENV_PREFIX}_ENV', 'local')
class ConfigLoader: class ConfigLoader:
"""Search through SEARCH_PATHS and load configuration""" """Search through SEARCH_PATHS and load configuration. Environment variables starting with
`ENV_PREFIX` are also applied.
A variable like PASSBOOK_POSTGRESQL__HOST would translate to postgresql.host"""
loaded_file = [] loaded_file = []
__config = {} __config = {}
__context_default = None
__sub_dicts = [] __sub_dicts = []
def __init__(self): def __init__(self):
@ -41,21 +45,13 @@ class ConfigLoader:
self.update_from_file(path) self.update_from_file(path)
elif os.path.isdir(path) and os.path.exists(path): elif os.path.isdir(path) and os.path.exists(path):
# Path is an existing dir, so we try to read the env config from it # Path is an existing dir, so we try to read the env config from it
env_paths = [os.path.join(path, ENVIRONMENT+'.yml'), env_paths = [os.path.join(path, ENVIRONMENT + '.yml'),
os.path.join(path, ENVIRONMENT+'.env.yml')] os.path.join(path, ENVIRONMENT + '.env.yml')]
for env_file in env_paths: for env_file in env_paths:
if os.path.isfile(env_file) and os.path.exists(env_file): if os.path.isfile(env_file) and os.path.exists(env_file):
# Update config with env file # Update config with env file
self.update_from_file(env_file) self.update_from_file(env_file)
self.handle_secret_key() self.update_from_env()
def handle_secret_key(self):
"""Handle `secret_key_file`"""
if 'secret_key_file' in self.__config:
secret_key_file = self.__config.get('secret_key_file')
if os.path.isfile(secret_key_file) and os.path.exists(secret_key_file):
with open(secret_key_file) as file:
self.__config['secret_key'] = file.read().replace('\n', '')
def update(self, root, updatee): def update(self, root, updatee):
"""Recursively update dictionary""" """Recursively update dictionary"""
@ -63,16 +59,25 @@ class ConfigLoader:
if isinstance(value, Mapping): if isinstance(value, Mapping):
root[key] = self.update(root.get(key, {}), value) root[key] = self.update(root.get(key, {}), value)
else: else:
if isinstance(value, str):
value = self.parse_uri(value)
root[key] = value root[key] = value
return root return root
def parse_uri(self, value):
"""Parse string values which start with a URI"""
url = urlparse(value)
if url.scheme == 'env':
value = os.getenv(url.netloc, url.query)
return value
def update_from_file(self, path: str): def update_from_file(self, path: str):
"""Update config from file contents""" """Update config from file contents"""
try: try:
with open(path) as file: with open(path) as file:
try: try:
self.update(self.__config, yaml.safe_load(file)) self.update(self.__config, yaml.safe_load(file))
LOGGER.debug("Loaded %s", path) LOGGER.debug("Loaded config", file=path)
self.loaded_file.append(path) self.loaded_file.append(path)
except yaml.YAMLError as exc: except yaml.YAMLError as exc:
raise ImproperlyConfigured from exc raise ImproperlyConfigured from exc
@ -83,12 +88,26 @@ class ConfigLoader:
"""Update config from dict""" """Update config from dict"""
self.__config.update(update) self.__config.update(update)
@contextmanager def update_from_env(self):
def default(self, value: Any): """Check environment variables"""
"""Contextmanage that sets default""" outer = {}
self.__context_default = value idx = 0
yield for key, value in os.environ.items():
self.__context_default = None if not key.startswith(ENV_PREFIX):
continue
relative_key = key.replace(f"{ENV_PREFIX}_", '').replace('__', '.').lower()
# Recursively convert path from a.b.c into outer[a][b][c]
current_obj = outer
dot_parts = relative_key.split('.')
for dot_part in dot_parts[:-1]:
if dot_part not in current_obj:
current_obj[dot_part] = {}
current_obj = current_obj[dot_part]
current_obj[dot_parts[-1]] = value
idx += 1
if idx > 0:
LOGGER.debug("Loaded environment variables", count=idx)
self.update(self.__config, outer)
@contextmanager @contextmanager
# pylint: disable=invalid-name # pylint: disable=invalid-name
@ -98,15 +117,6 @@ class ConfigLoader:
yield yield
self.__sub_dicts.pop() self.__sub_dicts.pop()
def get(self, key: str, default=None) -> Any:
"""Get value from loaded config file"""
if default is None:
default = self.__context_default
config_copy = self.raw
for sub in self.__sub_dicts:
config_copy = config_copy.get(sub, None)
return config_copy.get(key, default)
@property @property
def raw(self) -> dict: def raw(self) -> dict:
"""Get raw config dictionary""" """Get raw config dictionary"""
@ -115,8 +125,6 @@ class ConfigLoader:
# pylint: disable=invalid-name # pylint: disable=invalid-name
def y(self, path: str, default=None, sep='.') -> Any: def y(self, path: str, default=None, sep='.') -> Any:
"""Access attribute by using yaml path""" """Access attribute by using yaml path"""
if default is None:
default = self.__context_default
# Walk sub_dicts before parsing path # Walk sub_dicts before parsing path
root = self.raw root = self.raw
for sub in self.__sub_dicts: for sub in self.__sub_dicts:
@ -129,11 +137,17 @@ class ConfigLoader:
return default return default
return root return root
def y_bool(self, path: str, default=False) -> bool:
"""Wrapper for y that converts value into boolean"""
return str(self.y(path, default)).lower() == 'true'
CONFIG = ConfigLoader() CONFIG = ConfigLoader()
# pylint: disable=unused-argument # pylint: disable=unused-argument
def signal_handler(sender, **kwargs):
def signal_handler(sender, **_):
"""Add all loaded config files to autoreload watcher""" """Add all loaded config files to autoreload watcher"""
for path in CONFIG.loaded_file: for path in CONFIG.loaded_file:
sender.watch_file(path) sender.watch_file(path)

View File

@ -1,43 +1,20 @@
# This is the default configuration file # This is the default configuration file
databases: postgresql:
default:
engine: 'django.db.backends.postgresql'
name: passbook
user: passbook
password: 'EK-5jnKfjrGRm<77'
host: localhost
log:
level:
console: DEBUG
file: DEBUG
file: /dev/null
syslog:
host: 127.0.0.1
port: 514
email:
host: localhost host: localhost
port: 25 name: passbook
user: '' user: postgres
password: '' password: ''
use_tls: false
use_ssl: false redis:
from: passbook <passbook@domain.tld> host: localhost
web: password: ''
server.socket_host: 0.0.0.0 cache_db: 0
server.socket_port: 8000 message_queue_db: 1
server.thread_pool: 20
log.screen: false
log.access_file: ''
log.error_file: ''
debug: false debug: false
secure_proxy_header:
HTTP_X_FORWARDED_PROTO: https
rabbitmq: guest:guest@localhost/passbook
redis: localhost/0
# Error reporting, sends stacktrace to sentry.services.beryju.org # Error reporting, sends stacktrace to sentry.services.beryju.org
error_report_enabled: true error_report_enabled: true
secret_key: 9$@r!d^1^jrn#fk#1#@ks#9&i$^s#1)_13%$rwjrhd=e8jfi_s
domains: domains:
- passbook.local - passbook.local

View File

@ -75,7 +75,7 @@ class EnableView(LoginRequiredMixin, FormView):
# TODO: Check if OTP Factor exists and applies to user # TODO: Check if OTP Factor exists and applies to user
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['config'] = CONFIG.get('passbook') kwargs['config'] = CONFIG.y('passbook')
kwargs['is_login'] = True kwargs['is_login'] = True
kwargs['title'] = _('Configue OTP') kwargs['title'] = _('Configue OTP')
kwargs['primary_action'] = _('Setup') kwargs['primary_action'] = _('Setup')

View File

@ -15,19 +15,15 @@ import logging
import os import os
import sys import sys
from celery.schedules import crontab import structlog
from django.contrib import messages
from sentry_sdk import init as sentry_init from sentry_sdk import init as sentry_init
from sentry_sdk.integrations.celery import CeleryIntegration from sentry_sdk.integrations.celery import CeleryIntegration
from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.logging import LoggingIntegration
from passbook import __version__ from passbook import __version__
from passbook.lib.config import CONFIG from passbook.lib.config import CONFIG
from passbook.lib.sentry import before_send from passbook.lib.sentry import before_send
VERSION = __version__
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
STATIC_ROOT = BASE_DIR + '/static' STATIC_ROOT = BASE_DIR + '/static'
@ -36,12 +32,13 @@ STATIC_ROOT = BASE_DIR + '/static'
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = CONFIG.get('secret_key') SECRET_KEY = CONFIG.y('secret_key',
"9$@r!d^1^jrn#fk#1#@ks#9&i$^s#1)_13%$rwjrhd=e8jfi_s") # noqa Debug
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = CONFIG.get('debug') DEBUG = CONFIG.y_bool('debug')
INTERNAL_IPS = ['127.0.0.1'] INTERNAL_IPS = ['127.0.0.1']
# ALLOWED_HOSTS = CONFIG.get('domains', []) + [CONFIG.get('primary_domain')] # ALLOWED_HOSTS = CONFIG.y('domains', []) + [CONFIG.y('primary_domain')]
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
@ -53,7 +50,7 @@ AUTH_USER_MODEL = 'passbook_core.User'
CSRF_COOKIE_NAME = 'passbook_csrf' CSRF_COOKIE_NAME = 'passbook_csrf'
SESSION_COOKIE_NAME = 'passbook_session' SESSION_COOKIE_NAME = 'passbook_session'
SESSION_COOKIE_DOMAIN = CONFIG.get('primary_domain') SESSION_COOKIE_DOMAIN = CONFIG.y('primary_domain')
SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default" SESSION_CACHE_ALIAS = "default"
LANGUAGE_COOKIE_NAME = 'passbook_language' LANGUAGE_COOKIE_NAME = 'passbook_language'
@ -72,8 +69,8 @@ INSTALLED_APPS = [
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.postgres', 'django.contrib.postgres',
'rest_framework', # 'rest_framework',
'drf_yasg', # 'drf_yasg',
'passbook.core.apps.PassbookCoreConfig', 'passbook.core.apps.PassbookCoreConfig',
'passbook.admin.apps.PassbookAdminConfig', 'passbook.admin.apps.PassbookAdminConfig',
'passbook.api.apps.PassbookAPIConfig', 'passbook.api.apps.PassbookAPIConfig',
@ -93,16 +90,6 @@ INSTALLED_APPS = [
'passbook.app_gw.apps.PassbookApplicationApplicationGatewayConfig', 'passbook.app_gw.apps.PassbookApplicationApplicationGatewayConfig',
] ]
# Message Tag fix for bootstrap CSS Classes
MESSAGE_TAGS = {
messages.DEBUG: 'primary',
messages.INFO: 'info',
messages.SUCCESS: 'success',
messages.WARNING: 'warning',
messages.ERROR: 'danger',
}
REST_FRAMEWORK = { REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions, # Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users. # or allow read-only access for unauthenticated users.
@ -114,17 +101,20 @@ REST_FRAMEWORK = {
CACHES = { CACHES = {
"default": { "default": {
"BACKEND": "django_redis.cache.RedisCache", "BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://%s" % CONFIG.get('redis'), "LOCATION": f"redis://{CONFIG.y('redis.host')}:6379/{CONFIG.y('redis.cache_db')}",
"OPTIONS": { "OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient", "CLIENT_CLASS": "django_redis.client.DefaultClient",
} }
} }
} }
DJANGO_REDIS_IGNORE_EXCEPTIONS = True
DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
MIDDLEWARE = [ MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'passbook.app_gw.middleware.ApplicationGatewayMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
@ -150,22 +140,20 @@ TEMPLATES = [
}, },
] ]
WSGI_APPLICATION = 'passbook.core.wsgi.application' WSGI_APPLICATION = 'passbook.root.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
DATABASES = {} DATABASES = {
for db_alias, db_config in CONFIG.get('databases').items(): 'default': {
DATABASES[db_alias] = { 'ENGINE': 'django.db.backends.postgresql',
'ENGINE': db_config.get('engine'), 'HOST': CONFIG.y('postgresql.host'),
'HOST': db_config.get('host'), 'NAME': CONFIG.y('postgresql.name'),
'NAME': db_config.get('name'), 'USER': CONFIG.y('postgresql.user'),
'USER': db_config.get('user'), 'PASSWORD': CONFIG.y('postgresql.password'),
'PASSWORD': db_config.get('password'),
'OPTIONS': db_config.get('options', {}),
} }
}
# Password validation # Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
@ -203,20 +191,13 @@ USE_TZ = True
# Celery settings # Celery settings
# Add a 10 minute timeout to all Celery tasks. # Add a 10 minute timeout to all Celery tasks.
CELERY_TASK_SOFT_TIME_LIMIT = 600 CELERY_TASK_SOFT_TIME_LIMIT = 600
CELERY_TIMEZONE = TIME_ZONE
CELERY_BEAT_SCHEDULE = {} CELERY_BEAT_SCHEDULE = {}
CELERY_CREATE_MISSING_QUEUES = True CELERY_CREATE_MISSING_QUEUES = True
CELERY_TASK_DEFAULT_QUEUE = 'passbook' CELERY_TASK_DEFAULT_QUEUE = 'passbook'
CELERY_BROKER_URL = 'amqp://%s' % CONFIG.get('rabbitmq') CELERY_BROKER_URL = (f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}"
CELERY_RESULT_BACKEND = 'rpc://' f":6379/{CONFIG.y('redis.message_queue_db')}")
CELERY_ACKS_LATE = True CELERY_RESULT_BACKEND = (f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}"
CELERY_BROKER_HEARTBEAT = 0 f":6379/{CONFIG.y('redis.message_queue_db')}")
CELERY_BEAT_SCHEDULE = {
'cleanup-expired-nonces': {
'task': 'passbook.core.tasks.clean_nonces',
'schedule': crontab(hour=1, minute=1)
}
}
if not DEBUG: if not DEBUG:
@ -224,11 +205,7 @@ if not DEBUG:
dsn="https://33cdbcb23f8b436dbe0ee06847410b67@sentry.beryju.org/3", dsn="https://33cdbcb23f8b436dbe0ee06847410b67@sentry.beryju.org/3",
integrations=[ integrations=[
DjangoIntegration(), DjangoIntegration(),
CeleryIntegration(), CeleryIntegration()
LoggingIntegration(
level=logging.INFO,
event_level=logging.ERROR
)
], ],
send_default_pii=True, send_default_pii=True,
before_send=before_send, before_send=before_send,
@ -240,96 +217,77 @@ if not DEBUG:
STATIC_URL = '/static/' STATIC_URL = '/static/'
structlog.configure_once(
processors=[
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(),
structlog.processors.StackInfoRenderer(),
# structlog.processors.format_exc_info,
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
],
context_class=structlog.threadlocal.wrap_dict(dict),
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
LOG_PRE_CHAIN = [
# Add the log level and a timestamp to the event_dict if the log entry
# is not from structlog.
structlog.stdlib.add_log_level,
structlog.processors.TimeStamper(),
]
with CONFIG.cd('log'): with CONFIG.cd('log'):
LOGGING_HANDLER_MAP = {
'passbook': 'DEBUG',
'django': 'WARNING',
'celery': 'WARNING',
'grpc': 'DEBUG',
'oauthlib': 'DEBUG',
'oauth2_provider': 'DEBUG',
'daphne': 'INFO',
}
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
'disable_existing_loggers': True, 'disable_existing_loggers': False,
'formatters': { 'formatters': {
'verbose': { "plain": {
'format': ('%(asctime)s %(levelname)-8s %(name)-55s ' "()": structlog.stdlib.ProcessorFormatter,
'%(funcName)-20s %(message)s'), "processor": structlog.processors.JSONRenderer(),
"foreign_pre_chain": LOG_PRE_CHAIN,
},
"colored": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.dev.ConsoleRenderer(colors=DEBUG),
"foreign_pre_chain": LOG_PRE_CHAIN,
}, },
'color': {
'()': 'colorlog.ColoredFormatter',
'format': ('%(log_color)s%(asctime)s %(levelname)-8s %(name)-55s '
'%(funcName)-20s %(message)s'),
'log_colors': {
'DEBUG': 'bold_black',
'INFO': 'white',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'bold_red',
'SUCCESS': 'green',
},
}
}, },
'handlers': { 'handlers': {
'console': { 'console': {
'level': CONFIG.get('level').get('console'), 'level': DEBUG,
'class': 'logging.StreamHandler', 'class': 'logging.StreamHandler',
'formatter': 'color', 'formatter': "colored" if DEBUG else "plain",
},
'syslog': {
'level': CONFIG.get('level').get('file'),
'class': 'logging.handlers.SysLogHandler',
'formatter': 'verbose',
'address': (CONFIG.get('syslog').get('host'),
CONFIG.get('syslog').get('port'))
},
'file': {
'level': CONFIG.get('level').get('file'),
'class': 'logging.FileHandler',
'formatter': 'verbose',
'filename': CONFIG.get('file'),
}, },
'queue': { 'queue': {
'level': CONFIG.get('level').get('console'), 'level': DEBUG,
'class': 'passbook.lib.log.QueueListenerHandler', 'class': 'passbook.lib.log.QueueListenerHandler',
'handlers': [ 'handlers': [
'cfg://handlers.console', 'cfg://handlers.console',
# 'cfg://handlers.syslog',
'cfg://handlers.file',
], ],
} }
}, },
'loggers': { 'loggers': {
'passbook': {
'handlers': ['queue'],
'level': 'DEBUG',
'propagate': True,
},
'django': {
'handlers': ['queue'],
'level': 'INFO',
'propagate': True,
},
'tasks': {
'handlers': ['queue'],
'level': 'DEBUG',
'propagate': True,
},
'cherrypy': {
'handlers': ['queue'],
'level': 'DEBUG',
'propagate': True,
},
'oauthlib': {
'handlers': ['queue'],
'level': 'DEBUG',
'propagate': True,
},
'oauth2_provider': {
'handlers': ['queue'],
'level': 'DEBUG',
'propagate': True,
},
'daphne': {
'handlers': ['queue'],
'level': 'INFO',
'propagate': True,
}
} }
} }
for handler_name, level in LOGGING_HANDLER_MAP.items():
LOGGING['loggers'][handler_name] = {
'handlers': ['console'],
'level': level,
'propagate': True,
}
TEST = False TEST = False
TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner' TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner'
@ -342,6 +300,7 @@ if any('test' in arg for arg in sys.argv):
TEST = True TEST = True
CELERY_TASK_ALWAYS_EAGER = True CELERY_TASK_ALWAYS_EAGER = True
_DISALLOWED_ITEMS = ['INSTALLED_APPS', 'MIDDLEWARE', 'AUTHENTICATION_BACKENDS'] _DISALLOWED_ITEMS = ['INSTALLED_APPS', 'MIDDLEWARE', 'AUTHENTICATION_BACKENDS']
# Load subapps's INSTALLED_APPS # Load subapps's INSTALLED_APPS
for _app in INSTALLED_APPS: for _app in INSTALLED_APPS: