summaryrefslogtreecommitdiffstats
path: root/aw_api/Logger.py
blob: ea3910c4457f6964b20ab58db2b071840caf7456 (plain) (blame)
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
#!/usr/bin/python
#
# Copyright 2009 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""Interface for handling logging."""

__author__ = 'api.arogal@gmail.com (Adam Rogal)'

import logging
import os
import sys

from aw_api import LIB_NAME
from aw_api import LIB_VERSION


class Logger(object):

  """Responsible for impementing logging.

  Allows to write to an external log or a console. There are 4 types of
  handlers denoted by the handler constants: NONE, FILE, CONSOLE, and
  FILE_AND_CONSOLE. There are also 6 verbosity levels defined by the constants:
  NOTSET, DEBUG, INFO, WARNING, ERROR, and CRITICAL. These constants are
  derived from the original logging module with CRITCAL being the highest
  importance.

  This class is a wrapper for the standard logging module.
  """

  # Handler constants.
  NONE = 0
  FILE = 1
  CONSOLE = 2
  FILE_AND_CONSOLE = 3

  # Level constants.
  CRITICAL = logging.CRITICAL
  ERROR = logging.ERROR
  WARNING = logging.WARNING
  INFO = logging.INFO
  DEBUG = logging.DEBUG
  NOTSET = logging.NOTSET

  def __init__(self, log_path=os.path.join(os.getcwd(), 'logs')):
    """Inits Logger.

    Args:
      [optional]
      log_path: str absolute or relative path to the logs directory.
    """
    self.__log_path = log_path
    self.__log_table = {}

  def __CreateLog(self, log_name, log_level=NOTSET, log_handler=FILE,
                  stream=sys.stderr):
    """Creates the log used for logging.

    Args:
      log_name: str the name of the log. If the log is handled by an external
                file, this will be the file name.
      [optional]
      log_level: str the level of the log. Should be one of CRITICAL, ERROR,
                 WARNING, INFO, DEBUG, or NOTSET.
      log_handler: int the type of log handler. Should be one of NONE, FILE,
                   CONSOLE, or FILE_AND_CONSOLE.
      stream: file the stream to send data into.
    """
    logger = logging.getLogger(log_name)

    # Update log level to reflect changes. If a higher log level is given
    # the logger should raise it's boundary.
    if log_level < logger.level or logger.level == logging.NOTSET:
      logger.setLevel(log_level)

    if (log_name in self.__log_table and
        self.__log_table[log_name] == Logger.FILE_AND_CONSOLE):
      # Don't add any more handlers.
      return

    # Create an entry for log name.
    if log_name not in self.__log_table:
      self.__log_table[log_name] = Logger.NONE

    if log_handler != Logger.NONE:
      lib_sig = '%s v%s' % (LIB_NAME, LIB_VERSION)
      fmt = '[%(asctime)s::%(levelname)s::' + lib_sig + '] %(message)s'
      # Add FILE handler if needed.
      if (log_handler == Logger.FILE or
          log_handler == Logger.FILE_AND_CONSOLE and
          self.__log_table[log_name] != Logger.FILE):
        if not os.path.exists(self.__log_path):
          os.makedirs(self.__log_path)
        fh = logging.FileHandler(os.path.join(self.__log_path,
                                              '%s.log' % log_name))
        fh.setLevel(log_level)
        fh.setFormatter(logging.Formatter(fmt))
        logger.addHandler(fh)
        # Binary arithmetic to yield updated handler.
        self.__log_table[log_name] = self.__log_table[log_name] + Logger.FILE

      # Add CONSOLE handler if needed.
      if (log_handler == Logger.CONSOLE or
          log_handler == Logger.FILE_AND_CONSOLE and
          self.__log_table[log_name] != Logger.CONSOLE):
        ch = logging.StreamHandler(stream)
        ch.setLevel(log_level)
        ch.setFormatter(logging.Formatter(fmt))
        logger.addHandler(ch)
        # Binary arithmetic to yield updated handler.
        self.__log_table[log_name] = self.__log_table[log_name] + Logger.CONSOLE

  def Log(self, log_name, message, log_level=NOTSET, log_handler=FILE):
    """Log message to an external file.

    Args:
      log_name: str the name of the log. If the log is handled by an external
                file, this will be the file name appended by log.
      message: str message to log.
      [optional]
      log_level: int the level of importance of the current message. Not
                 supplying this parameter will cause the logger to log at the
                 lowest level of its handlers. If a handler has cut-off higher
                 than this lowest level, the message will be ignored by that
                 handler.
      log_handler: int the type of log handler. Should be one of NONE, FILE,
                   CONSOLE, or FILE_AND_CONSOLE.
    """
    logger = logging.getLogger(log_name)

    # Instantiate handlers for logger with default values if none exists.
    if not logger.handlers:
      self.__CreateLog(log_name, log_level, log_handler)

    if log_level == Logger.NOTSET:
      logger.log(logger.getEffectiveLevel(), message)
    else:
      logger.log(log_level, message)