|
32 | 32 | from __future__ import absolute_import |
33 | 33 |
|
34 | 34 | import logging |
35 | | -import sys |
36 | | -import traceback |
37 | | -import warnings |
38 | 35 |
|
39 | 36 | import wrapt |
40 | 37 |
|
41 | 38 | from elasticapm import get_client |
42 | | -from elasticapm.base import Client |
43 | 39 | from elasticapm.traces import execution_context |
44 | | -from elasticapm.utils.stacks import iter_stack_frames |
45 | | - |
46 | | - |
47 | | -class LoggingHandler(logging.Handler): |
48 | | - def __init__(self, *args, **kwargs) -> None: |
49 | | - warnings.warn( |
50 | | - "The LoggingHandler is deprecated and will be removed in v7.0 of " |
51 | | - "the agent. Please use `log_ecs_reformatting` and ship the logs " |
52 | | - "with Elastic Agent or Filebeat instead. " |
53 | | - "https://www.elastic.co/guide/en/apm/agent/python/current/logs.html", |
54 | | - DeprecationWarning, |
55 | | - ) |
56 | | - self.client = None |
57 | | - if "client" in kwargs: |
58 | | - self.client = kwargs.pop("client") |
59 | | - elif len(args) > 0: |
60 | | - arg = args[0] |
61 | | - if isinstance(arg, Client): |
62 | | - self.client = arg |
63 | | - |
64 | | - if not self.client: |
65 | | - client_cls = kwargs.pop("client_cls", None) |
66 | | - if client_cls: |
67 | | - self.client = client_cls(*args, **kwargs) |
68 | | - else: |
69 | | - warnings.warn( |
70 | | - "LoggingHandler requires a Client instance. No Client was received.", |
71 | | - DeprecationWarning, |
72 | | - ) |
73 | | - self.client = Client(*args, **kwargs) |
74 | | - logging.Handler.__init__(self, level=kwargs.get("level", logging.NOTSET)) |
75 | | - |
76 | | - def emit(self, record): |
77 | | - self.format(record) |
78 | | - |
79 | | - # Avoid typical config issues by overriding loggers behavior |
80 | | - if record.name.startswith(("elasticapm.errors",)): |
81 | | - sys.stderr.write(record.getMessage() + "\n") |
82 | | - return |
83 | | - |
84 | | - try: |
85 | | - return self._emit(record) |
86 | | - except Exception: |
87 | | - sys.stderr.write("Top level ElasticAPM exception caught - failed creating log record.\n") |
88 | | - sys.stderr.write(record.getMessage() + "\n") |
89 | | - sys.stderr.write(traceback.format_exc() + "\n") |
90 | | - |
91 | | - try: |
92 | | - self.client.capture("Exception") |
93 | | - except Exception: |
94 | | - pass |
95 | | - |
96 | | - def _emit(self, record, **kwargs): |
97 | | - data = {} |
98 | | - |
99 | | - for k, v in record.__dict__.items(): |
100 | | - if "." not in k and k not in ("culprit",): |
101 | | - continue |
102 | | - data[k] = v |
103 | | - |
104 | | - stack = getattr(record, "stack", None) |
105 | | - if stack is True: |
106 | | - stack = iter_stack_frames(config=self.client.config) |
107 | | - |
108 | | - if stack: |
109 | | - frames = [] |
110 | | - started = False |
111 | | - last_mod = "" |
112 | | - for item in stack: |
113 | | - if isinstance(item, (list, tuple)): |
114 | | - frame, lineno = item |
115 | | - else: |
116 | | - frame, lineno = item, item.f_lineno |
117 | | - |
118 | | - if not started: |
119 | | - f_globals = getattr(frame, "f_globals", {}) |
120 | | - module_name = f_globals.get("__name__", "") |
121 | | - if last_mod.startswith("logging") and not module_name.startswith("logging"): |
122 | | - started = True |
123 | | - else: |
124 | | - last_mod = module_name |
125 | | - continue |
126 | | - frames.append((frame, lineno)) |
127 | | - stack = frames |
128 | | - |
129 | | - custom = getattr(record, "data", {}) |
130 | | - # Add in all of the data from the record that we aren't already capturing |
131 | | - for k in record.__dict__.keys(): |
132 | | - if k in ( |
133 | | - "stack", |
134 | | - "name", |
135 | | - "args", |
136 | | - "msg", |
137 | | - "levelno", |
138 | | - "exc_text", |
139 | | - "exc_info", |
140 | | - "data", |
141 | | - "created", |
142 | | - "levelname", |
143 | | - "msecs", |
144 | | - "relativeCreated", |
145 | | - ): |
146 | | - continue |
147 | | - if k.startswith("_"): |
148 | | - continue |
149 | | - custom[k] = record.__dict__[k] |
150 | | - |
151 | | - # If there's no exception being processed, |
152 | | - # exc_info may be a 3-tuple of None |
153 | | - # http://docs.python.org/library/sys.html#sys.exc_info |
154 | | - if record.exc_info and all(record.exc_info): |
155 | | - handler = self.client.get_handler("elasticapm.events.Exception") |
156 | | - exception = handler.capture(self.client, exc_info=record.exc_info) |
157 | | - else: |
158 | | - exception = None |
159 | | - |
160 | | - return self.client.capture( |
161 | | - "Message", |
162 | | - param_message={"message": str(record.msg), "params": record.args}, |
163 | | - stack=stack, |
164 | | - custom=custom, |
165 | | - exception=exception, |
166 | | - level=record.levelno, |
167 | | - logger_name=record.name, |
168 | | - **kwargs, |
169 | | - ) |
170 | | - |
171 | | - |
172 | | -class LoggingFilter(logging.Filter): |
173 | | - """ |
174 | | - This filter doesn't actually do any "filtering" -- rather, it just adds |
175 | | - three new attributes to any "filtered" LogRecord objects: |
176 | | -
|
177 | | - * elasticapm_transaction_id |
178 | | - * elasticapm_trace_id |
179 | | - * elasticapm_span_id |
180 | | - * elasticapm_service_name |
181 | | -
|
182 | | - These attributes can then be incorporated into your handlers and formatters, |
183 | | - so that you can tie log messages to transactions in elasticsearch. |
184 | | -
|
185 | | - This filter also adds these fields to a dictionary attribute, |
186 | | - `elasticapm_labels`, using the official tracing fields names as documented |
187 | | - here: https://www.elastic.co/guide/en/ecs/current/ecs-tracing.html |
188 | | -
|
189 | | - Note that if you're using Python 3.2+, by default we will add a |
190 | | - LogRecordFactory to your root logger which will add these attributes |
191 | | - automatically. |
192 | | - """ |
193 | | - |
194 | | - def __init__(self, name=""): |
195 | | - super().__init__(name=name) |
196 | | - warnings.warn( |
197 | | - "The LoggingFilter is deprecated and will be removed in v7.0 of " |
198 | | - "the agent. On Python 3.2+, by default we add a LogRecordFactory to " |
199 | | - "your root logger automatically" |
200 | | - "https://www.elastic.co/guide/en/apm/agent/python/current/logs.html", |
201 | | - DeprecationWarning, |
202 | | - ) |
203 | | - |
204 | | - def filter(self, record): |
205 | | - """ |
206 | | - Add elasticapm attributes to `record`. |
207 | | - """ |
208 | | - _add_attributes_to_log_record(record) |
209 | | - return True |
210 | 40 |
|
211 | 41 |
|
212 | 42 | @wrapt.decorator |
|
0 commit comments