whatap-python 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- whatap/LICENSE +0 -0
- whatap/README.rst +49 -0
- whatap/__init__.py +923 -0
- whatap/__main__.py +4 -0
- whatap/agent/darwin/amd64/whatap_python +0 -0
- whatap/agent/darwin/arm64/whatap_python +0 -0
- whatap/agent/linux/amd64/whatap_python +0 -0
- whatap/agent/linux/arm64/whatap_python +0 -0
- whatap/agent/windows/whatap_python.exe +0 -0
- whatap/bootstrap/__init__.py +0 -0
- whatap/bootstrap/sitecustomize.py +19 -0
- whatap/build.py +4 -0
- whatap/conf/__init__.py +0 -0
- whatap/conf/configuration.py +280 -0
- whatap/conf/configure.py +105 -0
- whatap/conf/license.py +49 -0
- whatap/control/__init__.py +0 -0
- whatap/counter/__init__.py +14 -0
- whatap/counter/counter_manager.py +45 -0
- whatap/counter/tasks/__init__.py +3 -0
- whatap/counter/tasks/base_task.py +26 -0
- whatap/counter/tasks/llm_evaluator_task.py +501 -0
- whatap/counter/tasks/llm_log_sink_task.py +309 -0
- whatap/counter/tasks/llm_stat_task.py +78 -0
- whatap/counter/tasks/openfiledescriptor.py +67 -0
- whatap/io/__init__.py +1 -0
- whatap/io/data_inputx.py +161 -0
- whatap/io/data_outputx.py +262 -0
- whatap/llm/__init__.py +17 -0
- whatap/llm/definitions.py +43 -0
- whatap/llm/evaluators/__init__.py +136 -0
- whatap/llm/evaluators/base.py +114 -0
- whatap/llm/evaluators/builtins/__init__.py +91 -0
- whatap/llm/evaluators/builtins/answer_relevance.py +46 -0
- whatap/llm/evaluators/builtins/combined_judge.py +271 -0
- whatap/llm/evaluators/builtins/factuality.py +71 -0
- whatap/llm/evaluators/builtins/hallucination.py +97 -0
- whatap/llm/evaluators/builtins/llm_judge.py +516 -0
- whatap/llm/evaluators/builtins/pii_leak.py +214 -0
- whatap/llm/evaluators/builtins/prompt_injection.py +71 -0
- whatap/llm/evaluators/builtins/toxicity.py +53 -0
- whatap/llm/evaluators/builtins/url_scan.py +194 -0
- whatap/llm/evaluators/registry.py +192 -0
- whatap/llm/evaluators/sampler.py +83 -0
- whatap/llm/evaluators/scope.py +334 -0
- whatap/llm/features.py +66 -0
- whatap/llm/log_sink_packs/__init__.py +9 -0
- whatap/llm/log_sink_packs/llm_input_message.py +16 -0
- whatap/llm/log_sink_packs/llm_log_sink_pack.py +72 -0
- whatap/llm/log_sink_packs/llm_output_message.py +19 -0
- whatap/llm/log_sink_packs/llm_step_eval_status.py +94 -0
- whatap/llm/log_sink_packs/llm_step_status.py +118 -0
- whatap/llm/log_sink_packs/llm_system_message.py +16 -0
- whatap/llm/log_sink_packs/llm_tool_calls.py +44 -0
- whatap/llm/log_sink_packs/llm_tool_results.py +16 -0
- whatap/llm/log_sink_packs/llm_tx_status.py +108 -0
- whatap/llm/pricing.py +236 -0
- whatap/llm/prompt_meta.py +288 -0
- whatap/llm/providers/__init__.py +0 -0
- whatap/llm/providers/anthropic/__init__.py +37 -0
- whatap/llm/providers/anthropic/messages/__init__.py +0 -0
- whatap/llm/providers/anthropic/messages/messages.py +70 -0
- whatap/llm/providers/anthropic/messages/messages_context.py +76 -0
- whatap/llm/providers/anthropic/messages/messages_extractor.py +126 -0
- whatap/llm/providers/interceptor.py +182 -0
- whatap/llm/providers/openai/__init__.py +133 -0
- whatap/llm/providers/openai/chat/__init__.py +0 -0
- whatap/llm/providers/openai/chat/chat.py +82 -0
- whatap/llm/providers/openai/chat/chat_context.py +78 -0
- whatap/llm/providers/openai/chat/chat_extractor.py +127 -0
- whatap/llm/providers/openai/completions/__init__.py +0 -0
- whatap/llm/providers/openai/completions/completions.py +70 -0
- whatap/llm/providers/openai/completions/completions_context.py +31 -0
- whatap/llm/providers/openai/completions/completions_extractor.py +61 -0
- whatap/llm/providers/openai/content_parser.py +41 -0
- whatap/llm/providers/openai/embeddings/__init__.py +0 -0
- whatap/llm/providers/openai/embeddings/embeddings.py +59 -0
- whatap/llm/providers/openai/embeddings/embeddings_context.py +25 -0
- whatap/llm/providers/openai/embeddings/embeddings_extractor.py +26 -0
- whatap/llm/providers/openai/responses/__init__.py +0 -0
- whatap/llm/providers/openai/responses/responses.py +70 -0
- whatap/llm/providers/openai/responses/responses_context.py +88 -0
- whatap/llm/providers/openai/responses/responses_extractor.py +126 -0
- whatap/llm/providers/stream_accumulator.py +73 -0
- whatap/llm/stats/__init__.py +35 -0
- whatap/llm/stats/active_stat.py +86 -0
- whatap/llm/stats/answer_relevance_eval_stat.py +10 -0
- whatap/llm/stats/api_status_stat.py +35 -0
- whatap/llm/stats/base_stat.py +107 -0
- whatap/llm/stats/combined_judge_eval_stat.py +11 -0
- whatap/llm/stats/error_stat.py +59 -0
- whatap/llm/stats/eval_stat.py +225 -0
- whatap/llm/stats/factuality_eval_stat.py +10 -0
- whatap/llm/stats/feature_stat.py +104 -0
- whatap/llm/stats/finish_stat.py +105 -0
- whatap/llm/stats/hallucination_eval_stat.py +10 -0
- whatap/llm/stats/meter.py +18 -0
- whatap/llm/stats/perf_stat.py +117 -0
- whatap/llm/stats/pii_leak_eval_stat.py +12 -0
- whatap/llm/stats/prompt_injection_eval_stat.py +10 -0
- whatap/llm/stats/token_usage_stat.py +133 -0
- whatap/llm/stats/toxicity_eval_stat.py +10 -0
- whatap/llm/stats/url_scan_eval_stat.py +12 -0
- whatap/net/__init__.py +0 -0
- whatap/net/async_sender.py +107 -0
- whatap/net/packet_enum.py +44 -0
- whatap/net/packet_type_enum.py +31 -0
- whatap/net/param_def.py +69 -0
- whatap/net/stackhelper.py +87 -0
- whatap/net/udp_session.py +394 -0
- whatap/net/udp_thread.py +54 -0
- whatap/pack/__init__.py +0 -0
- whatap/pack/logSinkPack.py +77 -0
- whatap/pack/pack.py +34 -0
- whatap/pack/pack_enum.py +41 -0
- whatap/pack/tagCountPack.py +61 -0
- whatap/scripts/__init__.py +208 -0
- whatap/trace/__init__.py +12 -0
- whatap/trace/mod/__init__.py +0 -0
- whatap/trace/mod/amqp/__init__.py +0 -0
- whatap/trace/mod/amqp/kombu.py +122 -0
- whatap/trace/mod/amqp/pika.py +62 -0
- whatap/trace/mod/application/__init__.py +0 -0
- whatap/trace/mod/application/bottle.py +34 -0
- whatap/trace/mod/application/celery.py +81 -0
- whatap/trace/mod/application/cherrypy.py +30 -0
- whatap/trace/mod/application/django.py +287 -0
- whatap/trace/mod/application/django_asgi.py +266 -0
- whatap/trace/mod/application/django_py3.py +251 -0
- whatap/trace/mod/application/fastapi/__init__.py +31 -0
- whatap/trace/mod/application/fastapi/endpoint.py +73 -0
- whatap/trace/mod/application/fastapi/exception_log.py +63 -0
- whatap/trace/mod/application/fastapi/instrumentation.py +204 -0
- whatap/trace/mod/application/fastapi/scope.py +115 -0
- whatap/trace/mod/application/fastapi/transaction.py +67 -0
- whatap/trace/mod/application/flask.py +52 -0
- whatap/trace/mod/application/frappe.py +224 -0
- whatap/trace/mod/application/graphql.py +170 -0
- whatap/trace/mod/application/nameko.py +39 -0
- whatap/trace/mod/application/odoo.py +63 -0
- whatap/trace/mod/application/starlette.py +126 -0
- whatap/trace/mod/application/tornado.py +163 -0
- whatap/trace/mod/application/wsgi.py +195 -0
- whatap/trace/mod/database/__init__.py +0 -0
- whatap/trace/mod/database/cxoracle.py +49 -0
- whatap/trace/mod/database/mongo.py +169 -0
- whatap/trace/mod/database/mysql.py +80 -0
- whatap/trace/mod/database/neo4j.py +90 -0
- whatap/trace/mod/database/psycopg2.py +45 -0
- whatap/trace/mod/database/psycopg3.py +359 -0
- whatap/trace/mod/database/redis.py +122 -0
- whatap/trace/mod/database/sqlalchemy.py +213 -0
- whatap/trace/mod/database/sqlite3.py +130 -0
- whatap/trace/mod/database/util.py +630 -0
- whatap/trace/mod/email/__init__.py +0 -0
- whatap/trace/mod/email/smtp.py +78 -0
- whatap/trace/mod/httpc/__init__.py +0 -0
- whatap/trace/mod/httpc/django.py +31 -0
- whatap/trace/mod/httpc/httplib.py +70 -0
- whatap/trace/mod/httpc/httpx.py +62 -0
- whatap/trace/mod/httpc/requests.py +20 -0
- whatap/trace/mod/httpc/urllib3.py +27 -0
- whatap/trace/mod/httpc/util.py +388 -0
- whatap/trace/mod/logging.py +161 -0
- whatap/trace/mod/plugin.py +84 -0
- whatap/trace/mod/standalone/__init__.py +0 -0
- whatap/trace/mod/standalone/multiple.py +293 -0
- whatap/trace/mod/standalone/single.py +135 -0
- whatap/trace/simple_trace_context.py +18 -0
- whatap/trace/trace_context.py +212 -0
- whatap/trace/trace_context_manager.py +244 -0
- whatap/trace/trace_error.py +84 -0
- whatap/trace/trace_handler.py +89 -0
- whatap/trace/trace_import.py +91 -0
- whatap/trace/trace_module_definition.py +156 -0
- whatap/util/__init__.py +0 -0
- whatap/util/bit_util.py +49 -0
- whatap/util/cardinality/__init__.py +0 -0
- whatap/util/cardinality/hyperloglog.py +84 -0
- whatap/util/cardinality/murmurhash.py +20 -0
- whatap/util/cardinality/registerset.py +60 -0
- whatap/util/compare_util.py +19 -0
- whatap/util/date_util.py +55 -0
- whatap/util/debug_util.py +73 -0
- whatap/util/escape_literal_sql.py +233 -0
- whatap/util/frame_util.py +20 -0
- whatap/util/hash_util.py +103 -0
- whatap/util/hexa32.py +66 -0
- whatap/util/int_set.py +199 -0
- whatap/util/ip_util.py +63 -0
- whatap/util/keygen.py +11 -0
- whatap/util/linked_list.py +113 -0
- whatap/util/linked_map.py +359 -0
- whatap/util/metering_util.py +103 -0
- whatap/util/request_double_queue.py +68 -0
- whatap/util/request_queue.py +60 -0
- whatap/util/string_util.py +20 -0
- whatap/util/throttle_util.py +99 -0
- whatap/util/userid_util.py +134 -0
- whatap/value/__init__.py +1 -0
- whatap/value/blob_value.py +38 -0
- whatap/value/boolean_value.py +33 -0
- whatap/value/decimal_value.py +36 -0
- whatap/value/double_summary.py +86 -0
- whatap/value/double_value.py +33 -0
- whatap/value/float_array.py +42 -0
- whatap/value/float_value.py +34 -0
- whatap/value/int_array.py +42 -0
- whatap/value/ip4_value.py +50 -0
- whatap/value/list_value.py +105 -0
- whatap/value/long_array.py +44 -0
- whatap/value/long_summary.py +83 -0
- whatap/value/map_value.py +154 -0
- whatap/value/null_value.py +21 -0
- whatap/value/number_value.py +33 -0
- whatap/value/summary_value.py +39 -0
- whatap/value/text_array.py +58 -0
- whatap/value/text_hash_value.py +37 -0
- whatap/value/text_value.py +43 -0
- whatap/value/value.py +26 -0
- whatap/value/value_enum.py +80 -0
- whatap/whatap.conf +14 -0
- whatap_python-2.1.0.dist-info/METADATA +87 -0
- whatap_python-2.1.0.dist-info/RECORD +227 -0
- whatap_python-2.1.0.dist-info/WHEEL +5 -0
- whatap_python-2.1.0.dist-info/entry_points.txt +6 -0
- whatap_python-2.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
from whatap.trace.trace_context import TraceContext
|
|
2
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
3
|
+
from whatap.util.date_util import DateUtil
|
|
4
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
5
|
+
from whatap.conf.configure import Configure as conf
|
|
6
|
+
from whatap.trace.trace_error import interceptor_step_error
|
|
7
|
+
from whatap import logging
|
|
8
|
+
import whatap.net.async_sender as async_sender
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _stringify(value):
|
|
14
|
+
if value is None:
|
|
15
|
+
return ''
|
|
16
|
+
if isinstance(value, str):
|
|
17
|
+
return value
|
|
18
|
+
if isinstance(value, (bytes, bytearray)):
|
|
19
|
+
try:
|
|
20
|
+
return bytes(value).decode('utf-8', errors='replace')
|
|
21
|
+
except Exception:
|
|
22
|
+
return str(value)
|
|
23
|
+
if hasattr(value, '__fspath__'):
|
|
24
|
+
try:
|
|
25
|
+
p = os.fspath(value)
|
|
26
|
+
if isinstance(p, (bytes, bytearray)):
|
|
27
|
+
return bytes(p).decode('utf-8', errors='replace')
|
|
28
|
+
return p
|
|
29
|
+
except Exception:
|
|
30
|
+
return str(value)
|
|
31
|
+
return str(value)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def extract_db_error_message(e):
|
|
35
|
+
try:
|
|
36
|
+
# PostgreSQL (psycopg2)
|
|
37
|
+
if hasattr(e, 'pgcode') and hasattr(e, 'pgerror'):
|
|
38
|
+
return str(e.pgerror)
|
|
39
|
+
|
|
40
|
+
# MySQL (PyMySQL, mysql-connector-python)
|
|
41
|
+
if hasattr(e, 'args') and len(e.args) > 1 and isinstance(e.args[1], str):
|
|
42
|
+
return e.args[1]
|
|
43
|
+
|
|
44
|
+
# 기본 에러 메시지
|
|
45
|
+
if hasattr(e, 'args') and len(e.args) > 0:
|
|
46
|
+
if isinstance(e.args[0], str):
|
|
47
|
+
return e.args[0]
|
|
48
|
+
return str(e.args[0])
|
|
49
|
+
|
|
50
|
+
return str(e)
|
|
51
|
+
except Exception as e:
|
|
52
|
+
logging.warning('[DB] extract_db_error_message failed: %s' % e, extra={'id': 'DB001'})
|
|
53
|
+
return "Unknown database error"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def addQuoteDict(arg_dict):
|
|
58
|
+
quoted_dict = dict()
|
|
59
|
+
|
|
60
|
+
for k, v in arg_dict.items():
|
|
61
|
+
if isinstance(v, str):
|
|
62
|
+
quoted_dict[k] = "'" + v.replace("'", "\\'") + "'"
|
|
63
|
+
else:
|
|
64
|
+
quoted_dict[k] = v
|
|
65
|
+
|
|
66
|
+
return quoted_dict
|
|
67
|
+
|
|
68
|
+
def addQuoteList(arg_list):
|
|
69
|
+
quoted_list = list()
|
|
70
|
+
|
|
71
|
+
for v in arg_list:
|
|
72
|
+
if isinstance(v, str):
|
|
73
|
+
quoted_list.append("'" + v.replace("'", "\\'") + "'")
|
|
74
|
+
else:
|
|
75
|
+
quoted_list.append("'" + str(v) + "'")
|
|
76
|
+
|
|
77
|
+
return tuple(quoted_list)
|
|
78
|
+
|
|
79
|
+
def addQuoteMany(arg_list, query_template=None):
|
|
80
|
+
"""
|
|
81
|
+
executemany용 파라미터 리스트를 쿼리 문자열로 변환
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
arg_list: [(val1, val2, val3), (val4, val5, val6), ...] 형태의 리스트
|
|
85
|
+
query_template: 쿼리 템플릿 (제공되면 개별 쿼리들 생성)
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
str: 포매팅된 문자열
|
|
89
|
+
"""
|
|
90
|
+
if not arg_list:
|
|
91
|
+
return ""
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
if query_template is None:
|
|
95
|
+
# 기존 방식: VALUES 절용 포맷
|
|
96
|
+
formatted_rows = []
|
|
97
|
+
for row in arg_list:
|
|
98
|
+
if isinstance(row, (list, tuple)):
|
|
99
|
+
quoted_values = []
|
|
100
|
+
for value in row:
|
|
101
|
+
if value is None:
|
|
102
|
+
quoted_values.append("NULL")
|
|
103
|
+
elif isinstance(value, str):
|
|
104
|
+
escaped = value.replace("'", "''")
|
|
105
|
+
quoted_values.append(f"'{escaped}'")
|
|
106
|
+
else:
|
|
107
|
+
quoted_values.append("'" + str(value) + "'")
|
|
108
|
+
|
|
109
|
+
formatted_row = "({})".format(", ".join(quoted_values))
|
|
110
|
+
formatted_rows.append(formatted_row)
|
|
111
|
+
|
|
112
|
+
result = ", ".join(formatted_rows)
|
|
113
|
+
else:
|
|
114
|
+
# 개별 쿼리 생성 방식
|
|
115
|
+
queries = []
|
|
116
|
+
for row in arg_list:
|
|
117
|
+
if isinstance(row, (list, tuple)):
|
|
118
|
+
try:
|
|
119
|
+
quoted_values = []
|
|
120
|
+
for value in row:
|
|
121
|
+
if value is None:
|
|
122
|
+
quoted_values.append("NULL")
|
|
123
|
+
elif isinstance(value, str):
|
|
124
|
+
escaped = value.replace("'", "''")
|
|
125
|
+
quoted_values.append(f"'{escaped}'")
|
|
126
|
+
else:
|
|
127
|
+
quoted_values.append(str(value))
|
|
128
|
+
|
|
129
|
+
individual_query = query_template % tuple(quoted_values)
|
|
130
|
+
queries.append(individual_query)
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logging.warning('[DB] addQuoteMany row format failed: %s' % e, extra={'id': 'DB002'})
|
|
133
|
+
queries.append(f"{query_template} [failed to format row: {row}]")
|
|
134
|
+
|
|
135
|
+
result = ";\n".join(queries)
|
|
136
|
+
|
|
137
|
+
return result
|
|
138
|
+
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logging.warning('[DB] addQuoteMany failed: %s' % e, extra={'id': 'DB003'})
|
|
141
|
+
return f"[{len(arg_list)} rows]"
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def neo4jQuery(query,paremeter):
|
|
146
|
+
neo4j_query = query
|
|
147
|
+
|
|
148
|
+
for key, value in paremeter.items():
|
|
149
|
+
placeholder = f"${key}"
|
|
150
|
+
replacement = f"'{str(value)}'"
|
|
151
|
+
neo4j_query = neo4j_query.replace(placeholder, replacement)
|
|
152
|
+
|
|
153
|
+
return neo4j_query
|
|
154
|
+
|
|
155
|
+
def sqliteQuery(query, paremeter):
|
|
156
|
+
sqlite_query = query
|
|
157
|
+
|
|
158
|
+
def quote(v):
|
|
159
|
+
if v is None: return "NULL"
|
|
160
|
+
if isinstance(v, (bytes, bytearray)): return "X'" + v.hex() + "'"
|
|
161
|
+
return "'" + str(v).replace("'", "''") + "'"
|
|
162
|
+
|
|
163
|
+
# 이름 기반: :name / @name / $name (단일 세트)
|
|
164
|
+
if isinstance(paremeter, dict):
|
|
165
|
+
for pfx in (':', '@', '$'):
|
|
166
|
+
for k, v in paremeter.items():
|
|
167
|
+
sqlite_query = sqlite_query.replace(f"{pfx}{k}", quote(v))
|
|
168
|
+
return sqlite_query
|
|
169
|
+
|
|
170
|
+
# 포지셔널 + executemany 분기
|
|
171
|
+
if isinstance(paremeter, (list, tuple)):
|
|
172
|
+
# ── executemany: [tuple|dict, ...] ──
|
|
173
|
+
if isinstance(paremeter, list) and paremeter and isinstance(paremeter[0], (tuple, dict)):
|
|
174
|
+
stmts = []
|
|
175
|
+
for params in paremeter:
|
|
176
|
+
sqlite_query = query
|
|
177
|
+
if isinstance(params, dict):
|
|
178
|
+
for pfx in (':', '@', '$'):
|
|
179
|
+
for k, v in params.items():
|
|
180
|
+
sqlite_query = sqlite_query.replace(f"{pfx}{k}", quote(v))
|
|
181
|
+
else: # tuple
|
|
182
|
+
for i, v in enumerate(params, 1):
|
|
183
|
+
sqlite_query = sqlite_query.replace(f"?{i}", quote(v))
|
|
184
|
+
for v in params:
|
|
185
|
+
sqlite_query = sqlite_query.replace('?', quote(v), 1)
|
|
186
|
+
stmts.append(sqlite_query)
|
|
187
|
+
return ";\n".join(stmts)
|
|
188
|
+
|
|
189
|
+
# ── 단일 포지셔널 세트 (tuple 또는 스칼라 list) ──
|
|
190
|
+
seq = paremeter if isinstance(paremeter, tuple) else tuple(paremeter)
|
|
191
|
+
for i, v in enumerate(seq, 1):
|
|
192
|
+
sqlite_query = sqlite_query.replace(f"?{i}", quote(v))
|
|
193
|
+
for v in seq:
|
|
194
|
+
sqlite_query = sqlite_query.replace('?', quote(v), 1)
|
|
195
|
+
return sqlite_query
|
|
196
|
+
|
|
197
|
+
return sqlite_query
|
|
198
|
+
|
|
199
|
+
def interceptor_db_con(fn, db_info, *args, **kwargs):
|
|
200
|
+
ctx = TraceContextManager.getLocalContext()
|
|
201
|
+
if not ctx:
|
|
202
|
+
return fn(*args, **kwargs)
|
|
203
|
+
|
|
204
|
+
start_time = DateUtil.nowSystem()
|
|
205
|
+
ctx.start_time = start_time
|
|
206
|
+
|
|
207
|
+
ctx.db_opening = True
|
|
208
|
+
try:
|
|
209
|
+
callback = fn(*args, **kwargs)
|
|
210
|
+
finally:
|
|
211
|
+
ctx.db_opening = False
|
|
212
|
+
|
|
213
|
+
if not kwargs:
|
|
214
|
+
kwargs = dict(
|
|
215
|
+
x.split('=') for x in re.sub(r'\s*=\s*', '=', args[0]).split())
|
|
216
|
+
|
|
217
|
+
db_type = db_info.get('type')
|
|
218
|
+
|
|
219
|
+
if db_type == "sqlite":
|
|
220
|
+
text = "sqlite:"
|
|
221
|
+
|
|
222
|
+
elif db_type == "neo4j":
|
|
223
|
+
text = "neo4j"
|
|
224
|
+
text += "@"
|
|
225
|
+
text += db_info.get('uri','')
|
|
226
|
+
text += "/"
|
|
227
|
+
text += db_info.get('user', '')
|
|
228
|
+
|
|
229
|
+
#psycopg3 에서 db_str을 통해 하나의 문자열로 전부 데이터가 오는 경우
|
|
230
|
+
elif db_info.get('db_con_stc', '') == 'completed':
|
|
231
|
+
text = db_info.get('db_str', '')
|
|
232
|
+
|
|
233
|
+
else:
|
|
234
|
+
text = '{}://'.format(db_type)
|
|
235
|
+
text += kwargs.get('user', '')
|
|
236
|
+
text += "@"
|
|
237
|
+
text += kwargs.get('host', kwargs.get('dsn', ''))
|
|
238
|
+
text += '/'
|
|
239
|
+
|
|
240
|
+
text += _stringify(kwargs.get('database', kwargs.get('db', kwargs.get('dbname', ''))))
|
|
241
|
+
ctx.active_dbc = text
|
|
242
|
+
ctx.lctx['dbc'] = text
|
|
243
|
+
|
|
244
|
+
ctx.active_dbc = 0
|
|
245
|
+
|
|
246
|
+
datas = [text]
|
|
247
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
248
|
+
async_sender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, datas)
|
|
249
|
+
|
|
250
|
+
return callback
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _build_dbc_from_db_info(db_info):
|
|
254
|
+
db_type = db_info.get('type', 'db')
|
|
255
|
+
if db_type == "sqlite":
|
|
256
|
+
return "sqlite:"
|
|
257
|
+
elif db_type == "neo4j":
|
|
258
|
+
return "neo4j@{}/{}".format(db_info.get('uri', ''), db_info.get('user', ''))
|
|
259
|
+
elif db_info.get('db_con_stc', '') == 'completed':
|
|
260
|
+
return db_info.get('db_str', '')
|
|
261
|
+
else:
|
|
262
|
+
return "{}://{}@{}/{}".format(
|
|
263
|
+
db_type,
|
|
264
|
+
db_info.get('user', ''),
|
|
265
|
+
db_info.get('host', db_info.get('dsn', '')),
|
|
266
|
+
db_info.get('database', db_info.get('db', db_info.get('dbname', '')))
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def interceptor_db_execute(fn, db_info, *args, **kwargs):
|
|
271
|
+
ctx = TraceContextManager.getLocalContext()
|
|
272
|
+
if not ctx:
|
|
273
|
+
return fn(*args, **kwargs)
|
|
274
|
+
# sendDebugProfile(ctx, 'interceptor_db_execute step -1')
|
|
275
|
+
if not ctx.lctx.get('dbc') and db_info:
|
|
276
|
+
ctx.lctx['dbc'] = _build_dbc_from_db_info(db_info)
|
|
277
|
+
self = args[0]
|
|
278
|
+
db_type = db_info.get('type')
|
|
279
|
+
query = None
|
|
280
|
+
if db_type == "neo4j":
|
|
281
|
+
try:
|
|
282
|
+
query = neo4jQuery(args[1], kwargs)
|
|
283
|
+
except Exception as e:
|
|
284
|
+
logging.warning('[DB] neo4jQuery failed: %s' % e, extra={'id': 'DB004'})
|
|
285
|
+
|
|
286
|
+
elif db_type == "sqlite" and len(args) > 2:
|
|
287
|
+
try:
|
|
288
|
+
query = sqliteQuery(args[1],args[2])
|
|
289
|
+
except Exception as e:
|
|
290
|
+
logging.warning('[DB] sqliteQuery failed: %s' % e, extra={'id': 'DB005'})
|
|
291
|
+
|
|
292
|
+
else:
|
|
293
|
+
if (len(args) > 2 and
|
|
294
|
+
isinstance(args[2], (list, tuple)) and
|
|
295
|
+
len(args[2]) > 0 and
|
|
296
|
+
isinstance(args[2][0], (list, tuple))):
|
|
297
|
+
try:
|
|
298
|
+
query_str = args[1]
|
|
299
|
+
if isinstance(query_str, bytes):
|
|
300
|
+
query_str = query_str.decode()
|
|
301
|
+
|
|
302
|
+
query_upper = query_str.upper()
|
|
303
|
+
|
|
304
|
+
if 'VALUES' in query_upper:
|
|
305
|
+
# INSERT 처리
|
|
306
|
+
values_pos = query_upper.find('VALUES')
|
|
307
|
+
before_values = query_str[:values_pos + 6]
|
|
308
|
+
query = f"{before_values} {addQuoteMany(args[2])}"
|
|
309
|
+
else:
|
|
310
|
+
# UPDATE, DELETE 등 처리 - 개별 쿼리로 표시
|
|
311
|
+
query = addQuoteMany(args[2], query_str)
|
|
312
|
+
except Exception as e:
|
|
313
|
+
logging.warning('[DB] executemany query format failed: %s' % e, extra={'id': 'DB006'})
|
|
314
|
+
|
|
315
|
+
elif len(args) > 2 and type(args[2]) == dict and args[2]:
|
|
316
|
+
try:
|
|
317
|
+
query = args[1] % addQuoteDict(args[2])
|
|
318
|
+
except Exception as e:
|
|
319
|
+
logging.warning('[DB] addQuoteDict failed: %s' % e, extra={'id': 'DB007'})
|
|
320
|
+
elif len(args) > 2 and type(args[2]) in (list, tuple) and args[2]:
|
|
321
|
+
try:
|
|
322
|
+
query = args[1] % addQuoteList(args[2])
|
|
323
|
+
except Exception as e:
|
|
324
|
+
logging.warning('[DB] addQuoteList failed: %s' % e, extra={'id': 'DB008'})
|
|
325
|
+
try:
|
|
326
|
+
if not query:
|
|
327
|
+
raw_query = args[1]
|
|
328
|
+
if isinstance(raw_query, (bytes, bytearray)):
|
|
329
|
+
query = bytes(raw_query).decode('utf-8', errors='replace')
|
|
330
|
+
else:
|
|
331
|
+
query = raw_query
|
|
332
|
+
except Exception as e:
|
|
333
|
+
logging.warning('[DB] query decode failed: %s' % e, extra={'id': 'DB009'})
|
|
334
|
+
query = args[1]
|
|
335
|
+
|
|
336
|
+
if not query:
|
|
337
|
+
return fn(*args, **kwargs)
|
|
338
|
+
|
|
339
|
+
start_time = DateUtil.nowSystem()
|
|
340
|
+
ctx.start_time = start_time
|
|
341
|
+
ctx.active_sqlhash = query
|
|
342
|
+
try:
|
|
343
|
+
callback = fn(*args, **kwargs)
|
|
344
|
+
return callback
|
|
345
|
+
except Exception as e:
|
|
346
|
+
interceptor_step_error(e)
|
|
347
|
+
finally:
|
|
348
|
+
try:
|
|
349
|
+
if hasattr(args[0], 'rowcount'):
|
|
350
|
+
count = args[0].rowcount
|
|
351
|
+
else:
|
|
352
|
+
count = -1
|
|
353
|
+
except AttributeError:
|
|
354
|
+
count = -1
|
|
355
|
+
|
|
356
|
+
datas = [ctx.lctx.get('dbc', ''), query, str(count)]
|
|
357
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
358
|
+
async_sender.send_packet(PacketTypeEnum.TX_SQL, ctx, datas)
|
|
359
|
+
|
|
360
|
+
if (count is not None) and (count > -1):
|
|
361
|
+
desc = '{0}: {1}'.format('Fetch count', count)
|
|
362
|
+
datas = [' ', ' ', desc]
|
|
363
|
+
ctx.elapsed = 0
|
|
364
|
+
async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
|
|
365
|
+
|
|
366
|
+
ctx.active_sqlhash = 0
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def interceptor_db_close(fn, *args, **kwargs):
|
|
370
|
+
ctx = TraceContextManager.getLocalContext()
|
|
371
|
+
ctx.db_opening = False
|
|
372
|
+
|
|
373
|
+
if not conf.profile_dbc_close:
|
|
374
|
+
try:
|
|
375
|
+
return fn(*args, **kwargs)
|
|
376
|
+
except Exception as e:
|
|
377
|
+
interceptor_step_error(e)
|
|
378
|
+
finally:
|
|
379
|
+
return
|
|
380
|
+
start_time = DateUtil.nowSystem()
|
|
381
|
+
ctx.start_time = start_time
|
|
382
|
+
|
|
383
|
+
try:
|
|
384
|
+
callback = fn(*args, **kwargs)
|
|
385
|
+
return callback
|
|
386
|
+
except Exception as e:
|
|
387
|
+
interceptor_step_error(e)
|
|
388
|
+
finally:
|
|
389
|
+
text = 'DB: Close Connection.'
|
|
390
|
+
datas = [' ', ' ', text]
|
|
391
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
392
|
+
async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
async def async_interceptor_db_con(fn, db_info, *args, **kwargs):
|
|
396
|
+
ctx = TraceContextManager.getLocalContext()
|
|
397
|
+
if not ctx:
|
|
398
|
+
return await fn(*args, **kwargs)
|
|
399
|
+
|
|
400
|
+
start_time = DateUtil.nowSystem()
|
|
401
|
+
ctx.start_time = start_time
|
|
402
|
+
|
|
403
|
+
ctx.db_opening = True
|
|
404
|
+
try:
|
|
405
|
+
callback = await fn(*args, **kwargs)
|
|
406
|
+
finally:
|
|
407
|
+
ctx.db_opening = False
|
|
408
|
+
|
|
409
|
+
if not kwargs:
|
|
410
|
+
kwargs = dict(
|
|
411
|
+
x.split('=') for x in re.sub(r'\s*=\s*', '=', args[0]).split())
|
|
412
|
+
|
|
413
|
+
db_type = db_info.get('type')
|
|
414
|
+
|
|
415
|
+
if db_type == "sqlite":
|
|
416
|
+
text = "sqlite:"
|
|
417
|
+
|
|
418
|
+
elif db_type == "postgresql":
|
|
419
|
+
if db_info.get('db_con_stc', '') == 'completed':
|
|
420
|
+
text = db_info.get('db_str', '')
|
|
421
|
+
else:
|
|
422
|
+
text = '{}://'.format(db_type)
|
|
423
|
+
text += kwargs.get('user', '')
|
|
424
|
+
text += "@"
|
|
425
|
+
text += kwargs.get('host', kwargs.get('dsn', ''))
|
|
426
|
+
text += '/'
|
|
427
|
+
|
|
428
|
+
text += _stringify(kwargs.get('database', kwargs.get('db', kwargs.get('dbname', ''))))
|
|
429
|
+
ctx.active_dbc = text
|
|
430
|
+
ctx.lctx['dbc'] = text
|
|
431
|
+
|
|
432
|
+
ctx.active_dbc = 0
|
|
433
|
+
|
|
434
|
+
datas = [text]
|
|
435
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
436
|
+
async_sender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, datas)
|
|
437
|
+
|
|
438
|
+
return callback
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
async def async_interceptor_db_execute(fn, db_info, *args, **kwargs):
|
|
442
|
+
ctx = TraceContextManager.getLocalContext()
|
|
443
|
+
if not ctx:
|
|
444
|
+
return await fn(*args, **kwargs)
|
|
445
|
+
if not ctx.lctx.get('dbc') and db_info:
|
|
446
|
+
ctx.lctx['dbc'] = _build_dbc_from_db_info(db_info)
|
|
447
|
+
self = args[0]
|
|
448
|
+
db_type = db_info.get('type')
|
|
449
|
+
query = None
|
|
450
|
+
if db_type == "neo4j":
|
|
451
|
+
try:
|
|
452
|
+
query = neo4jQuery(args[1], kwargs)
|
|
453
|
+
except Exception as e:
|
|
454
|
+
logging.warning('[DB] neo4jQuery async failed: %s' % e, extra={'id': 'DB004'})
|
|
455
|
+
|
|
456
|
+
elif db_type == "sqlite" and len(args) > 2:
|
|
457
|
+
try:
|
|
458
|
+
query = sqliteQuery(args[1], args[2])
|
|
459
|
+
except Exception as e:
|
|
460
|
+
logging.warning('[DB] sqliteQuery async failed: %s' % e, extra={'id': 'DB005'})
|
|
461
|
+
|
|
462
|
+
else:
|
|
463
|
+
if (len(args) > 2 and
|
|
464
|
+
isinstance(args[2], (list, tuple)) and
|
|
465
|
+
len(args[2]) > 0 and
|
|
466
|
+
isinstance(args[2][0], (list, tuple))):
|
|
467
|
+
try:
|
|
468
|
+
# VALUES 절을 찾아서 전체를 교체
|
|
469
|
+
query_str = args[1]
|
|
470
|
+
if isinstance(query_str, bytes):
|
|
471
|
+
query_str = query_str.decode()
|
|
472
|
+
|
|
473
|
+
# VALUES (...) 부분을 찾아서 교체
|
|
474
|
+
query_upper = query_str.upper()
|
|
475
|
+
if 'VALUES' in query_upper:
|
|
476
|
+
values_pos = query_upper.find('VALUES')
|
|
477
|
+
before_values = query_str[:values_pos + 6] # "VALUES" 까지
|
|
478
|
+
query = f"{before_values} {addQuoteMany(args[2])}"
|
|
479
|
+
else:
|
|
480
|
+
# VALUES가 없으면 단순 치환 시도
|
|
481
|
+
query = query_str.replace('%s', addQuoteMany(args[2]), 1)
|
|
482
|
+
except Exception as e:
|
|
483
|
+
logging.warning('[DB] executemany query format async failed: %s' % e, extra={'id': 'DB006'})
|
|
484
|
+
|
|
485
|
+
elif len(args) > 2 and type(args[2]) == dict and args[2]:
|
|
486
|
+
try:
|
|
487
|
+
query = args[1] % addQuoteDict(args[2])
|
|
488
|
+
except Exception as e:
|
|
489
|
+
logging.warning('[DB] addQuoteDict async failed: %s' % e, extra={'id': 'DB007'})
|
|
490
|
+
elif len(args) > 2 and type(args[2]) in (list, tuple) and args[2]:
|
|
491
|
+
try:
|
|
492
|
+
query = args[1] % addQuoteList(args[2])
|
|
493
|
+
except Exception as e:
|
|
494
|
+
logging.warning('[DB] addQuoteList async failed: %s' % e, extra={'id': 'DB008'})
|
|
495
|
+
|
|
496
|
+
try:
|
|
497
|
+
if not query:
|
|
498
|
+
if hasattr(args[1], 'decode'):
|
|
499
|
+
query = args[1].decode()
|
|
500
|
+
else:
|
|
501
|
+
query = args[1]
|
|
502
|
+
except Exception as e:
|
|
503
|
+
logging.warning('[DB] query decode async failed: %s' % e, extra={'id': 'DB009'})
|
|
504
|
+
query = args[1]
|
|
505
|
+
|
|
506
|
+
if not query:
|
|
507
|
+
return await fn(*args, **kwargs)
|
|
508
|
+
|
|
509
|
+
start_time = DateUtil.nowSystem()
|
|
510
|
+
ctx.start_time = start_time
|
|
511
|
+
ctx.active_sqlhash = query
|
|
512
|
+
|
|
513
|
+
try:
|
|
514
|
+
callback = await fn(*args, **kwargs)
|
|
515
|
+
return callback
|
|
516
|
+
except Exception as e:
|
|
517
|
+
interceptor_step_error(e)
|
|
518
|
+
raise
|
|
519
|
+
finally:
|
|
520
|
+
try:
|
|
521
|
+
if hasattr(args[0], 'rowcount'):
|
|
522
|
+
count = args[0].rowcount
|
|
523
|
+
else:
|
|
524
|
+
count = -1
|
|
525
|
+
except AttributeError:
|
|
526
|
+
count = -1
|
|
527
|
+
|
|
528
|
+
datas = [ctx.lctx.get('dbc', ''), query, str(count)]
|
|
529
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
530
|
+
async_sender.send_packet(PacketTypeEnum.TX_SQL, ctx, datas)
|
|
531
|
+
|
|
532
|
+
if (count is not None) and (count > -1):
|
|
533
|
+
desc = '{0}: {1}'.format('Fetch count', count)
|
|
534
|
+
datas = [' ', ' ', desc]
|
|
535
|
+
ctx.elapsed = 0
|
|
536
|
+
async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
|
|
537
|
+
|
|
538
|
+
ctx.active_sqlhash = 0
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
async def async_interceptor_db_close(fn, *args, **kwargs):
|
|
542
|
+
ctx = TraceContextManager.getLocalContext()
|
|
543
|
+
ctx.db_opening = False
|
|
544
|
+
|
|
545
|
+
if not conf.profile_dbc_close:
|
|
546
|
+
try:
|
|
547
|
+
return await fn(*args, **kwargs)
|
|
548
|
+
except Exception as e:
|
|
549
|
+
interceptor_step_error(e)
|
|
550
|
+
raise
|
|
551
|
+
finally:
|
|
552
|
+
return
|
|
553
|
+
|
|
554
|
+
start_time = DateUtil.nowSystem()
|
|
555
|
+
ctx.start_time = start_time
|
|
556
|
+
|
|
557
|
+
try:
|
|
558
|
+
callback = await fn(*args, **kwargs)
|
|
559
|
+
return callback
|
|
560
|
+
except Exception as e:
|
|
561
|
+
interceptor_step_error(e)
|
|
562
|
+
raise
|
|
563
|
+
finally:
|
|
564
|
+
text = 'DB: Close Connection.'
|
|
565
|
+
datas = [' ', ' ', text]
|
|
566
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
567
|
+
async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def interceptor_pool_get(db_info):
|
|
571
|
+
ctx = TraceContextManager.getLocalContext()
|
|
572
|
+
if not ctx:
|
|
573
|
+
return
|
|
574
|
+
|
|
575
|
+
start_time = DateUtil.nowSystem()
|
|
576
|
+
|
|
577
|
+
db_type = db_info.get('type', 'db')
|
|
578
|
+
text = f"{db_type}://{db_info.get('user', '')}@{db_info.get('host', '')}/{db_info.get('dbname', '')}"
|
|
579
|
+
|
|
580
|
+
ctx.active_dbc = text
|
|
581
|
+
ctx.lctx['dbc'] = text
|
|
582
|
+
ctx.active_dbc = 0
|
|
583
|
+
|
|
584
|
+
datas = [text]
|
|
585
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
586
|
+
|
|
587
|
+
async_sender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, datas)
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def interceptor_pool_release():
|
|
591
|
+
ctx = TraceContextManager.getLocalContext()
|
|
592
|
+
if not ctx or not conf.profile_dbc_close:
|
|
593
|
+
return
|
|
594
|
+
|
|
595
|
+
start_time = DateUtil.nowSystem()
|
|
596
|
+
text = 'DB: Close Connection.'
|
|
597
|
+
datas = [' ', ' ', text]
|
|
598
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
599
|
+
async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
async def async_interceptor_pool_get(db_info):
|
|
603
|
+
ctx = TraceContextManager.getLocalContext()
|
|
604
|
+
if not ctx:
|
|
605
|
+
return
|
|
606
|
+
|
|
607
|
+
start_time = DateUtil.nowSystem()
|
|
608
|
+
|
|
609
|
+
db_type = db_info.get('type', 'db')
|
|
610
|
+
text = f"{db_type}://{db_info.get('user', '')}@{db_info.get('host', '')}/{db_info.get('dbname', '')}"
|
|
611
|
+
|
|
612
|
+
ctx.active_dbc = text
|
|
613
|
+
ctx.lctx['dbc'] = text
|
|
614
|
+
ctx.active_dbc = 0
|
|
615
|
+
|
|
616
|
+
datas = [text]
|
|
617
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
618
|
+
|
|
619
|
+
async_sender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, datas)
|
|
620
|
+
|
|
621
|
+
async def async_interceptor_pool_release():
|
|
622
|
+
ctx = TraceContextManager.getLocalContext()
|
|
623
|
+
if not ctx or not conf.profile_dbc_close:
|
|
624
|
+
return
|
|
625
|
+
|
|
626
|
+
start_time = DateUtil.nowSystem()
|
|
627
|
+
text = 'DB: Close Connection.'
|
|
628
|
+
datas = [' ', ' ', text]
|
|
629
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
630
|
+
async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
|
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from whatap.trace import get_dict
|
|
2
|
+
from whatap.trace.trace_handler import trace_handler
|
|
3
|
+
from whatap.trace.trace_error import \
|
|
4
|
+
interceptor_step_error
|
|
5
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
6
|
+
import whatap.net.async_sender as async_sender
|
|
7
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
8
|
+
from whatap.util.date_util import DateUtil
|
|
9
|
+
import traceback
|
|
10
|
+
|
|
11
|
+
def intercept_connect(fn, *args, **kwargs):
|
|
12
|
+
ctx = TraceContextManager.getLocalContext()
|
|
13
|
+
start_time = DateUtil.nowSystem()
|
|
14
|
+
ctx.start_time = start_time
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
callback = fn(*args, **kwargs)
|
|
18
|
+
return callback
|
|
19
|
+
except Exception as e:
|
|
20
|
+
interceptor_step_error(e)
|
|
21
|
+
finally:
|
|
22
|
+
text = None
|
|
23
|
+
|
|
24
|
+
if kwargs:
|
|
25
|
+
text = 'smtp://'
|
|
26
|
+
text += kwargs.get('host')
|
|
27
|
+
text += ':'
|
|
28
|
+
text += str(kwargs.get('port', 0))
|
|
29
|
+
elif args and len(args) > 2:
|
|
30
|
+
text = 'smtplilb.SMTP.connect('
|
|
31
|
+
text += args[1]
|
|
32
|
+
text += ','
|
|
33
|
+
text += str(args[2])
|
|
34
|
+
text += ')'
|
|
35
|
+
if text:
|
|
36
|
+
payloads = [text, '']
|
|
37
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
38
|
+
async_sender.send_packet(PacketTypeEnum.TX_METHOD, ctx, payloads)
|
|
39
|
+
|
|
40
|
+
def intercept_method(method, fn, *args, **kwargs):
|
|
41
|
+
ctx = TraceContextManager.getLocalContext()
|
|
42
|
+
|
|
43
|
+
start_time = DateUtil.nowSystem()
|
|
44
|
+
ctx.start_time = start_time
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
callback = fn(*args, **kwargs)
|
|
48
|
+
return callback
|
|
49
|
+
except Exception as e:
|
|
50
|
+
interceptor_step_error(e)
|
|
51
|
+
finally:
|
|
52
|
+
payloads = [method, '']
|
|
53
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
54
|
+
async_sender.send_packet(PacketTypeEnum.TX_METHOD, ctx, payloads)
|
|
55
|
+
|
|
56
|
+
def instrument_smtp(module):
|
|
57
|
+
def wrapper(fn):
|
|
58
|
+
@trace_handler(fn)
|
|
59
|
+
def trace(*args, **kwargs):
|
|
60
|
+
callback = intercept_connect(fn, *args, **kwargs)
|
|
61
|
+
return callback
|
|
62
|
+
|
|
63
|
+
return trace
|
|
64
|
+
|
|
65
|
+
if hasattr(module, 'SMTP') and hasattr(module.SMTP, 'connect'):
|
|
66
|
+
module.SMTP.connect= wrapper(module.SMTP.connect)
|
|
67
|
+
|
|
68
|
+
def wrapper(fn, method):
|
|
69
|
+
@trace_handler(fn)
|
|
70
|
+
def trace(*args, **kwargs):
|
|
71
|
+
callback = intercept_method(method, fn, *args, **kwargs)
|
|
72
|
+
return callback
|
|
73
|
+
|
|
74
|
+
return trace
|
|
75
|
+
|
|
76
|
+
for method in ['sendmail',]:
|
|
77
|
+
if hasattr(module, 'SMTP') and hasattr(module.SMTP, method):
|
|
78
|
+
setattr(module.SMTP, method, wrapper(getattr(module.SMTP, method), 'smtplib.SMTP.{}'.format(method)))
|