Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

steal zamboni's logging

  • Loading branch information...
commit 358818220c52bd003c2474ba0d63646df2d51ca4 1 parent 1abc7d9
authored June 23, 2010
15  apps/basketauth/__init__.py
... ...
@@ -1,4 +1,4 @@
1  
-import logging
  1
+import commonware.log
2 2
 
3 3
 from django.http import HttpResponse
4 4
 
@@ -7,6 +7,9 @@
7 7
 from piston.models import Consumer as ConsumerModel
8 8
 
9 9
 
  10
+log = commonware.log.getLogger('basket')
  11
+
  12
+
10 13
 class BasketAuthentication(object):
11 14
     """
12 15
     This supplements Piston's auth system by providing 2-legged OAuth
@@ -42,15 +45,15 @@ def is_authenticated(self, request):
42 45
             self.server.verify_request(oauth_req, consumer, None)
43 46
             return True
44 47
         except ConsumerModel.DoesNotExist, e:
45  
-            logging.error(e)
  48
+            log.error(e)
46 49
             return False
47 50
         except oauth.Error, e:
48  
-            logging.error(e)
49  
-            logging.error(request)
  51
+            log.error(e)
  52
+            log.error(request)
50 53
             return False
51 54
         except:
52  
-            logging.error('fallback error')
53  
-            logging.error(request)
  55
+            log.error('fallback error')
  56
+            log.error(request)
54 57
 
55 58
     def challenge(self):
56 59
         response = HttpResponse(status=401)
10  apps/emailer/base.py
... ...
@@ -1,4 +1,4 @@
1  
-import logging
  1
+import commonware.log
2 2
 
3 3
 from django.conf import settings
4 4
 from django.core.exceptions import ValidationError
@@ -10,9 +10,7 @@
10 10
 from subscriptions.models import Subscriber
11 11
 
12 12
 
13  
-log = logging.getLogger('basket.emailer')
14  
-log.addHandler(logging.StreamHandler())
15  
-log.setLevel(settings.LOG_LEVEL)
  13
+log = commonware.log.getLogger('basket')
16 14
 
17 15
 mailchimp = MailChimp(settings.MAILCHIMP_API_KEY)
18 16
 
@@ -74,7 +72,7 @@ def send_email(self):
74 72
             log.info('Nothing to do: List of recipients is empty.')
75 73
             return
76 74
 
77  
-        log.debug('Establishing SMTP connection...')
  75
+        log.info('Establishing SMTP connection...')
78 76
         connection = mail.get_connection()
79 77
         connection.open()
80 78
 
@@ -91,7 +89,7 @@ def send_email(self):
91 89
                 msg.attach_alternative(html, 'text/html')
92 90
 
93 91
             try:
94  
-                log.debug('Sending email to %s' % recipient.email)
  92
+                log.info('Sending email to %s' % recipient.email)
95 93
                 msg.send(fail_silently=False)
96 94
             except Exception, e:
97 95
                 log.warning('Sending email to %s failed: %s' % (
5  apps/emailer/management/commands/sendmail.py
... ...
@@ -1,9 +1,9 @@
1 1
 import functools
2  
-import logging
3 2
 from optparse import make_option
4 3
 import os
5 4
 import tempfile
6 5
 
  6
+import commonware.log
7 7
 import lockfile
8 8
 
9 9
 from django.core.management.base import LabelCommand, CommandError
@@ -12,6 +12,7 @@
12 12
 
13 13
 
14 14
 LOCKFILE_PREFIX = 'basket_emailer_lock'
  15
+log = commonware.log.getLogger('basket')
15 16
 
16 17
 
17 18
 def locked(f):
@@ -29,7 +30,7 @@ def wrapper(self, label, **options):
29 30
             # Try to acquire the lock without blocking.
30 31
             lock.acquire(0)
31 32
         except lockfile.LockError:
32  
-            logging.debug('Aborting %s; lock acquisition failed.' % name)
  33
+            log.debug('Aborting %s; lock acquisition failed.' % name)
33 34
             return 0
34 35
         else:
35 36
             # We have the lock, call the function.
2  apps/subscriptions/tests.py
... ...
@@ -1,5 +1,3 @@
1  
-import logging
2  
-
3 1
 from django.core.exceptions import ValidationError
4 2
 from django import test
5 3
 
527  libs/dictconfig.py
... ...
@@ -0,0 +1,527 @@
  1
+import logging.handlers
  2
+import re
  3
+import sys
  4
+import types
  5
+
  6
+IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
  7
+
  8
+def valid_ident(s):
  9
+    m = IDENTIFIER.match(s)
  10
+    if not m:
  11
+        raise ValueError('Not a valid Python identifier: %r' % s)
  12
+    return True
  13
+
  14
+#
  15
+# This function is defined in logging only in recent versions of Python
  16
+#
  17
+try:
  18
+    from logging import _checkLevel
  19
+except ImportError:
  20
+    def _checkLevel(level):
  21
+        if isinstance(level, int):
  22
+            rv = level
  23
+        elif str(level) == level:
  24
+            if level not in logging._levelNames:
  25
+                raise ValueError('Unknown level: %r' % level)
  26
+            rv = logging._levelNames[level]
  27
+        else:
  28
+            raise TypeError('Level not an integer or a '
  29
+                            'valid string: %r' % level)
  30
+        return rv
  31
+
  32
+# The ConvertingXXX classes are wrappers around standard Python containers,
  33
+# and they serve to convert any suitable values in the container. The
  34
+# conversion converts base dicts, lists and tuples to their wrapped
  35
+# equivalents, whereas strings which match a conversion format are converted
  36
+# appropriately.
  37
+#
  38
+# Each wrapper should have a configurator attribute holding the actual
  39
+# configurator to use for conversion.
  40
+
  41
+class ConvertingDict(dict):
  42
+    """A converting dictionary wrapper."""
  43
+
  44
+    def __getitem__(self, key):
  45
+        value = dict.__getitem__(self, key)
  46
+        result = self.configurator.convert(value)
  47
+        #If the converted value is different, save for next time
  48
+        if value is not result:
  49
+            self[key] = result
  50
+            if type(result) in (ConvertingDict, ConvertingList,
  51
+                                ConvertingTuple):
  52
+                result.parent = self
  53
+                result.key = key
  54
+        return result
  55
+        
  56
+    def get(self, key, default=None):
  57
+        value = dict.get(self, key, default)
  58
+        result = self.configurator.convert(value)
  59
+        #If the converted value is different, save for next time
  60
+        if value is not result:
  61
+            self[key] = result
  62
+            if type(result) in (ConvertingDict, ConvertingList,
  63
+                                ConvertingTuple):
  64
+                result.parent = self
  65
+                result.key = key
  66
+        return result
  67
+        
  68
+    def pop(self, key, default=None):
  69
+        value = dict.pop(self, key, default)
  70
+        result = self.configurator.convert(value)
  71
+        if value is not result:
  72
+            if type(result) in (ConvertingDict, ConvertingList,
  73
+                                ConvertingTuple):
  74
+                result.parent = self
  75
+                result.key = key
  76
+        return result
  77
+
  78
+class ConvertingList(list):
  79
+    """A converting list wrapper."""
  80
+    def __getitem__(self, key):
  81
+        value = list.__getitem__(self, key)
  82
+        result = self.configurator.convert(value)
  83
+        #If the converted value is different, save for next time
  84
+        if value is not result:
  85
+            self[key] = result
  86
+            if type(result) in (ConvertingDict, ConvertingList,
  87
+                                ConvertingTuple):
  88
+                result.parent = self
  89
+                result.key = key
  90
+        return result
  91
+
  92
+    def pop(self, idx=-1):
  93
+        value = list.pop(self, idx)
  94
+        result = self.configurator.convert(value)
  95
+        if value is not result:
  96
+            if type(result) in (ConvertingDict, ConvertingList,
  97
+                                ConvertingTuple):
  98
+                result.parent = self
  99
+        return result
  100
+
  101
+class ConvertingTuple(tuple):
  102
+    """A converting tuple wrapper."""
  103
+    def __getitem__(self, key):
  104
+        value = tuple.__getitem__(self, key)
  105
+        result = self.configurator.convert(value)
  106
+        if value is not result:
  107
+            if type(result) in (ConvertingDict, ConvertingList,
  108
+                                ConvertingTuple):
  109
+                result.parent = self
  110
+                result.key = key
  111
+        return result
  112
+
  113
+class BaseConfigurator(object):
  114
+    """
  115
