1
|
1 |
|
from exception_wrappers import ExceptionWrapper |
2
|
1 |
|
from six import add_metaclass |
3
|
1 |
|
from sortedcontainers import SortedSet |
4
|
1 |
|
import collections |
5
|
1 |
|
import logging |
6
|
1 |
|
import sys |
7
|
1 |
|
import time |
8
|
|
|
|
9
|
1 |
|
Record = collections.namedtuple('Record', 'level timestamp message') |
10
|
1 |
|
log = logging.getLogger(__name__) |
11
|
|
|
|
12
|
|
|
|
13
|
1 |
|
class InterfaceMessagesMeta(type): |
14
|
1 |
|
@property |
15
|
|
|
def critical(cls): |
16
|
1 |
|
return cls.severity >= logging.CRITICAL |
17
|
|
|
|
18
|
1 |
|
@property |
19
|
|
|
def message(cls): |
20
|
|
|
if not cls.records: |
21
|
|
|
return logging.NOTSET |
22
|
|
|
|
23
|
|
|
# Return the latest highest level/severity message |
24
|
|
|
return cls.records[-1].message |
25
|
|
|
|
26
|
1 |
|
@property |
27
|
|
|
def record(cls): |
28
|
|
|
if not cls.records: |
29
|
|
|
return None |
30
|
|
|
|
31
|
|
|
return cls.records[-1] |
32
|
|
|
|
33
|
1 |
|
@property |
34
|
|
|
def severity(cls): |
35
|
1 |
|
if not cls.records: |
36
|
1 |
|
return logging.NOTSET |
37
|
|
|
|
38
|
|
|
# Return the highest error level/severity |
39
|
|
|
return cls.records[-1].level |
40
|
|
|
|
41
|
|
|
|
42
|
1 |
|
@add_metaclass(InterfaceMessagesMeta) |
43
|
1 |
|
class InterfaceMessages(object): |
44
|
1 |
|
records = SortedSet() |
45
|
|
|
|
46
|
1 |
|
@classmethod |
47
|
|
|
def add(cls, level, message, *args): |
48
|
|
|
try: |
49
|
|
|
message = message % args |
50
|
|
|
except TypeError: |
51
|
|
|
log.warn('Unable to format string %r, with arguments: %r', message, args) |
52
|
|
|
|
53
|
|
|
# Append message to log file |
54
|
|
|
if level <= logging.CRITICAL: |
55
|
|
|
log.log(level, message) |
56
|
|
|
else: |
57
|
|
|
log.log(logging.CRITICAL, message) |
58
|
|
|
|
59
|
|
|
# Add interface message record |
60
|
|
|
cls.records.add(Record( |
61
|
|
|
level=level, |
62
|
|
|
timestamp=time.time(), |
63
|
|
|
message=message |
64
|
|
|
)) |
65
|
|
|
|
66
|
1 |
|
@classmethod |
67
|
1 |
|
def add_exception(cls, level, message, exc_info=None): |
68
|
|
|
if exc_info is None: |
69
|
|
|
exc_info = sys.exc_info() |
70
|
|
|
|
71
|
|
|
# Ensure message has been provided |
72
|
|
|
if not message: |
73
|
|
|
log.warn('Ignoring add_exception() call, no message was provided') |
74
|
|
|
return |
75
|
|
|
|
76
|
|
|
# Append message to log file |
77
|
|
|
log.log(level, message, exc_info=exc_info) |
78
|
|
|
|
79
|
|
|
# Add interface message record |
80
|
|
|
cls.records.add(Record( |
81
|
|
|
level=level, |
82
|
|
|
timestamp=time.time(), |
83
|
|
|
message=message |
84
|
|
|
)) |
85
|
|
|
|
86
|
1 |
|
@classmethod |
87
|
|
|
def bind(cls): |
88
|
|
|
ExceptionWrapper.on('exception', cls._on_exception) |
89
|
|
|
|
90
|
|
|
log.info('Bound to exception events') |
91
|
|
|
|
92
|
1 |
|
@classmethod |
93
|
|
|
def _on_exception(cls, source, message, exc_info): |
|
|
|
|
94
|
|
|
# Append error message |
95
|
|
|
InterfaceMessages.add_exception(logging.CRITICAL, message, exc_info) |
96
|
|
|
|