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
152
153
154
155
|
# GeneNetwork logger
#
# The standard python logging module is very good. This logger adds a
# few facilities on top of that. Main one being that it picks up
# settings for log levels (global and by module) and (potentially)
# offers some fine grained log levels for the standard levels.
#
# All behaviour is defined here. Global settings (defined in
# default_settings.py).
#
# To use logging and settings put this at the top of a module:
#
# import utility.logger
# logger = utility.logger.getLogger(__name__ )
#
# To override global behaviour set the LOG_LEVEL in default_settings.py
# or use an environment variable, e.g.
#
# env LOG_LEVEL=INFO ./bin/genenetwork2
#
# To override log level for a module replace that with, for example,
#
# import logging
# import utility.logger
# logger = utility.logger.getLogger(__name__,level=logging.DEBUG)
#
# We'll add more overrides soon.
import logging
import string
from inspect import isfunction
from pprint import pformat as pf
from inspect import stack
import datetime
from utility.tools import LOG_LEVEL, LOG_LEVEL_DEBUG, LOG_SQL
class GNLogger:
"""A logger class with some additional functionality, such as
multiple parameter logging, SQL logging, timing, colors, and lazy
functions.
"""
def __init__(self, name):
self.logger = logging.getLogger(name)
def setLevel(self, value):
"""Set the undelying log level"""
self.logger.setLevel(value)
def debug(self, *args):
"""Call logging.debug for multiple args. Use (lazy) debugf and
level=num to filter on LOG_LEVEL_DEBUG.
"""
self.collect(self.logger.debug, *args)
def debug20(self, *args):
"""Call logging.debug for multiple args. Use level=num to filter on
LOG_LEVEL_DEBUG (NYI).
"""
if level <= LOG_LEVEL_DEBUG:
if self.logger.getEffectiveLevel() < 20:
self.collect(self.logger.debug, *args)
def info(self, *args):
"""Call logging.info for multiple args"""
self.collect(self.logger.info, *args)
def warning(self, *args):
"""Call logging.warning for multiple args"""
self.collect(self.logger.warning, *args)
# self.logger.warning(self.collect(*args))
def error(self, *args):
"""Call logging.error for multiple args"""
now = datetime.datetime.utcnow()
time_str = now.strftime('%H:%M:%S UTC %Y%m%d')
l = [time_str] + list(args)
self.collect(self.logger.error, *l)
def infof(self, *args):
"""Call logging.info for multiple args lazily"""
# only evaluate function when logging
if self.logger.getEffectiveLevel() < 30:
self.collectf(self.logger.debug, *args)
def debugf(self, level=0, *args):
"""Call logging.debug for multiple args lazily and handle
LOG_LEVEL_DEBUG correctly
"""
# only evaluate function when logging
if level <= LOG_LEVEL_DEBUG:
if self.logger.getEffectiveLevel() < 20:
self.collectf(self.logger.debug, *args)
def sql(self, sqlcommand, fun=None):
"""Log SQL command, optionally invoking a timed fun"""
if LOG_SQL:
caller = stack()[1][3]
if caller in ['fetchone', 'fetch1', 'fetchall']:
caller = stack()[2][3]
self.info(caller, sqlcommand)
if fun:
result = fun(sqlcommand)
if LOG_SQL:
self.info(result)
return result
def collect(self, fun, *args):
"""Collect arguments and use fun to output"""
out = "." + stack()[2][3]
for a in args:
if len(out) > 1:
out += ": "
if isinstance(a, str):
out = out + a
else:
out = out + pf(a, width=160)
fun(out)
def collectf(self, fun, *args):
"""Collect arguments and use fun to output one by one"""
out = "." + stack()[2][3]
for a in args:
if len(out) > 1:
out += ": "
if isfunction(a):
out += a()
else:
if isinstance(a, str):
out = out + a
else:
out = out + pf(a, width=160)
fun(out)
# Get the module logger. You can override log levels at the
# module level
def getLogger(name, level=None):
gnlogger = GNLogger(name)
logger = gnlogger.logger
if level:
logger.setLevel(level)
else:
logger.setLevel(LOG_LEVEL)
logger.info("Log level of " + name + " set to " + logging.getLevelName(logger.getEffectiveLevel()))
return gnlogger
|