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,212 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from whatap.conf.configure import Configure as conf
|
|
5
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
6
|
+
from whatap.util.date_util import DateUtil
|
|
7
|
+
from whatap.util.hexa32 import Hexa32
|
|
8
|
+
from whatap.util.hash_util import HashUtil
|
|
9
|
+
|
|
10
|
+
from whatap.util.linked_map import LinkedMap
|
|
11
|
+
try:
|
|
12
|
+
from resource import getrusage, RUSAGE_SELF
|
|
13
|
+
HAS_RESOURCE = True
|
|
14
|
+
except ImportError:
|
|
15
|
+
# resource module is not available on Windows
|
|
16
|
+
HAS_RESOURCE = False
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TraceContext(object):
|
|
21
|
+
transfer_id = None
|
|
22
|
+
transfer_info = None
|
|
23
|
+
|
|
24
|
+
def __init__(self):
|
|
25
|
+
self.host = ''
|
|
26
|
+
self.elapsed = 0
|
|
27
|
+
|
|
28
|
+
self.isStaticContents = 'false'
|
|
29
|
+
|
|
30
|
+
self.id = TraceContextManager.getId()
|
|
31
|
+
self.thread = threading.current_thread()
|
|
32
|
+
self.thread_id = self.thread.ident
|
|
33
|
+
TraceContextManager.start(self)
|
|
34
|
+
|
|
35
|
+
self.pid = os.getpid()
|
|
36
|
+
|
|
37
|
+
self.start_time = 0
|
|
38
|
+
self.start_cpu = 0
|
|
39
|
+
self.start_malloc = 0
|
|
40
|
+
|
|
41
|
+
self.status = 0
|
|
42
|
+
self.asgi_response = None
|
|
43
|
+
self.service_hash = 0
|
|
44
|
+
self.service_name = ''
|
|
45
|
+
self.remoteIp = ''
|
|
46
|
+
self.error = 0
|
|
47
|
+
self.error_step = ''
|
|
48
|
+
self.http_method = ''
|
|
49
|
+
self.http_query = ''
|
|
50
|
+
self.http_content_type = ''
|
|
51
|
+
|
|
52
|
+
self.sql_count = 0
|
|
53
|
+
self.sql_time = 0
|
|
54
|
+
self.sql_insert = 0
|
|
55
|
+
self.sql_update = 0
|
|
56
|
+
self.sql_delete = 0
|
|
57
|
+
self.sql_select = 0
|
|
58
|
+
self.sql_others = 0
|
|
59
|
+
|
|
60
|
+
self.executed_sqlhash = 0
|
|
61
|
+
self.active_sqlhash = 0
|
|
62
|
+
self.active_dbc = 0
|
|
63
|
+
self.active_crud = 0
|
|
64
|
+
|
|
65
|
+
self.httpc_checked = False
|
|
66
|
+
self.httpc_count = 0
|
|
67
|
+
self.httpc_time = 0
|
|
68
|
+
self.httpc_url = ''
|
|
69
|
+
|
|
70
|
+
self.active_httpc_hash = 0
|
|
71
|
+
self.active_httpc_start_time = 0
|
|
72
|
+
self.httpc_host = ''
|
|
73
|
+
self.httpc_port = 0
|
|
74
|
+
|
|
75
|
+
self.mtid = 0
|
|
76
|
+
self.mdepth = 0
|
|
77
|
+
self.mcallee = 0
|
|
78
|
+
|
|
79
|
+
self.mcaller_txid = 0
|
|
80
|
+
self.mcaller_pcode = 0
|
|
81
|
+
self.mcaller_spec = ''
|
|
82
|
+
self.mcaller_url = ''
|
|
83
|
+
self.mcaller_poid = ''
|
|
84
|
+
self.transfer_poid = None
|
|
85
|
+
|
|
86
|
+
self.userid = ''
|
|
87
|
+
self._rawuserid = ''
|
|
88
|
+
self.userAgent = 0
|
|
89
|
+
self.userAgentString = ''
|
|
90
|
+
self.referer = ''
|
|
91
|
+
self.login = ''
|
|
92
|
+
self.userTransaction = 0
|
|
93
|
+
self.debug_sql_call = False
|
|
94
|
+
self.lastSqlStep = None
|
|
95
|
+
self.profileActive = 0
|
|
96
|
+
|
|
97
|
+
self.jdbc_updated = False
|
|
98
|
+
self.jdbc_update_record = 0
|
|
99
|
+
self.jdbc_identity = 0
|
|
100
|
+
self.jdbc_commit = 0
|
|
101
|
+
self.resultSql = LinkedMap()
|
|
102
|
+
|
|
103
|
+
self.rs_count = 0
|
|
104
|
+
self.rs_time = 0
|
|
105
|
+
self.db_opening = False
|
|
106
|
+
self.socket_connecting = False
|
|
107
|
+
|
|
108
|
+
self.mcaller_url_hash = 0
|
|
109
|
+
self.mcaller_step_id = 0
|
|
110
|
+
|
|
111
|
+
self.step_id = 0
|
|
112
|
+
|
|
113
|
+
self.lctx = {}
|
|
114
|
+
self.is_ignored = False
|
|
115
|
+
self.driver = ''
|
|
116
|
+
self.is_llm = 0
|
|
117
|
+
self._llm_httpc_pending = False
|
|
118
|
+
self._llm_httpc_url = ''
|
|
119
|
+
self._llm_operation_type = ''
|
|
120
|
+
self._llm_model = ''
|
|
121
|
+
self._llm_provider = ''
|
|
122
|
+
|
|
123
|
+
def getElapsedTime(self, time=None):
|
|
124
|
+
if not time:
|
|
125
|
+
time = DateUtil.now()
|
|
126
|
+
return time - self.start_time
|
|
127
|
+
|
|
128
|
+
def getCpuTime(self):
|
|
129
|
+
return int(time.time())
|
|
130
|
+
|
|
131
|
+
def getMemory(self):
|
|
132
|
+
# https://docs.python.org/3/library/resource.html?highlight=resource#resource-usage
|
|
133
|
+
if HAS_RESOURCE:
|
|
134
|
+
return int(getrusage(RUSAGE_SELF)[3] + getrusage(RUSAGE_SELF)[4])
|
|
135
|
+
else:
|
|
136
|
+
# Windows fallback: use psutil if available, otherwise return 0
|
|
137
|
+
try:
|
|
138
|
+
import psutil
|
|
139
|
+
process = psutil.Process(os.getpid())
|
|
140
|
+
mem_info = process.memory_info()
|
|
141
|
+
return int(mem_info.rss / 1024) # Convert bytes to KB
|
|
142
|
+
except ImportError:
|
|
143
|
+
return 0
|
|
144
|
+
|
|
145
|
+
def resetStartTime(self):
|
|
146
|
+
self.start_time = 0
|
|
147
|
+
|
|
148
|
+
def transfer(self):
|
|
149
|
+
if self.transfer_id:
|
|
150
|
+
return self.transfer_id
|
|
151
|
+
|
|
152
|
+
sb = []
|
|
153
|
+
sb.append(Hexa32.toString32(self.mtid))
|
|
154
|
+
sb.append(str(self.mdepth + 1))
|
|
155
|
+
sb.append(Hexa32.toString32(self.id))
|
|
156
|
+
sb.append(Hexa32.toString32(self.step_id) if self.step_id else '0')
|
|
157
|
+
transfer_id = ','.join(sb)
|
|
158
|
+
return transfer_id
|
|
159
|
+
|
|
160
|
+
def transferInfo(self):
|
|
161
|
+
if self.transfer_info:
|
|
162
|
+
return self.transfer_info
|
|
163
|
+
|
|
164
|
+
sb = []
|
|
165
|
+
sb.append(str(conf.mtrace_spec))
|
|
166
|
+
sb.append(str(self.service_hash))
|
|
167
|
+
|
|
168
|
+
transfer_info = ','.join(sb)
|
|
169
|
+
return transfer_info
|
|
170
|
+
|
|
171
|
+
def setTransfer(self, headerString):
|
|
172
|
+
x = headerString.find(',')
|
|
173
|
+
if x > 0:
|
|
174
|
+
self.mtid = Hexa32.toLong32(headerString[0:x])
|
|
175
|
+
y = headerString.find(',', x + 1)
|
|
176
|
+
if y > 0:
|
|
177
|
+
self.mdepth = int(headerString[x + 1:y])
|
|
178
|
+
z = headerString.find(',', y + 1)
|
|
179
|
+
|
|
180
|
+
if z < 0:
|
|
181
|
+
self.mcaller_txid = Hexa32.toLong32(headerString[y + 1:])
|
|
182
|
+
else:
|
|
183
|
+
self.mcaller_txid = Hexa32.toLong32(headerString[y + 1: z])
|
|
184
|
+
w = headerString.find(',', z + 1)
|
|
185
|
+
if w < 0:
|
|
186
|
+
step_id_str = headerString[z + 1:]
|
|
187
|
+
else:
|
|
188
|
+
step_id_str = headerString[z + 1: w]
|
|
189
|
+
if step_id_str and step_id_str != '0':
|
|
190
|
+
self.mcaller_step_id = Hexa32.toLong32(step_id_str)
|
|
191
|
+
|
|
192
|
+
def setTransferInfo(self, headerString):
|
|
193
|
+
x = headerString.index(',')
|
|
194
|
+
s1 = headerString[0: x]
|
|
195
|
+
self.mcaller_spec = s1
|
|
196
|
+
self.mcaller_url_hash = headerString[x + 1:]
|
|
197
|
+
|
|
198
|
+
def setTxid(self, myid):
|
|
199
|
+
old_id = self.id
|
|
200
|
+
new_id = Hexa32.toLong32(myid)
|
|
201
|
+
self.id = new_id
|
|
202
|
+
TraceContextManager.entry.remove(old_id)
|
|
203
|
+
TraceContextManager.entry.put(new_id, self)
|
|
204
|
+
|
|
205
|
+
def transferPOID(self):
|
|
206
|
+
if not self.transfer_poid:
|
|
207
|
+
self.transfer_poid = ",".join(
|
|
208
|
+
(Hexa32.toString32(conf.PCODE),
|
|
209
|
+
Hexa32.toString32(HashUtil.hashFromString(conf.OKIND) if isinstance(conf.OKIND, str) else conf.OKIND),
|
|
210
|
+
Hexa32.toString32(int(conf.OID))))
|
|
211
|
+
|
|
212
|
+
return self.transfer_poid
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import os, sys
|
|
2
|
+
import random
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
import uuid
|
|
6
|
+
import random
|
|
7
|
+
from collections import defaultdict
|
|
8
|
+
from ctypes import c_int64
|
|
9
|
+
from urllib.parse import urlparse
|
|
10
|
+
from whatap import logging
|
|
11
|
+
from whatap.conf.configure import Configure as conf
|
|
12
|
+
from whatap.io.data_inputx import DataInputX
|
|
13
|
+
from whatap.io.data_outputx import DataOutputX
|
|
14
|
+
from whatap.util.linked_map import LinkedMap
|
|
15
|
+
from whatap.util.hash_util import HashUtil
|
|
16
|
+
from whatap.util.frame_util import set_greenlet_info
|
|
17
|
+
|
|
18
|
+
import whatap.util.date_util as date_util
|
|
19
|
+
if sys.version_info >= (3, 7):
|
|
20
|
+
import contextvars
|
|
21
|
+
else:
|
|
22
|
+
contextvars = None
|
|
23
|
+
|
|
24
|
+
class TraceContextManager(object):
|
|
25
|
+
entry = LinkedMap()
|
|
26
|
+
local = threading.local()
|
|
27
|
+
if contextvars:
|
|
28
|
+
## python3.7+ support contextvars
|
|
29
|
+
## contextvars support compatibility with threading.local()
|
|
30
|
+
whatap_coroutine_context = contextvars.ContextVar("whatap_coroutine_context", default=None)
|
|
31
|
+
node = uuid.getnode()
|
|
32
|
+
clock_seq = random.randrange(1 << 14)
|
|
33
|
+
dbpool = LinkedMap()
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def keys(cls):
|
|
37
|
+
return cls.entry.keys()
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def size(cls):
|
|
41
|
+
return cls.entry.size()
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def getActiveCount(cls):
|
|
45
|
+
act = [0 for _ in range(3)]
|
|
46
|
+
try:
|
|
47
|
+
en = cls.entry.values()
|
|
48
|
+
while en.hasMoreElements():
|
|
49
|
+
ctx = en.nextElement()
|
|
50
|
+
elapsed = ctx.getElapsedTime()
|
|
51
|
+
if elapsed < conf.trace_active_transaction_yellow_time:
|
|
52
|
+
act[0] += 1
|
|
53
|
+
elif elapsed < conf.trace_active_transaction_red_time:
|
|
54
|
+
act[1] += 1
|
|
55
|
+
else:
|
|
56
|
+
act[2] += 1
|
|
57
|
+
|
|
58
|
+
except Exception as e:
|
|
59
|
+
logging.debug(e, extra={'id': 'WA310'}, exc_info=True)
|
|
60
|
+
|
|
61
|
+
return act
|
|
62
|
+
|
|
63
|
+
_host_hash_cache = {}
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def _get_host_hash(cls, url):
|
|
67
|
+
cached = cls._host_hash_cache.get(url)
|
|
68
|
+
if cached is not None:
|
|
69
|
+
return cached
|
|
70
|
+
try:
|
|
71
|
+
url_str = str(url)
|
|
72
|
+
if '://' not in url_str:
|
|
73
|
+
url_str = 'http://' + url_str
|
|
74
|
+
host = urlparse(url_str).hostname
|
|
75
|
+
if not host:
|
|
76
|
+
return 0
|
|
77
|
+
h = HashUtil.hashFromString(host)
|
|
78
|
+
if len(cls._host_hash_cache) < 1000:
|
|
79
|
+
cls._host_hash_cache[url] = h
|
|
80
|
+
return h
|
|
81
|
+
except Exception:
|
|
82
|
+
return 0
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def getActiveStats(cls):
|
|
86
|
+
act = [0 for _ in range(5)]
|
|
87
|
+
httpc_detail = defaultdict(lambda: {'actx': 0, 'acts': [0, 0, 0]})
|
|
88
|
+
now = date_util.DateUtil.nowSystem()
|
|
89
|
+
try:
|
|
90
|
+
en = cls.entry.values()
|
|
91
|
+
while en.hasMoreElements():
|
|
92
|
+
ctx = en.nextElement()
|
|
93
|
+
if ctx.active_sqlhash:
|
|
94
|
+
act[1] += 1 # sql
|
|
95
|
+
elif ctx.active_httpc_hash:
|
|
96
|
+
act[2] += 1 # httpc
|
|
97
|
+
host_hash = cls._get_host_hash(ctx.active_httpc_hash)
|
|
98
|
+
if host_hash:
|
|
99
|
+
stats = httpc_detail[host_hash]
|
|
100
|
+
stats['actx'] += 1
|
|
101
|
+
start_time = getattr(ctx, 'active_httpc_start_time', 0)
|
|
102
|
+
if start_time > 0:
|
|
103
|
+
elapsed_sec = int(now - start_time) // 1000
|
|
104
|
+
if elapsed_sec <= 2:
|
|
105
|
+
stats['acts'][0] += 1
|
|
106
|
+
elif elapsed_sec <= 7:
|
|
107
|
+
stats['acts'][1] += 1
|
|
108
|
+
else:
|
|
109
|
+
stats['acts'][2] += 1
|
|
110
|
+
elif ctx.db_opening:
|
|
111
|
+
act[3] += 1 # dbc
|
|
112
|
+
elif ctx.socket_connecting:
|
|
113
|
+
act[4] += 1 # socket
|
|
114
|
+
else:
|
|
115
|
+
act[0] += 1 # method
|
|
116
|
+
except Exception as e:
|
|
117
|
+
logging.debug(e, extra={'id': 'WA311'}, exc_info=True)
|
|
118
|
+
return act, httpc_detail
|
|
119
|
+
|
|
120
|
+
@classmethod
|
|
121
|
+
def getContextEnumeration(cls):
|
|
122
|
+
return cls.entry.values()
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def getContext(cls, key):
|
|
126
|
+
return cls.entry.get(key)
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def getLocalContext(cls):
|
|
130
|
+
##python3.7+ support contextvars
|
|
131
|
+
##if contextvars is imported, use contextvars first
|
|
132
|
+
if contextvars:
|
|
133
|
+
return cls.whatap_coroutine_context.get()
|
|
134
|
+
|
|
135
|
+
if not bool(cls.local.__dict__):
|
|
136
|
+
cls.local.context = None
|
|
137
|
+
return cls.local.context
|
|
138
|
+
|
|
139
|
+
@classmethod
|
|
140
|
+
def setLocalContext(cls, o):
|
|
141
|
+
o.thread = threading.current_thread()
|
|
142
|
+
o.thread_id = o.thread.ident
|
|
143
|
+
|
|
144
|
+
# greenlet 환경이면 greenlet 정보 저장
|
|
145
|
+
set_greenlet_info(o)
|
|
146
|
+
|
|
147
|
+
if contextvars:
|
|
148
|
+
cls.whatap_coroutine_context.set(o)
|
|
149
|
+
cls.local.context = o
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def getId(cls):
|
|
153
|
+
uuid1 = uuid.uuid1(TraceContextManager.node, TraceContextManager.clock_seq)
|
|
154
|
+
key = (uuid1.int >> 64 & 0xffffffffffffffff) ^ (uuid1.int << 64 & 0xffffffffffffffff)
|
|
155
|
+
return c_int64(key).value
|
|
156
|
+
|
|
157
|
+
@classmethod
|
|
158
|
+
def start(cls, o):
|
|
159
|
+
key = o.id
|
|
160
|
+
if contextvars:
|
|
161
|
+
cls.whatap_coroutine_context.set(o)
|
|
162
|
+
cls.local.context = o
|
|
163
|
+
cls.entry.put(key, o)
|
|
164
|
+
|
|
165
|
+
return key
|
|
166
|
+
|
|
167
|
+
@classmethod
|
|
168
|
+
def parseThreadId(cls, key):
|
|
169
|
+
o = cls.entry.get(key)
|
|
170
|
+
if o:
|
|
171
|
+
return o.thread_id, o.pid
|
|
172
|
+
else:
|
|
173
|
+
return None, None
|
|
174
|
+
|
|
175
|
+
@classmethod
|
|
176
|
+
def end(cls, key):
|
|
177
|
+
cls.local.context = None
|
|
178
|
+
cls.entry.remove(key)
|
|
179
|
+
|
|
180
|
+
@classmethod
|
|
181
|
+
def getTxProfile(cls, n):
|
|
182
|
+
ctx = cls.getLocalContext()
|
|
183
|
+
if not ctx:
|
|
184
|
+
return None
|
|
185
|
+
return ctx.profile.getLastSteps(n)
|
|
186
|
+
|
|
187
|
+
@classmethod
|
|
188
|
+
def getCurrentThreadId(cls):
|
|
189
|
+
greenlet = sys.modules.get('greenlet')
|
|
190
|
+
|
|
191
|
+
if greenlet:
|
|
192
|
+
current = greenlet.getcurrent()
|
|
193
|
+
if current is not None and current.parent:
|
|
194
|
+
return id(current.parent)
|
|
195
|
+
return threading.get_ident()
|
|
196
|
+
|
|
197
|
+
@classmethod
|
|
198
|
+
def getDBConnPool(cls):
|
|
199
|
+
d = dict()
|
|
200
|
+
keyvalueEnumer = cls.dbpool.entries()
|
|
201
|
+
while keyvalueEnumer.hasMoreElements():
|
|
202
|
+
en = keyvalueEnumer.nextElement()
|
|
203
|
+
d[en.getKey()] = en.getValue()
|
|
204
|
+
return d
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def addDBPoolIdle(cls, url, isCheckIn = False):
|
|
208
|
+
if not cls.dbpool.containsKey(url):
|
|
209
|
+
cls.dbpool.put(url,[0, 0])
|
|
210
|
+
|
|
211
|
+
newstat = cls.dbpool.get(url)
|
|
212
|
+
if isCheckIn:
|
|
213
|
+
newstat[0] -= 1
|
|
214
|
+
if newstat[0] < 0:
|
|
215
|
+
newstat[0] = 0
|
|
216
|
+
newstat[1] += 1
|
|
217
|
+
|
|
218
|
+
cls.dbpool.put(url, newstat)
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
@classmethod
|
|
222
|
+
def removeDBPoolIdle(cls, url):
|
|
223
|
+
if not cls.dbpool.containsKey(url):
|
|
224
|
+
cls.dbpool.put(url,[0, 0])
|
|
225
|
+
return
|
|
226
|
+
newstat = cls.dbpool.get(url)
|
|
227
|
+
if newstat:
|
|
228
|
+
newstat[1] -= 1
|
|
229
|
+
if newstat[1] < 0:
|
|
230
|
+
newstat[1] = 0
|
|
231
|
+
cls.dbpool.put(url, newstat)
|
|
232
|
+
|
|
233
|
+
@classmethod
|
|
234
|
+
def addDBPoolActive(cls, url):
|
|
235
|
+
if not cls.dbpool.containsKey(url):
|
|
236
|
+
cls.dbpool.put(url,[0, 0])
|
|
237
|
+
newstat = cls.dbpool.get(url)
|
|
238
|
+
if newstat:
|
|
239
|
+
newstat[0] += 1
|
|
240
|
+
newstat[1] -= 1
|
|
241
|
+
if newstat[1] < 0:
|
|
242
|
+
newstat[1] = 0
|
|
243
|
+
cls.dbpool.put(url, newstat)
|
|
244
|
+
return
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
4
|
+
from whatap.conf.configure import Configure as conf
|
|
5
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
6
|
+
import whatap.net.async_sender as async_sender
|
|
7
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
8
|
+
from whatap.util.date_util import DateUtil
|
|
9
|
+
from whatap.util.frame_util import get_current_frame
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def sendDebugProfile(ctx, msg):
|
|
13
|
+
if ctx:
|
|
14
|
+
ctx.elapsed = 0
|
|
15
|
+
datas = [' ', ' ', 'DEBUG: ' + msg]
|
|
16
|
+
async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def interceptor_error(status_code, errors, ctx=None):
|
|
20
|
+
if not ctx:
|
|
21
|
+
ctx = TraceContextManager.getLocalContext()
|
|
22
|
+
if not ctx:
|
|
23
|
+
return
|
|
24
|
+
ctx.status = status_code
|
|
25
|
+
if status_code >= 400 and not ctx.error:
|
|
26
|
+
ctx.error = 1
|
|
27
|
+
|
|
28
|
+
error = ''
|
|
29
|
+
frame = get_current_frame(ctx)
|
|
30
|
+
if not frame:
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
for stack in traceback.extract_stack(frame):
|
|
34
|
+
line = stack[0]
|
|
35
|
+
line_num = stack[1]
|
|
36
|
+
method_name = stack[2]
|
|
37
|
+
|
|
38
|
+
if 'whatap' + os.sep + 'trace' in line or 'threading.py' in line:
|
|
39
|
+
continue
|
|
40
|
+
error += '{} ({}:{})\n'.format(method_name, line, line_num)
|
|
41
|
+
|
|
42
|
+
errors.append(error)
|
|
43
|
+
|
|
44
|
+
async_sender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def interceptor_step_error(e, ctx=None):
|
|
48
|
+
from whatap.trace.mod.database.util import extract_db_error_message
|
|
49
|
+
if not ctx:
|
|
50
|
+
ctx = TraceContextManager.getLocalContext()
|
|
51
|
+
if not ctx:
|
|
52
|
+
return
|
|
53
|
+
ctx.error_step = e
|
|
54
|
+
if not ctx.error:
|
|
55
|
+
ctx.error = 1
|
|
56
|
+
|
|
57
|
+
errors = []
|
|
58
|
+
errors.append(e.__class__.__name__)
|
|
59
|
+
|
|
60
|
+
error_message = extract_db_error_message(e)
|
|
61
|
+
errors.append(error_message)
|
|
62
|
+
|
|
63
|
+
error = ''
|
|
64
|
+
frame = get_current_frame(ctx)
|
|
65
|
+
if not frame:
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
for stack in traceback.extract_stack(frame):
|
|
69
|
+
line = stack[0]
|
|
70
|
+
line_num = stack[1]
|
|
71
|
+
method_name = stack[2]
|
|
72
|
+
|
|
73
|
+
if 'whatap' + os.sep + 'trace' in line or 'threading.py' in line:
|
|
74
|
+
continue
|
|
75
|
+
error += '{} ({}:{})\n'.format(method_name, line, line_num)
|
|
76
|
+
|
|
77
|
+
errors.append(error)
|
|
78
|
+
async_sender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors)
|
|
79
|
+
|
|
80
|
+
if conf.profile_exception_stack:
|
|
81
|
+
desc = '\n'.join(errors)
|
|
82
|
+
datas = [' ', ' ', desc]
|
|
83
|
+
ctx.start_time = DateUtil.nowSystem()
|
|
84
|
+
async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _in_eval_worker():
|
|
6
|
+
"""평가 워커 안에서 호출되었고 + judge tracking 옵션이 켜져있는지.
|
|
7
|
+
|
|
8
|
+
옵션 ``llm_eval_track_judge_calls`` (기본 False) 로 토글:
|
|
9
|
+
- False (기본): judge 호출은 intercept 우회 (short-circuit) → 메트릭/logsink 에 안 잡힘
|
|
10
|
+
- True : judge 호출도 intercept 가 정상 동작 → llm_step_status + 메트릭에 잡힘
|
|
11
|
+
|
|
12
|
+
재귀 방지는 ``LlmEvaluatorTask.enqueue`` 의 동일 가드로 차단됨.
|
|
13
|
+
Conf 값이 string 'true'/'false' 일 수도 있어 (Configure.setProperty 의 reload 버그)
|
|
14
|
+
안전하게 string interpret.
|
|
15
|
+
"""
|
|
16
|
+
try:
|
|
17
|
+
from whatap.conf.configure import Configure as conf
|
|
18
|
+
val = getattr(conf, 'llm_eval_track_judge_calls', False)
|
|
19
|
+
if isinstance(val, str):
|
|
20
|
+
enabled = val.strip().lower() in ('true', 'yes', '1', 'on')
|
|
21
|
+
else:
|
|
22
|
+
enabled = bool(val)
|
|
23
|
+
if not enabled:
|
|
24
|
+
return False
|
|
25
|
+
from whatap.counter.tasks.llm_evaluator_task import _is_in_evaluator_worker
|
|
26
|
+
return _is_in_evaluator_worker()
|
|
27
|
+
except Exception:
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def trace_handler(fn, start=False, preload=None):
|
|
32
|
+
def handler(func):
|
|
33
|
+
@wraps(func)
|
|
34
|
+
def wrapper(*args, **kwargs):
|
|
35
|
+
if preload:
|
|
36
|
+
preload(*args, **kwargs)
|
|
37
|
+
|
|
38
|
+
ctx = TraceContextManager.getLocalContext()
|
|
39
|
+
|
|
40
|
+
if not start and not ctx and not _in_eval_worker():
|
|
41
|
+
return fn(*args, **kwargs)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
callback = func(*args, **kwargs)
|
|
45
|
+
except Exception as e:
|
|
46
|
+
if ctx and ctx.error_step == e:
|
|
47
|
+
ctx.error_step = None
|
|
48
|
+
raise e
|
|
49
|
+
raise
|
|
50
|
+
else:
|
|
51
|
+
if ctx and ctx.error_step:
|
|
52
|
+
e = ctx.error_step
|
|
53
|
+
ctx.error_step = None
|
|
54
|
+
raise e
|
|
55
|
+
return callback
|
|
56
|
+
|
|
57
|
+
return wrapper
|
|
58
|
+
|
|
59
|
+
return handler
|
|
60
|
+
|
|
61
|
+
def async_trace_handler(fn, start=False, preload=None):
|
|
62
|
+
def handler(func):
|
|
63
|
+
@wraps(func)
|
|
64
|
+
async def wrapper(*args, **kwargs):
|
|
65
|
+
if preload:
|
|
66
|
+
preload(*args, **kwargs)
|
|
67
|
+
|
|
68
|
+
ctx = TraceContextManager.getLocalContext()
|
|
69
|
+
|
|
70
|
+
if not start and not ctx and not _in_eval_worker():
|
|
71
|
+
return await fn(*args, **kwargs)
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
callback = await func(*args, **kwargs)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
if ctx and ctx.error_step == e:
|
|
77
|
+
ctx.error_step = None
|
|
78
|
+
raise e
|
|
79
|
+
raise
|
|
80
|
+
else:
|
|
81
|
+
if ctx and ctx.error_step:
|
|
82
|
+
e = ctx.error_step
|
|
83
|
+
ctx.error_step = None
|
|
84
|
+
raise e
|
|
85
|
+
return callback
|
|
86
|
+
|
|
87
|
+
return wrapper
|
|
88
|
+
|
|
89
|
+
return handler
|