+    The configurator base class which defines some useful defaults.
  116
+    """
  117
+    
  118
+    CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
  119
+
  120
+    WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
  121
+    DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
  122
+    INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
  123
+    DIGIT_PATTERN = re.compile(r'^\d+$')
  124
+
  125
+    value_converters = {
  126
+        'ext' : 'ext_convert',
  127
+        'cfg' : 'cfg_convert',
  128
+    }
  129
+
  130
+    # We might want to use a different one, e.g. importlib
  131
+    importer = __import__
  132
+
  133
+    def __init__(self, config):
  134
+        self.config = ConvertingDict(config)
  135
+        self.config.configurator = self
  136
+
  137
+    def resolve(self, s):
  138
+        """
  139
+        Resolve strings to objects using standard import and attribute
  140
+        syntax.
  141
+        """
  142
+        name = s.split('.')
  143
+        used = name.pop(0)
  144
+        found = self.importer(used)
  145
+        for frag in name:
  146
+            used += '.' + frag
  147
+            try:
  148
+                found = getattr(found, frag)
  149
+            except AttributeError:
  150
+                self.importer(used)
  151
+                found = getattr(found, frag)
  152
+        return found
  153
+
  154
+    def ext_convert(self, value):
  155
+        """Default converter for the ext:// protocol."""
  156
