|
| 1 | +""" |
| 2 | +Settings for Datadog tracer are all namespaced in the DATADOG_TRACE setting. |
| 3 | +For example your project's `settings.py` file might look like this: |
| 4 | +
|
| 5 | +DATADOG_TRACE = { |
| 6 | + 'TRACER': 'myapp.tracer', |
| 7 | +} |
| 8 | +
|
| 9 | +This module provides the `setting` object, that is used to access |
| 10 | +Datadog settings, checking for user settings first, then falling |
| 11 | +back to the defaults. |
| 12 | +""" |
| 13 | +from __future__ import unicode_literals |
| 14 | + |
| 15 | +import importlib |
| 16 | + |
| 17 | +from django.conf import settings as django_settings |
| 18 | + |
| 19 | +from django.test.signals import setting_changed |
| 20 | + |
| 21 | + |
| 22 | +USER_SETTINGS = getattr(django_settings, 'DATADOG_TRACE', None) |
| 23 | + |
| 24 | +# List of available settings with their defaults |
| 25 | +DEFAULTS = { |
| 26 | + 'TRACER': 'ddtrace.tracer', |
| 27 | + 'DEFAULT_SERVICE': 'django', |
| 28 | + 'ENABLED': not django_settings.DEBUG, |
| 29 | +} |
| 30 | + |
| 31 | +# List of settings that may be in string import notation. |
| 32 | +IMPORT_STRINGS = ( |
| 33 | + 'TRACER', |
| 34 | +) |
| 35 | + |
| 36 | +# List of settings that have been removed |
| 37 | +REMOVED_SETTINGS = () |
| 38 | + |
| 39 | + |
| 40 | +def import_from_string(val, setting_name): |
| 41 | + """ |
| 42 | + Attempt to import a class from a string representation. |
| 43 | + """ |
| 44 | + try: |
| 45 | + # Nod to tastypie's use of importlib. |
| 46 | + parts = val.split('.') |
| 47 | + module_path, class_name = '.'.join(parts[:-1]), parts[-1] |
| 48 | + module = importlib.import_module(module_path) |
| 49 | + return getattr(module, class_name) |
| 50 | + except (ImportError, AttributeError) as e: |
| 51 | + msg = 'Could not import "{}" for setting "{}". {}: {}.'.format( |
| 52 | + val, setting_name, |
| 53 | + e.__class__.__name__, e |
| 54 | + ) |
| 55 | + |
| 56 | + raise ImportError(msg) |
| 57 | + |
| 58 | + |
| 59 | +class DatadogSettings(object): |
| 60 | + """ |
| 61 | + A settings object, that allows Datadog settings to be accessed as properties. |
| 62 | + For example: |
| 63 | +
|
| 64 | + from ddtrace.contrib.django.conf import settings |
| 65 | +
|
| 66 | + tracer = settings.TRACER |
| 67 | +
|
| 68 | + Any setting with string import paths will be automatically resolved |
| 69 | + and return the class, rather than the string literal. |
| 70 | + """ |
| 71 | + def __init__(self, user_settings=None, defaults=None, import_strings=None): |
| 72 | + if user_settings: |
| 73 | + self._user_settings = self.__check_user_settings(user_settings) |
| 74 | + self.defaults = defaults or DEFAULTS |
| 75 | + self.import_strings = import_strings or IMPORT_STRINGS |
| 76 | + |
| 77 | + @property |
| 78 | + def user_settings(self): |
| 79 | + if not hasattr(self, '_user_settings'): |
| 80 | + self._user_settings = getattr(settings, 'DATADOG_TRACE', {}) |
| 81 | + return self._user_settings |
| 82 | + |
| 83 | + def __getattr__(self, attr): |
| 84 | + if attr not in self.defaults: |
| 85 | + raise AttributeError('Invalid setting: "{}"'.format(attr)) |
| 86 | + |
| 87 | + try: |
| 88 | + # Check if present in user settings |
| 89 | + val = self.user_settings[attr] |
| 90 | + except KeyError: |
| 91 | + # Otherwise, fall back to defaults |
| 92 | + val = self.defaults[attr] |
| 93 | + |
| 94 | + # Coerce import strings into classes |
| 95 | + if attr in self.import_strings: |
| 96 | + val = import_from_string(val, attr) |
| 97 | + |
| 98 | + # Cache the result |
| 99 | + setattr(self, attr, val) |
| 100 | + return val |
| 101 | + |
| 102 | + def __check_user_settings(self, user_settings): |
| 103 | + SETTINGS_DOC = 'http://pypi.datadoghq.com/trace-dev/docs/#module-ddtrace.contrib.django' |
| 104 | + for setting in REMOVED_SETTINGS: |
| 105 | + if setting in user_settings: |
| 106 | + raise RuntimeError( |
| 107 | + 'The "{}" setting has been removed, check "{}".'.format(setting, SETTINGS_DOC) |
| 108 | + ) |
| 109 | + return user_settings |
| 110 | + |
| 111 | + |
| 112 | +settings = DatadogSettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS) |
| 113 | + |
| 114 | + |
| 115 | +def reload_settings(*args, **kwargs): |
| 116 | + """ |
| 117 | + Triggers a reload when Django emits the reloading signal |
| 118 | + """ |
| 119 | + global settings |
| 120 | + setting, value = kwargs['setting'], kwargs['value'] |
| 121 | + if setting == 'DATADOG_TRACE': |
| 122 | + settings = DatadogSettings(value, DEFAULTS, IMPORT_STRINGS) |
| 123 | + |
| 124 | + |
| 125 | +setting_changed.connect(reload_settings) |
0 commit comments