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,161 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from whatap import DateUtil, conf
|
|
3
|
+
import whatap.net.async_sender as async_sender
|
|
4
|
+
from whatap.pack import logSinkPack
|
|
5
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
6
|
+
import whatap.io as whatapio
|
|
7
|
+
|
|
8
|
+
loguru_injection_processed = False
|
|
9
|
+
def instrument_loguru(module):
|
|
10
|
+
global loguru_injection_processed
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
if not conf.trace_loguru_enabled:
|
|
14
|
+
return
|
|
15
|
+
|
|
16
|
+
def wrapper(fn):
|
|
17
|
+
def trace(*args, **kwargs):
|
|
18
|
+
if not conf.trace_loguru_enabled:
|
|
19
|
+
return fn(*args, **kwargs)
|
|
20
|
+
|
|
21
|
+
if len(args) <=1:
|
|
22
|
+
return fn(*args, **kwargs)
|
|
23
|
+
|
|
24
|
+
ctx = TraceContextManager.getLocalContext()
|
|
25
|
+
if not ctx:
|
|
26
|
+
return fn(*args, **kwargs)
|
|
27
|
+
|
|
28
|
+
record = args[1]
|
|
29
|
+
|
|
30
|
+
if conf.trace_logging_mtid_enabled and ctx and hasattr(ctx, 'mtid') and ctx.mtid:
|
|
31
|
+
original_message = record["message"]
|
|
32
|
+
try:
|
|
33
|
+
record["message"] = original_message + f" (@mtid: {ctx.mtid})"
|
|
34
|
+
|
|
35
|
+
result = fn(*args, **kwargs)
|
|
36
|
+
record["message"] = original_message
|
|
37
|
+
|
|
38
|
+
return result
|
|
39
|
+
except Exception as e:
|
|
40
|
+
record["message"] = original_message
|
|
41
|
+
|
|
42
|
+
category = "AppLog"
|
|
43
|
+
tags = {'@txid': str(ctx.id)} if ctx is not None else {}
|
|
44
|
+
|
|
45
|
+
filename = None
|
|
46
|
+
# record = args[1]
|
|
47
|
+
levelname = record["level"].name
|
|
48
|
+
msg = record["message"]
|
|
49
|
+
fields = {"filename": filename}
|
|
50
|
+
|
|
51
|
+
content = f"{levelname} {msg}"
|
|
52
|
+
|
|
53
|
+
p = logSinkPack.getLogSinkPack(
|
|
54
|
+
t=DateUtil.now(),
|
|
55
|
+
category=f"{category}",
|
|
56
|
+
tags=tags,
|
|
57
|
+
fields=fields,
|
|
58
|
+
line=DateUtil.now(),
|
|
59
|
+
content=content
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
p.pcode = conf.PCODE
|
|
63
|
+
bout = whatapio.DataOutputX()
|
|
64
|
+
bout.writePack(p, None)
|
|
65
|
+
packbytes = bout.toByteArray()
|
|
66
|
+
|
|
67
|
+
async_sender.send_relaypack(packbytes)
|
|
68
|
+
return fn(*args, **kwargs)
|
|
69
|
+
return trace
|
|
70
|
+
if not loguru_injection_processed:
|
|
71
|
+
module.Handler.emit = wrapper(module.Handler.emit)
|
|
72
|
+
loguru_injection_processed = True
|
|
73
|
+
|
|
74
|
+
logging_injection_processed = False
|
|
75
|
+
def instrument_logging(module):
|
|
76
|
+
global logging_injection_processed
|
|
77
|
+
|
|
78
|
+
if not conf.trace_logging_enabled:
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
def wrapper(fn):
|
|
82
|
+
def trace(*args, **kwargs):
|
|
83
|
+
if not conf.trace_logging_enabled:
|
|
84
|
+
return fn(*args, **kwargs)
|
|
85
|
+
|
|
86
|
+
ctx = TraceContextManager.getLocalContext()
|
|
87
|
+
record = args[1]
|
|
88
|
+
|
|
89
|
+
if conf.trace_logging_mtid_enabled and ctx and hasattr(ctx, 'mtid') and ctx.mtid:
|
|
90
|
+
original_msg = record.msg
|
|
91
|
+
original_args = record.args
|
|
92
|
+
try:
|
|
93
|
+
if isinstance(record.msg, str):
|
|
94
|
+
if record.args:
|
|
95
|
+
# 포맷 문자열이 있는 경우, 원본 포맷팅을 먼저 수행
|
|
96
|
+
formatted_msg = record.msg % record.args
|
|
97
|
+
record.msg = formatted_msg
|
|
98
|
+
record.args = () # args를 비워줌
|
|
99
|
+
record.msg = record.msg + f" (@mtid: {ctx.mtid})"
|
|
100
|
+
|
|
101
|
+
result = fn(*args, **kwargs)
|
|
102
|
+
|
|
103
|
+
record.msg = original_msg
|
|
104
|
+
record.args = original_args
|
|
105
|
+
|
|
106
|
+
return result
|
|
107
|
+
except Exception as e:
|
|
108
|
+
record.msg = original_msg
|
|
109
|
+
record.args = original_args
|
|
110
|
+
|
|
111
|
+
##1.3.6 Backward Compatibility
|
|
112
|
+
setattr(record, "txid", None)
|
|
113
|
+
|
|
114
|
+
if not ctx:
|
|
115
|
+
return fn(*args, **kwargs)
|
|
116
|
+
|
|
117
|
+
instance = args[0]
|
|
118
|
+
category = "AppLog"
|
|
119
|
+
|
|
120
|
+
# logger_name = getattr(instance, "name", None)
|
|
121
|
+
# if logger_name and logger_name == "whatap":
|
|
122
|
+
# category = "#AppLog"
|
|
123
|
+
|
|
124
|
+
filehandler = [handler for handler in instance.handlers if handler.__class__.__name__ == "FileHandler"]
|
|
125
|
+
filename = None
|
|
126
|
+
if filehandler and len(filehandler)>0:
|
|
127
|
+
filehandler = filehandler[0]
|
|
128
|
+
if hasattr(filehandler, "baseFilename"):
|
|
129
|
+
filename = filehandler.baseFilename
|
|
130
|
+
|
|
131
|
+
levelname = getattr(record, "levelname", None)
|
|
132
|
+
msg = record.getMessage()
|
|
133
|
+
|
|
134
|
+
fields = {"filename": filename}
|
|
135
|
+
|
|
136
|
+
content = f"{levelname} {msg}"
|
|
137
|
+
|
|
138
|
+
tags = {'@txid': ctx.id} if ctx is not None else {}
|
|
139
|
+
|
|
140
|
+
p = logSinkPack.getLogSinkPack(
|
|
141
|
+
t=DateUtil.now(),
|
|
142
|
+
category=f"{category}",
|
|
143
|
+
tags=tags,
|
|
144
|
+
fields=fields,
|
|
145
|
+
line=DateUtil.now(),
|
|
146
|
+
content=content
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
p.pcode = conf.PCODE
|
|
150
|
+
bout = whatapio.DataOutputX()
|
|
151
|
+
bout.writePack(p, None)
|
|
152
|
+
packbytes = bout.toByteArray()
|
|
153
|
+
|
|
154
|
+
async_sender.send_relaypack(packbytes)
|
|
155
|
+
return fn(*args, **kwargs)
|
|
156
|
+
return trace
|
|
157
|
+
|
|
158
|
+
if not logging_injection_processed:
|
|
159
|
+
module = sys.modules.get("logging")
|
|
160
|
+
module.Logger.callHandlers = wrapper(module.Logger.callHandlers)
|
|
161
|
+
logging_injection_processed = True
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
3
|
+
import whatap.net.async_sender as async_sender
|
|
4
|
+
from whatap.trace import get_dict
|
|
5
|
+
from whatap.trace.trace_handler import trace_handler
|
|
6
|
+
from whatap.util.date_util import DateUtil
|
|
7
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
8
|
+
|
|
9
|
+
from whatap import logging
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def instrument_plugin(module_dict):
|
|
13
|
+
def wrapper(fn):
|
|
14
|
+
@trace_handler(fn)
|
|
15
|
+
def trace(*args, **kwargs):
|
|
16
|
+
module_name = fn.__module__
|
|
17
|
+
def_name = fn.__name__
|
|
18
|
+
class_name = args[0].__class__.__name__ if len(args) and hasattr(
|
|
19
|
+
args[0], def_name) else None
|
|
20
|
+
|
|
21
|
+
if class_name:
|
|
22
|
+
text = '{}:{}.{}'.format(module_name, class_name, def_name)
|
|
23
|
+
else:
|
|
24
|
+
text = '{}:{}'.format(module_name, def_name)
|
|
25
|
+
|
|
26
|
+
ctx = TraceContextManager.getLocalContext()
|
|
27
|
+
start_time = DateUtil.nowSystem()
|
|
28
|
+
ctx.start_time = start_time
|
|
29
|
+
try:
|
|
30
|
+
callback = fn(*args, **kwargs)
|
|
31
|
+
return callback
|
|
32
|
+
except Exception as e:
|
|
33
|
+
logging.debug(e, extra={'id': 'hook_method_patterns PARSING ERROR'})
|
|
34
|
+
finally:
|
|
35
|
+
datas = [text, '']
|
|
36
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
37
|
+
async_sender.send_packet(PacketTypeEnum.TX_METHOD, ctx, datas)
|
|
38
|
+
|
|
39
|
+
return trace
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
for class_def in module_dict['class_defs']:
|
|
43
|
+
class_def_list = class_def.split('.') # exception
|
|
44
|
+
|
|
45
|
+
m_list = []
|
|
46
|
+
t_list = []
|
|
47
|
+
module = module_dict['module']
|
|
48
|
+
if len(class_def_list) == 2:
|
|
49
|
+
if class_def_list[0] == '*':
|
|
50
|
+
for attr in dir(module):
|
|
51
|
+
if not attr.startswith('__') \
|
|
52
|
+
and not attr.endswith('__') \
|
|
53
|
+
and inspect.isclass(getattr(module, attr)) \
|
|
54
|
+
and not isinstance(getattr(module, attr), type(module)) \
|
|
55
|
+
and module.__name__ == getattr(module, attr).__module__ :
|
|
56
|
+
m_list.append(getattr(module, attr))
|
|
57
|
+
else:
|
|
58
|
+
m_list = [getattr(module, class_def_list[0])]
|
|
59
|
+
|
|
60
|
+
target = class_def_list[1]
|
|
61
|
+
else:
|
|
62
|
+
m_list = [module]
|
|
63
|
+
target = class_def_list[0]
|
|
64
|
+
|
|
65
|
+
for m in m_list:
|
|
66
|
+
if target == '*':
|
|
67
|
+
for attr in dir(m):
|
|
68
|
+
if not attr.startswith('__') \
|
|
69
|
+
and not attr.endswith('__') \
|
|
70
|
+
and hasattr(m, attr) \
|
|
71
|
+
and inspect.isfunction(getattr(m, attr)):
|
|
72
|
+
t_list.append(attr)
|
|
73
|
+
else:
|
|
74
|
+
t_list.append(target)
|
|
75
|
+
|
|
76
|
+
for t in t_list:
|
|
77
|
+
if hasattr(m, t):
|
|
78
|
+
try:
|
|
79
|
+
setattr(m, t, wrapper(getattr(m, t)))
|
|
80
|
+
except Exception as e:
|
|
81
|
+
get_dict(m)[t] = wrapper(getattr(m, t))
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logging.debug(e, extra={'id': 'hook_method_patterns PARSING ERROR'})
|
|
84
|
+
|
|
File without changes
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import atexit
|
|
4
|
+
import time
|
|
5
|
+
import traceback
|
|
6
|
+
|
|
7
|
+
import whatap.net.async_sender as async_sender
|
|
8
|
+
|
|
9
|
+
from functools import wraps
|
|
10
|
+
|
|
11
|
+
from whatap.conf.configure import Configure as conf
|
|
12
|
+
from whatap.util.date_util import DateUtil
|
|
13
|
+
from whatap.trace.trace_context import TraceContext
|
|
14
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
15
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
16
|
+
from whatap.trace.trace_handler import trace_handler
|
|
17
|
+
from whatap import logging
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
import threading
|
|
21
|
+
|
|
22
|
+
from whatap.util.frame_util import get_current_frame
|
|
23
|
+
|
|
24
|
+
def trace_handler(fn, start=False, preload=None):
|
|
25
|
+
def handler(func):
|
|
26
|
+
@wraps(func)
|
|
27
|
+
def wrapper(*args, **kwargs):
|
|
28
|
+
if preload:
|
|
29
|
+
preload(*args, **kwargs)
|
|
30
|
+
|
|
31
|
+
ctx = TraceContextManager.getLocalContext()
|
|
32
|
+
if not start and not ctx:
|
|
33
|
+
return fn(*args, **kwargs)
|
|
34
|
+
try:
|
|
35
|
+
callback = func(*args, **kwargs)
|
|
36
|
+
except Exception as e:
|
|
37
|
+
if ctx and ctx.error_step == e:
|
|
38
|
+
ctx.error_step = None
|
|
39
|
+
raise e
|
|
40
|
+
raise
|
|
41
|
+
else:
|
|
42
|
+
if ctx and ctx.error_step:
|
|
43
|
+
e = ctx.error_step
|
|
44
|
+
ctx.error_step = None
|
|
45
|
+
raise e
|
|
46
|
+
return callback
|
|
47
|
+
|
|
48
|
+
return wrapper
|
|
49
|
+
|
|
50
|
+
return handler
|
|
51
|
+
|
|
52
|
+
def load_transaction_patterns():
|
|
53
|
+
raw = conf.standalone_transaction_patterns
|
|
54
|
+
patterns = set(entry.strip() for entry in raw.split(",") if entry.strip())
|
|
55
|
+
|
|
56
|
+
return patterns
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def shutdown_agent():
|
|
60
|
+
|
|
61
|
+
start_time = time.time()
|
|
62
|
+
|
|
63
|
+
while not async_sender.q.empty():
|
|
64
|
+
if time.time() - start_time > 3.0:
|
|
65
|
+
break
|
|
66
|
+
time.sleep(0.1)
|
|
67
|
+
|
|
68
|
+
if async_sender.q.empty():
|
|
69
|
+
return
|
|
70
|
+
else:
|
|
71
|
+
remaining_items = async_sender.q.qsize()
|
|
72
|
+
|
|
73
|
+
def end_interceptor(ctx):
|
|
74
|
+
if not ctx:
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
if conf.dev:
|
|
78
|
+
logging.debug(f'end transaction id(seq): {ctx.id}', extra={'id': 'WA112'})
|
|
79
|
+
print(f'end transaction id(seq): {ctx.id}', dict(extra={'id': 'WA112'}))
|
|
80
|
+
|
|
81
|
+
start_time = DateUtil.nowSystem()
|
|
82
|
+
ctx.start_time = start_time
|
|
83
|
+
|
|
84
|
+
datas = [ctx.host, ctx.service_name, ctx.mtid, ctx.mdepth, ctx.mcaller_txid,
|
|
85
|
+
ctx.mcaller_pcode, ctx.mcaller_spec, str(ctx.mcaller_url_hash), ctx.mcaller_poid, ctx.status,
|
|
86
|
+
ctx.is_llm, ctx.mcaller_step_id]
|
|
87
|
+
|
|
88
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
89
|
+
|
|
90
|
+
async_sender.send_packet(PacketTypeEnum.TX_END, ctx, datas)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
from whatap.counter.tasks.llm_log_sink_task import send_llm_tx_status
|
|
94
|
+
send_llm_tx_status(ctx)
|
|
95
|
+
from whatap.counter.tasks.llm_stat_task import LlmStatTask
|
|
96
|
+
if LlmStatTask._instance:
|
|
97
|
+
LlmStatTask._instance.flush_last_error(ctx)
|
|
98
|
+
except Exception:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def start_interceptor(ctx):
|
|
103
|
+
if conf.dev:
|
|
104
|
+
logging.debug(f'start transaction id(seq): {ctx.id}', extra={'id': 'WA111'})
|
|
105
|
+
print(f'start transaction id(seq): {ctx.id}', dict(extra={'id': 'WA111'}))
|
|
106
|
+
|
|
107
|
+
start_time = DateUtil.nowSystem()
|
|
108
|
+
ctx.start_time = start_time
|
|
109
|
+
|
|
110
|
+
datas = [ctx.host, ctx.service_name, ctx.remoteIp, ctx.userAgentString,
|
|
111
|
+
ctx.referer, ctx.userid, ctx.isStaticContents, ctx.http_method]
|
|
112
|
+
|
|
113
|
+
async_sender.send_packet(PacketTypeEnum.TX_START, ctx, datas)
|
|
114
|
+
|
|
115
|
+
def error_interceptor(error_msg,error_type,ctx):
|
|
116
|
+
if not ctx:
|
|
117
|
+
ctx = TraceContextManager.getLocalContext()
|
|
118
|
+
if not ctx:
|
|
119
|
+
return
|
|
120
|
+
if not ctx.error:
|
|
121
|
+
ctx.error = 1
|
|
122
|
+
|
|
123
|
+
error = ''
|
|
124
|
+
errors = []
|
|
125
|
+
errors.append(error_type)
|
|
126
|
+
errors.append(error_msg)
|
|
127
|
+
|
|
128
|
+
frame = get_current_frame(ctx)
|
|
129
|
+
if not frame:
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
for stack in traceback.extract_stack(frame):
|
|
133
|
+
line = stack[0]
|
|
134
|
+
line_num = stack[1]
|
|
135
|
+
method_name = stack[2]
|
|
136
|
+
|
|
137
|
+
if 'whatap' + os.sep + 'trace' in line or 'threading.py' in line:
|
|
138
|
+
continue
|
|
139
|
+
error += '{} ({}:{})\n'.format(method_name, line, line_num)
|
|
140
|
+
|
|
141
|
+
errors.append(error)
|
|
142
|
+
|
|
143
|
+
async_sender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors)
|
|
144
|
+
|
|
145
|
+
if conf.profile_exception_stack:
|
|
146
|
+
desc = '\n'.join(errors)
|
|
147
|
+
datas = [' ', ' ', desc]
|
|
148
|
+
ctx.start_time = DateUtil.nowSystem()
|
|
149
|
+
async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
|
|
150
|
+
|
|
151
|
+
def instrument_standalone_multiple():
|
|
152
|
+
atexit.register(shutdown_agent)
|
|
153
|
+
|
|
154
|
+
patterns = load_transaction_patterns()
|
|
155
|
+
targets_to_patch = {tuple(entry.split(":", 1)) for entry in patterns}
|
|
156
|
+
|
|
157
|
+
tracked_methods = set()
|
|
158
|
+
tracked_funcs = set()
|
|
159
|
+
modules = set()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
for entry in patterns:
|
|
163
|
+
mod, target = entry.split(":", 1)
|
|
164
|
+
if "." in target:
|
|
165
|
+
cls, mtd = target.split(".", 1)
|
|
166
|
+
tracked_methods.add((mod, cls, mtd))
|
|
167
|
+
else:
|
|
168
|
+
func = target
|
|
169
|
+
tracked_funcs.add((mod, func))
|
|
170
|
+
|
|
171
|
+
modules.add(mod)
|
|
172
|
+
|
|
173
|
+
def wrapper(fn, module_name, target_name):
|
|
174
|
+
@trace_handler(fn, start=True)
|
|
175
|
+
def trace(*args, **kwargs):
|
|
176
|
+
prev_ctx = TraceContextManager.getLocalContext()
|
|
177
|
+
ctx = TraceContext()
|
|
178
|
+
ctx.service_name = f"{module_name}:{target_name}"
|
|
179
|
+
start_interceptor(ctx)
|
|
180
|
+
try:
|
|
181
|
+
callback = fn(*args, **kwargs)
|
|
182
|
+
return callback
|
|
183
|
+
except Exception as e:
|
|
184
|
+
error_msg = str(e)
|
|
185
|
+
error_type = e.__class__.__name__
|
|
186
|
+
error_interceptor(error_msg,error_type,ctx)
|
|
187
|
+
raise e
|
|
188
|
+
finally:
|
|
189
|
+
end_interceptor(ctx)
|
|
190
|
+
TraceContextManager.setLocalContext(prev_ctx)
|
|
191
|
+
|
|
192
|
+
return trace
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
patching_queue = []
|
|
197
|
+
sys_trace_current_service_name = None
|
|
198
|
+
|
|
199
|
+
def trace_and_patch_profiler(frame, event, arg):
|
|
200
|
+
global sys_trace_current_service_name
|
|
201
|
+
|
|
202
|
+
if not targets_to_patch and not patching_queue:
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
module_name = frame.f_globals.get("__name__")
|
|
206
|
+
if module_name not in (modules) or event not in ("call", "return","exception"):
|
|
207
|
+
return trace_and_patch_profiler
|
|
208
|
+
|
|
209
|
+
fn_name = frame.f_code.co_name
|
|
210
|
+
is_method = "self" in frame.f_locals
|
|
211
|
+
|
|
212
|
+
is_target = False
|
|
213
|
+
full_target_name = fn_name
|
|
214
|
+
target_tuple = None
|
|
215
|
+
|
|
216
|
+
if is_method:
|
|
217
|
+
cls_name = type(frame.f_locals["self"]).__name__
|
|
218
|
+
full_target_name = f"{cls_name}.{fn_name}"
|
|
219
|
+
if (module_name, cls_name, fn_name) in tracked_methods:
|
|
220
|
+
is_target = True
|
|
221
|
+
target_tuple = (module_name, full_target_name)
|
|
222
|
+
else:
|
|
223
|
+
if (module_name, fn_name) in tracked_funcs:
|
|
224
|
+
is_target = True
|
|
225
|
+
target_tuple = (module_name, full_target_name)
|
|
226
|
+
|
|
227
|
+
if target_tuple in targets_to_patch:
|
|
228
|
+
if event == "call":
|
|
229
|
+
if not is_target:
|
|
230
|
+
return trace_and_patch_profiler
|
|
231
|
+
|
|
232
|
+
prev_ctx = TraceContextManager.getLocalContext()
|
|
233
|
+
if prev_ctx:
|
|
234
|
+
patching_queue.append(prev_ctx)
|
|
235
|
+
|
|
236
|
+
ctx = TraceContext()
|
|
237
|
+
ctx.service_name = f"{module_name}:{full_target_name}"
|
|
238
|
+
sys_trace_current_service_name = ctx.service_name
|
|
239
|
+
|
|
240
|
+
TraceContextManager.setLocalContext(ctx)
|
|
241
|
+
start_interceptor(ctx)
|
|
242
|
+
|
|
243
|
+
try:
|
|
244
|
+
module_obj = sys.modules[module_name]
|
|
245
|
+
if is_method:
|
|
246
|
+
cls_name, mtd_name = target_tuple[1].split('.', 1)
|
|
247
|
+
cls_obj = getattr(module_obj, cls_name)
|
|
248
|
+
fn = getattr(cls_obj, mtd_name)
|
|
249
|
+
wrapped_fn = wrapper(fn, module_name, target_tuple[1])
|
|
250
|
+
setattr(cls_obj, mtd_name, wrapped_fn)
|
|
251
|
+
else:
|
|
252
|
+
func_name = target_tuple[1]
|
|
253
|
+
fn = getattr(module_obj, func_name)
|
|
254
|
+
wrapped_fn = wrapper(fn, module_name, func_name)
|
|
255
|
+
setattr(module_obj, func_name, wrapped_fn)
|
|
256
|
+
|
|
257
|
+
targets_to_patch.remove(target_tuple)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
except (AttributeError, KeyError) as e:
|
|
262
|
+
if target_tuple in targets_to_patch:
|
|
263
|
+
targets_to_patch.remove(target_tuple)
|
|
264
|
+
|
|
265
|
+
if event == "return":
|
|
266
|
+
ctx = TraceContextManager.getLocalContext()
|
|
267
|
+
if ctx and sys_trace_current_service_name == f"{module_name}:{full_target_name}":
|
|
268
|
+
end_interceptor(ctx)
|
|
269
|
+
if patching_queue:
|
|
270
|
+
prev_ctx = patching_queue.pop()
|
|
271
|
+
TraceContextManager.setLocalContext(prev_ctx)
|
|
272
|
+
sys_trace_current_service_name = prev_ctx.service_name
|
|
273
|
+
else:
|
|
274
|
+
if not(targets_to_patch):
|
|
275
|
+
sys.settrace(None)
|
|
276
|
+
|
|
277
|
+
if event == "exception":
|
|
278
|
+
ctx = TraceContextManager.getLocalContext()
|
|
279
|
+
if ctx and sys_trace_current_service_name == f"{module_name}:{full_target_name}":
|
|
280
|
+
exc_type, exc_value , tb = arg
|
|
281
|
+
error_interceptor(exc_value,exc_type,ctx)
|
|
282
|
+
end_interceptor(ctx)
|
|
283
|
+
if patching_queue:
|
|
284
|
+
prev_ctx = patching_queue.pop()
|
|
285
|
+
TraceContextManager.setLocalContext(prev_ctx)
|
|
286
|
+
sys_trace_current_service_name = prev_ctx.service_name
|
|
287
|
+
else:
|
|
288
|
+
if not (targets_to_patch):
|
|
289
|
+
sys.settrace(None)
|
|
290
|
+
|
|
291
|
+
return trace_and_patch_profiler
|
|
292
|
+
|
|
293
|
+
sys.settrace(trace_and_patch_profiler)
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import traceback
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from whatap.conf.configure import Configure as conf
|
|
8
|
+
from whatap.util.date_util import DateUtil
|
|
9
|
+
from whatap.trace.trace_context import TraceContext
|
|
10
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
11
|
+
import whatap.net.async_sender as async_sender
|
|
12
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
13
|
+
from whatap import logging
|
|
14
|
+
|
|
15
|
+
from whatap.util.frame_util import get_current_frame
|
|
16
|
+
|
|
17
|
+
ctx = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def global_exception_handler(exc_type, exc_value, exc_traceback):
|
|
21
|
+
|
|
22
|
+
global ctx
|
|
23
|
+
traceback.print_exception(exc_type, exc_value, exc_traceback)
|
|
24
|
+
|
|
25
|
+
standalone_error(exc_value, ctx)
|
|
26
|
+
|
|
27
|
+
sys.excepthook = global_exception_handler
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def standalone_error(e, ctx=None):
|
|
31
|
+
|
|
32
|
+
if not ctx:
|
|
33
|
+
ctx = TraceContextManager.getLocalContext()
|
|
34
|
+
if not ctx:
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
ctx.error_step = e
|
|
38
|
+
if not ctx.error:
|
|
39
|
+
ctx.error = 1
|
|
40
|
+
|
|
41
|
+
errors = []
|
|
42
|
+
errors.append(e.__class__.__name__)
|
|
43
|
+
|
|
44
|
+
error_message = str(e)
|
|
45
|
+
errors.append(error_message)
|
|
46
|
+
|
|
47
|
+
error_stack = ''
|
|
48
|
+
frame = get_current_frame(ctx)
|
|
49
|
+
if not frame:
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
for stack in traceback.extract_stack(frame):
|
|
53
|
+
if 'whatap' + os.sep + 'trace' in stack.filename or 'threading.py' in stack.filename:
|
|
54
|
+
continue
|
|
55
|
+
error_stack += f'{stack.name} ({stack.filename}:{stack.lineno})\n'
|
|
56
|
+
|
|
57
|
+
errors.append(error_stack)
|
|
58
|
+
async_sender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors)
|
|
59
|
+
if conf.profile_exception_stack:
|
|
60
|
+
desc = '\n'.join(errors)
|
|
61
|
+
datas = [' ', ' ', desc]
|
|
62
|
+
ctx.start_time = DateUtil.nowSystem()
|
|
63
|
+
async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@atexit.register
|
|
69
|
+
def standalone_end():
|
|
70
|
+
global ctx
|
|
71
|
+
ctx = ctx
|
|
72
|
+
|
|
73
|
+
if not ctx:
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
if conf.dev:
|
|
77
|
+
logging.debug('end transaction id(seq): {}'.format(ctx.id),
|
|
78
|
+
extra={'id': 'WA112'})
|
|
79
|
+
print('end transaction id(seq): {}'.format(ctx.id),
|
|
80
|
+
dict(extra={'id': 'WA112'}))
|
|
81
|
+
|
|
82
|
+
start_time = DateUtil.nowSystem()
|
|
83
|
+
ctx.start_time = start_time
|
|
84
|
+
|
|
85
|
+
datas = [ctx.host, ctx.service_name, ctx.mtid, ctx.mdepth, ctx.mcaller_txid,
|
|
86
|
+
ctx.mcaller_pcode, ctx.mcaller_spec, str(ctx.mcaller_url_hash), ctx.mcaller_poid, ctx.status,
|
|
87
|
+
ctx.is_llm, ctx.mcaller_step_id]
|
|
88
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
89
|
+
|
|
90
|
+
async_sender.send_packet(PacketTypeEnum.TX_END, ctx, datas)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
from whatap.counter.tasks.llm_log_sink_task import send_llm_tx_status
|
|
94
|
+
send_llm_tx_status(ctx)
|
|
95
|
+
from whatap.counter.tasks.llm_stat_task import LlmStatTask
|
|
96
|
+
if LlmStatTask._instance:
|
|
97
|
+
LlmStatTask._instance.flush_last_error(ctx)
|
|
98
|
+
except Exception:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
shutdown_start_time = time.time()
|
|
102
|
+
while not async_sender.q.empty():
|
|
103
|
+
if time.time() - shutdown_start_time > 3.0:
|
|
104
|
+
break
|
|
105
|
+
time.sleep(0.1)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def instrument_standalone_single():
|
|
110
|
+
global ctx
|
|
111
|
+
|
|
112
|
+
ctx = TraceContext()
|
|
113
|
+
ctx = ctx
|
|
114
|
+
if conf.dev:
|
|
115
|
+
logging.debug('start transaction id(seq): {}'.format(ctx.id),
|
|
116
|
+
extra={'id': 'WA111'})
|
|
117
|
+
print('start transaction id(seq): {}'.format(ctx.id), dict(extra={'id': 'WA111'}))
|
|
118
|
+
|
|
119
|
+
start_time = DateUtil.nowSystem()
|
|
120
|
+
ctx.start_time = start_time
|
|
121
|
+
|
|
122
|
+
ctx.service_name = sys.argv[0]
|
|
123
|
+
|
|
124
|
+
datas = [ctx.host,
|
|
125
|
+
ctx.service_name,
|
|
126
|
+
ctx.remoteIp,
|
|
127
|
+
ctx.userAgentString,
|
|
128
|
+
ctx.referer,
|
|
129
|
+
ctx.userid,
|
|
130
|
+
ctx.isStaticContents,
|
|
131
|
+
ctx.http_method
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
async_sender.send_packet(PacketTypeEnum.TX_START, ctx, datas)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
|
|
3
|
+
from whatap.trace.trace_context import TraceContext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SimpleTraceContext(TraceContext):
|
|
7
|
+
def __init__(self, ctx: TraceContext):
|
|
8
|
+
self.ctx = ctx
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
Replaced Python's built-in deepcopy function with a manual deep copy method in SimpleTraceContext.
|
|
12
|
+
This change prevents potential pickle errors when serializing the TraceContext object.
|
|
13
|
+
Each attribute is copied individually to ensure that complex objects within TraceContext are correctly handled.
|
|
14
|
+
"""
|
|
15
|
+
def getDeepCopiedContext(self):
|
|
16
|
+
for key, value in self.ctx.__dict__.items():
|
|
17
|
+
setattr(self, key, value)
|
|
18
|
+
return self
|