+        return self.resolve(value)
  157
+    
  158
+    def cfg_convert(self, value):
  159
+        """Default converter for the cfg:// protocol."""
  160
+        rest = value
  161
+        m = self.WORD_PATTERN.match(rest)
  162
+        if m is None:
  163
+            raise ValueError("Unable to convert %r" % value)
  164
+        else:
  165
+            rest = rest[m.end():]
  166
+            d = self.config[m.groups()[0]]
  167
+            #print d, rest
  168
+            while rest:
  169
+                m = self.DOT_PATTERN.match(rest)
  170
+                if m:
  171
+                    d = d[m.groups()[0]]
  172
+                else:
  173
+                    m = self.INDEX_PATTERN.match(rest)
  174
+                    if m:
  175
+                        idx = m.groups()[0]
  176
+                        if not self.DIGIT_PATTERN.match(idx):
  177
+                            d = d[idx]
  178
+                        else:
  179
+                            try:
  180
+                                n = int(idx) # try as number first (most likely)
  181
+                                d = d[n]
  182
+                            except TypeError:
  183
+                                d = d[idx]
  184
+                if m:
  185
+                    rest = rest[m.end():]
  186
+                else:
  187
+                    raise ValueError('Unable to convert '
  188
+                                     '%r at %r' % (value, rest))
  189
+        #rest should be empty
  190
+        return d
  191
+
  192
+    def convert(self, value):
  193
+        """
  194
+        Convert values to an appropriate type. dicts, lists and tuples are
  195
+        replaced by their converting alternatives. Strings are checked to
  196
+        see if they have a conversion format and are converted if they do.
  197
+        """
  198
+        if not isinstance(value, ConvertingDict) and isinstance(value, dict):
  199
+            value = ConvertingDict(value)
  200
+            value.configurator = self
  201
+        elif not isinstance(value, ConvertingList) and isinstance(value, list):
  202
+            value = ConvertingList(value)
  203
+            value.configurator = self
  204
+        elif not isinstance(value, ConvertingTuple) and\
  205
+                 isinstance(value, tuple):
  206
+            value = ConvertingTuple(value)
  207
+            value.configurator = self
  208
+        elif isinstance(value, basestring): # str for py3k
  209
+            m = self.CONVERT_PATTERN.match(value)
  210
+            if m:
  211
+                d = m.groupdict()
  212
+                prefix = d['prefix']
  213
+                converter = self.value_converters.get(prefix, None)
  214
+                if converter:
  215
+                    suffix = d['suffix']
  216
+                    converter = getattr(self, converter)
  217
+                    value = converter(suffix)
  218
+        return value
  219
+    
  220
+    def configure_custom(self, config):
  221
+        """Configure an object with a user-supplied factory."""
  222
+        c = config.pop('()')
  223
+        if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
  224
+            c = self.resolve(c)
  225
