-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtee.py
101 lines (77 loc) · 3.33 KB
/
tee.py
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
from typing import Any, Callable, List, Sequence, Type, cast
import structlog
from dataclasses import dataclass, field
__all__ = ["TeeOutput", "TeeLoggerFactory", "ConfigurationError"]
@dataclass
class TeeOutput:
"""
A specification of a destination that a TeeLoggerFactory should send
log events to. A chain of destination-specific processors can be
applied before the log event ultimately makes it way to a logger created
from the underlying factory.
"""
logger_factory: Callable[..., structlog.types.WrappedLogger]
processors: Sequence[structlog.types.Processor] = field(default_factory=list)
def _construct_bound_logger(self, *args: Any) -> structlog.BoundLogger:
return cast(
structlog.BoundLogger,
structlog.wrap_logger(
logger=self.logger_factory(*args),
processors=self.processors,
),
)
class TeeLogger:
def __init__(self, destinations: List[structlog.BoundLogger]):
self.destinations = destinations
def __getattr__(self, method_name: str) -> Any:
def f(*args_from_renderer: Any, **event_dict: Any) -> None:
if len(args_from_renderer):
raise ConfigurationError(
"TeeLoggerFactory cannot operate on events that have "
"already been processed by a Renderer. Please place "
"the Renderer in the per-output processor lists instead "
"of the top-level list passed to structlog.configure."
)
for destination in self.destinations:
getattr(destination, method_name)(**event_dict.copy())
return f
_ensure_logger_implements_protocol: Type[structlog.types.WrappedLogger] = TeeLogger
class TeeLoggerFactory:
"""
A logger factory that duplicates events to multiple destinations.
For example, the following code sets up structlog to send events to both
the console and a JSON file:
import structlog, structlog_overtime, sys
structlog.configure(
# The default list of processors contains a ConsoleRenderer, but
# TeeLoggerFactory cannot operate on rendered events so we must
# remove it.
processors=[
structlog.processors.StackInfoRenderer(),
structlog.dev.set_exc_info,
structlog.processors.format_exc_info,
structlog.processors.TimeStamper(),
],
logger_factory=structlog_overtime.TeeLoggerFactory(
structlog_overtime.TeeOutput(
processors=[structlog.dev.ConsoleRenderer(colors=sys.stderr.isatty())],
logger_factory=structlog.PrintLoggerFactory(sys.stderr),
),
structlog_overtime.TeeOutput(
processors=[structlog.processors.JSONRenderer()],
logger_factory=structlog.PrintLoggerFactory(open("test.log", "a")),
),
),
)
"""
def __init__(self, *outputs: TeeOutput):
self.outputs = outputs
def __call__(self, *args: Any) -> TeeLogger:
return TeeLogger(
[output._construct_bound_logger(*args) for output in self.outputs]
)
_ensure_logger_factory_implements_protocol: Type[
Callable[..., structlog.types.WrappedLogger]
] = TeeLoggerFactory
class ConfigurationError(Exception):
pass