1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5from __future__ import absolute_import, unicode_literals
6
7import logging
8
9
10class LoggingMixin(object):
11    """Provides functionality to control logging."""
12
13    def populate_logger(self, name=None):
14        """Ensure this class instance has a logger associated with it.
15
16        Users of this mixin that call log() will need to ensure self._logger is
17        a logging.Logger instance before they call log(). This function ensures
18        self._logger is defined by populating it if it isn't.
19        """
20        if hasattr(self, '_logger'):
21            return
22
23        if name is None:
24            name = '.'.join([self.__module__, self.__class__.__name__])
25
26        self._logger = logging.getLogger(name)
27
28    def log(self, level, action, params, format_str):
29        """Log a structured log event.
30
31        A structured log event consists of a logging level, a string action, a
32        dictionary of attributes, and a formatting string.
33
34        The logging level is one of the logging.* constants, such as
35        logging.INFO.
36
37        The action string is essentially the enumeration of the event. Each
38        different type of logged event should have a different action.
39
40        The params dict is the metadata constituting the logged event.
41
42        The formatting string is used to convert the structured message back to
43        human-readable format. Conversion back to human-readable form is
44        performed by calling format() on this string, feeding into it the dict
45        of attributes constituting the event.
46
47        Example Usage
48        -------------
49
50        self.log(logging.DEBUG, 'login', {'username': 'johndoe'},
51            'User login: {username}')
52        """
53        self._logger.log(level, format_str,
54                         extra={'action': action, 'params': params})
55