+        props = config.pop('.', None)
  226
+        # Check for valid identifiers
  227
+        kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
  228
+        result = c(**kwargs)
  229
+        if props:
  230
+            for name, value in props.items():
  231
+                setattr(result, name, value)
  232
+        return result
  233
+
  234
+    def as_tuple(self, value):
  235
+        """Utility function which converts lists to tuples."""
  236
+        if isinstance(value, list):
  237
+            value = tuple(value)
  238
+        return value
  239
+
  240
+class DictConfigurator(BaseConfigurator):
  241
+    """
  242
+    Configure logging using a dictionary-like object to describe the
  243
+    configuration.
  244
+    """
  245
+
  246
+    def configure(self):
  247
+        """Do the configuration."""
  248
+
  249
+        config = self.config
  250
+        if 'version' not in config:
  251
+            raise ValueError("dictionary doesn't specify a version")
  252
+        if config['version'] != 1:
  253
+            raise ValueError("Unsupported version: %s" % config['version'])
  254
+        incremental = config.pop('incremental', False)
  255
+        EMPTY_DICT = {}
  256
+        logging._acquireLock()
  257
+        try:
  258
+            if incremental:
  259
+                handlers = config.get('handlers', EMPTY_DICT)
  260
+                # incremental handler config only if handler name
  261
+                # ties in to logging._handlers (Python 2.7)
  262
+                if sys.version_info[:2] == (2, 7):
  263
+                    for name in handlers:
  264
+                        if name not in logging._handlers:
  265
+                            raise ValueError('No handler found with '
  266
+                                             'name %r'  % name)
  267
+                        else:
  268
+                            try:
  269
+                                handler = logging._handlers[name]
  270
+                                handler_config = handlers[name]
  271
+                                level = handler_config.get('level', None)
  272
+                                if level:
  273
+                                    handler.setLevel(_checkLevel(level))
  274
+                            except StandardError, e:
  275
+                                raise ValueError('Unable to configure handler '
  276
+                                                 '%r: %s' % (name, e))
  277
+                loggers = config.get('loggers', EMPTY_DICT)
  278
+                for name in loggers:
  279
+                    try:
  280
+                        self.configure_logger(name, loggers[name], True)
  281
+                    except StandardError, e:
  282
+                        raise ValueError('Unable to configure logger '
  283
+                                         '%r: %s' % (name, e))
  284
+                root = config.get('root', None)
  285
+                if root:
  286
+                    try:
  287
+                        self.configure_root(root, True)
  288
+                    except StandardError, e:
  289
+                        raise ValueError('Unable to configure root '
  290
+                                         'logger: %s' % e)
  291
+            else:
  292
+                disable_existing = config.pop('disable_existing_loggers', True)
  293
+                
  294
+                logging._handlers.clear()
  295
+                del logging._handlerList[:]
  296
+                    
  297
+                # Do formatters first - they don't refer to anything else
  298
+                formatters = config.get('formatters', EMPTY_DICT)
  299
+                for name in formatters:
  300
+                    try:
  301
+                        formatters[name] = self.configure_formatter(
  302
+                                                            formatters[name])
  303
+                    except StandardError, e:
  304
+                        raise ValueError('Unable to configure '
  305
+                                         'formatter %r: %s' % (name, e))
  306
+                # Next, do filters - they don't refer to anything else, either
  307
+                filters = config.get('filters', EMPTY_DICT)
  308
+                for name in filters:
  309
+                    try:
  310
+                        filters[name] = self.configure_filter(filters[name])
  311
+                    except StandardError, e:
  312
+                        raise ValueError('Unable to configure '
  313
+                                         'filter %r: %s' % (name, e))
  314
+
  315
+                # Next, do handlers - they refer to formatters and filters
  316
+                # As handlers can refer to other handlers, sort the keys
  317
+                # to allow a deterministic order of configuration
  318
+                handlers = config.get('handlers', EMPTY_DICT)
  319
+                for name in sorted(handlers):
  320
+                    try:
  321
+                        handler = self.configure_handler(handlers[name])
  322
+                        handler.name = name
  323
+                        handlers[name] = handler
  324
+                    except StandardError, e:
  325
+                        raise ValueError('Unable to configure handler '
  326
+                                         '%r: %s' % (name, e))
  327
+                # Next, do loggers - they refer to handlers and filters
  328
+                
  329
+                #we don't want to lose the existing loggers,
  330
+                #since other threads may have pointers to them.
  331
+                #existing is set to contain all existing loggers,
  332
+                #and as we go through the new configuration we
  333
+                #remove any which are configured. At the end,
  334
+                #what's left in existing is the set of loggers
  335
+                #which were in the previous configuration but
  336
+                #which are not in the new configuration.
  337
+                root = logging.root
  338
+                existing = root.manager.loggerDict.keys()
  339
+                #The list needs to be sorted so that we can
  340
+                #avoid disabling child loggers of explicitly
  341
+                #named loggers. With a sorted list it is easier
  342
+                #to find the child loggers.
  343
+                existing.sort()
  344
+                #We'll keep the list of existing loggers
  345
+                #which are children of named loggers here...
  346
+                child_loggers = []
  347
+                #now set up the new ones...
  348
+                loggers = config.get('loggers', EMPTY_DICT)
  349
+                for name in loggers:
  350
+                    if name in existing:
  351
+                        i = existing.index(name)
  352
+                        prefixed = name + "."
  353
+                        pflen = len(prefixed)
  354
+                        num_existing = len(existing)
  355
+                        i = i + 1 # look at the entry after name
  356
+                        while (i < num_existing) and\
  357
+                              (existing[i][:pflen] == prefixed):
  358
+                            child_loggers.append(existing[i])
  359
+                            i = i + 1
  360
+                        existing.remove(name)
  361
+                    try:
  362
+                        self.configure_logger(name, loggers[name])
  363
+                    except StandardError, e:
  364
+                        raise ValueError('Unable to configure logger '
  365
+                                         '%r: %s' % (name, e))
  366
+                    
  367
+                #Disable any old loggers. There's no point deleting
  368
+                #them as other threads may continue to hold references
  369
+                #and by disabling them, you stop them doing any logging.
  370
+                #However, don't disable children of named loggers, as that's
  371
+                #probably not what was intended by the user.
  372
+                for log in existing:
  373
+                    logger = root.manager.loggerDict[log]
  374
+                    if log in child_loggers:
  375
+                        logger.level = logging.NOTSET
  376
+                        logger.handlers = []
  377
+                        logger.propagate = True
  378
+                    elif disable_existing:
  379
+                        logger.disabled = True
  380
+    
  381
+                # And finally, do the root logger
  382
+                root = config.get('root', None)
  383
+                if root:
  384
+                    try:
  385
+                        self.configure_root(root)                        
  386
+                    except StandardError, e:
  387
+                        raise ValueError('Unable to configure root '
  388
+                                         'logger: %s' % e)
  389
+        finally:
  390
+            logging._releaseLock()
  391
+
  392
+    def configure_formatter(self, config):
  393
+        """Configure a formatter from a dictionary."""
  394
+        if '()' in config:
  395
+            factory = config['()'] # for use in exception handler
  396
+            try:
  397
+                result = self.configure_custom(config)
  398
+            except TypeError, te:
  399
+                if "'format'" not in str(te):
  400
+                    raise
  401
+                #Name of parameter changed from fmt to format.
  402
+                #Retry with old name.
  403
+                #This is so that code can be used with older Python versions
  404
+                #(e.g. by Django)
  405
+                config['fmt'] = config.pop('format')
  406
+                config['()'] = factory
  407
+                result = self.configure_custom(config)
  408
+        else:
  409
+            fmt = config.get('format', None)
  410
+            dfmt = config.get('datefmt', None)
  411
+            result = logging.Formatter(fmt, dfmt)
  412
+        return result
  413
+    
  414
+    def configure_filter(self, config):
  415
+        """Configure a filter from a dictionary."""
  416
+        if '()' in config:
  417
+            result = self.configure_custom(config)
  418
+        else:
  419
+            name = config.get('name', '')
  420
+            result = logging.Filter(name)
  421
+        return result
  422
+
  423
+    def add_filters(self, filterer, filters):
  424
+        """Add filters to a filterer from a list of names."""
  425
+        for f in filters:
  426
+            try:
  427
+                filterer.addFilter(self.config['filters'][f])
  428
+            except StandardError, e:
  429
+                raise ValueError('Unable to add filter %r: %s' % (f, e))
  430
+
  431
+    def configure_handler(self, config):
  432
+        """Configure a handler from a dictionary."""
  433
+        formatter = config.pop('formatter', None)
  434
+        if formatter:
  435
+            try:
  436
+                formatter = self.config['formatters'][formatter]
  437
+            except StandardError, e:
  438
+                raise ValueError('Unable to set formatter '
  439
+                                 '%r: %s' % (formatter, e))
  440
+        level = config.pop('level', None)
  441
+        filters = config.pop('filters', None)
  442
+        if '()' in config:
  443
+            c = config.pop('()')
  444
+            if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
  445
+                c = self.resolve(c)
  446
+            factory = c
  447
+        else:
  448
+            klass = self.resolve(config.pop('class'))
  449
+            #Special case for handler which refers to another handler
  450
+            if issubclass(klass, logging.handlers.MemoryHandler) and\
  451
+                'target' in config:
  452
+                try:
  453
+                    config['target'] = self.config['handlers'][config['target']]
  454
+                except StandardError, e:
  455
+                    raise ValueError('Unable to set target handler '
  456
+                                     '%r: %s' % (config['target'], e))
  457
+            elif issubclass(klass, logging.handlers.SMTPHandler) and\
  458
+                'mailhost' in config:
  459
+                config['mailhost'] = self.as_tuple(config['mailhost'])
  460
+            elif issubclass(klass, logging.handlers.SysLogHandler) and\
  461
+                'address' in config:
  462
+                config['address'] = self.as_tuple(config['address'])
  463
+            factory = klass
  464
+        kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
  465
+        try:
  466
+            result = factory(**kwargs)
  467
+        except TypeError, te:
  468
+            if "'stream'" not in str(te):
  469
+                raise
  470
+            #The argument name changed from strm to stream
  471
+            #Retry with old name.
  472
+            #This is so that code can be used with older Python versions
  473
+            #(e.g. by Django)
  474
+            kwargs['strm'] = kwargs.pop('stream')
  475
+            result = factory(**kwargs)
  476
+        if formatter:
  477
+            result.setFormatter(formatter)
  478
+        if level is not None:
  479
+            result.setLevel(_checkLevel(level))
  480
+        if filters:
  481
+            self.add_filters(result, filters)
  482
+        return result
  483
+
  484
+    def add_handlers(self, logger, handlers):
  485
+        """Add handlers to a logger from a list of names."""
  486
+        for h in handlers:
  487
+            try:
  488
+                logger.addHandler(self.config['handlers'][h])
  489
+            except StandardError, e:
  490
+                raise ValueError('Unable to add handler %r: %s' % (h, e))
  491
+
  492
+    def common_logger_config(self, logger, config, incremental=False):
  493
+        """
  494
+        Perform configuration which is common to root and non-root loggers.
  495
+        """
  496
+        level = config.get('level', None)
  497
+        if level is not None:
  498
+            logger.setLevel(_checkLevel(level))
  499
+        if not incremental:
  500
+            #Remove any existing handlers
  501
+            for h in logger.handlers[:]:
  502
+                logger.removeHandler(h)
  503
+            handlers = config.get('handlers', None)
  504
+            if handlers:
  505
+                self.add_handlers(logger, handlers)
  506
+            filters = config.get('filters', None)
  507
+            if filters:
  508
+                self.add_filters(logger, filters)
  509
+        
  510
+    def configure_logger(self, name, config, incremental=False):
  511
+        """Configure a non-root logger from a dictionary."""
  512
+        logger = logging.getLogger(name)
  513
+        self.common_logger_config(logger, config, incremental)
  514
+        propagate = config.get('propagate', None)
  515
+        if propagate is not None:
  516
+            logger.propagate = propagate
  517
+            
  518
+    def configure_root(self, config, incremental=False):
  519
+        """Configure a root logger from a dictionary."""
  520
+        root = logging.getLogger()
  521
+        self.common_logger_config(root, config, incremental)
  522
+
  523
+dictConfigClass = DictConfigurator
  524
+
  525
+def dictConfig(config):
  526
+    """Configure logging using a dictionary."""
  527
+    dictConfigClass(config).configure()
69  log_settings.py
... ...
@@ -0,0 +1,69 @@
  1
+import logging
  2
+import logging.handlers
  3
+
  4
+from django.conf import settings
  5
+
  6
+import commonware.log
  7
+import dictconfig
  8
+
  9
+
  10
+class NullHandler(logging.Handler):
  11
+
  12
+    def emit(self, record):
  13
+        pass
  14
+
  15
+
  16
+base_fmt = ('%(name)s:%(levelname)s %(message)s '
  17
+            ':%(pathname)s:%(lineno)s')
  18
+
  19
+cfg = {
  20
+    'version': 1,
  21
+    'filters': {},
  22
+    'formatters': {
  23
+        'debug': {
  24
+            '()': commonware.log.Formatter,
  25
+            'datefmt': '%H:%M:%s',
  26
+            'format': '%(asctime)s ' + base_fmt,
  27
+        },
  28
+        'prod': {
  29
+            '()': commonware.log.Formatter,
  30
+            'datefmt': '%H:%M:%s',
  31
+            'format': '%s: [%%(REMOTE_ADDR)s] %s' % (settings.SYSLOG_TAG,
  32
+                                                     base_fmt),
  33
+        },
  34
+    },
  35
+    'handlers': {
  36
+        'console': {
  37
+            '()': logging.StreamHandler,
  38
+            'formatter': 'debug',
  39
+        },
  40
+        'syslog': {
  41
+            '()': logging.handlers.SysLogHandler,
  42
+            'facility': logging.handlers.SysLogHandler.LOG_LOCAL7,
  43
+            'formatter': 'prod',
  44
+        },
  45
+        'null': {
  46
+            '()': NullHandler,
  47
+        },
  48
+    },
  49
+    'loggers': {
  50
+        'basket': {},
  51
+    },
  52
+    'root': {},
  53
+}
  54
+
  55
+for key, value in settings.LOGGING.items():
  56
+    cfg[key].update(value)
  57
+
  58
+# Set the level and handlers for all loggers.
  59
+for logger in cfg['loggers'].values() + [cfg['root']]:
  60
+    syslog = settings.HAS_SYSLOG and not settings.DEBUG
  61
+    if 'handlers' not in logger:
  62
+        logger['handlers'] = ['syslog' if syslog else 'console']
  63
+    if 'level' not in logger:
  64
+        logger['level'] = settings.LOG_LEVEL
  65
+    if logger is not cfg['root'] and 'propagate' not in logger:
  66
+        logger['propagate'] = False
  67
+
  68
+
  69
+dictconfig.dictConfig(cfg)
3  manage.py
@@ -42,6 +42,9 @@
42 42
 # needs to access settings, so we'll setup the environ early.
43 43
 setup_environ(settings)
44 44
 
  45
+# Import for side-effect: configures our logging handlers.
  46
+# pylint: disable-msg=W0611
  47
+import log_settings
45 48
 
46 49
 if __name__ == "__main__":
47 50
     execute_manager(settings)
16  settings.py
@@ -109,10 +109,22 @@
109 109
 
110 110
 LANGUAGES = ('en-US', 'es-ES')
111 111
 
112  
-LOG_LEVEL = logging.DEBUG
113  
-
114 112
 DEFAULT_FROM_EMAIL = 'basket@mozilla.com'
115 113
 DEFAULT_FROM_NAME = 'Mozilla'
116 114
 
117 115
 # MailChimp API
118 116
 MAILCHIMP_API_KEY = ''
  117
+
  118
+# Logging
  119
+LOG_LEVEL = logging.DEBUG
  120
+HAS_SYSLOG = True  # syslog is used if HAS_SYSLOG and NOT DEBUG.
  121
+SYSLOG_TAG = "http_app_basket"
  122
+# See PEP 391 and log_settings.py for formatting help.  Each section of LOGGING
  123
+# will get merged into the corresponding section of log_settings.py.
  124
+# Handlers and log levels are set up automatically based on LOG_LEVEL and DEBUG
  125
+# unless you set them here.  Messages will not propagate through a logger
  126
+# unless propagate: True is set.
  127
+LOGGING = {
  128
+    'loggers': {
  129
+    },
  130
+}

0 notes on commit 3588182

Please sign in to comment.
Something went wrong with that request. Please try again.