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,394 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
|
|
3
|
+
import sys, os
|
|
4
|
+
import traceback
|
|
5
|
+
import time
|
|
6
|
+
import logging
|
|
7
|
+
import socket
|
|
8
|
+
import errno
|
|
9
|
+
import socket
|
|
10
|
+
from . import stackhelper
|
|
11
|
+
|
|
12
|
+
from whatap.conf.configure import Configure as conf
|
|
13
|
+
from whatap.io import DataOutputX
|
|
14
|
+
from whatap.net.packet_enum import PacketEnum
|
|
15
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
16
|
+
from whatap.net.param_def import ParamDef
|
|
17
|
+
from whatap.trace import TraceContextManager
|
|
18
|
+
try:
|
|
19
|
+
from importlib.metadata import distributions as _distributions
|
|
20
|
+
def _get_installed_packages():
|
|
21
|
+
return ', '.join('{} {}'.format(d.metadata['Name'], d.version) for d in _distributions())
|
|
22
|
+
except ImportError:
|
|
23
|
+
import pkg_resources
|
|
24
|
+
def _get_installed_packages():
|
|
25
|
+
return ', '.join(str(d) for d in pkg_resources.working_set)
|
|
26
|
+
|
|
27
|
+
class UdpSession(object):
|
|
28
|
+
s = None
|
|
29
|
+
llm_s = None
|
|
30
|
+
buffer_arr = []
|
|
31
|
+
llm_buffer_arr = []
|
|
32
|
+
thread_lock = threading.Lock()
|
|
33
|
+
llm_thread_lock = threading.Lock()
|
|
34
|
+
socket_lock = threading.Lock()
|
|
35
|
+
read_timeout = 5
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def udp(cls):
|
|
39
|
+
|
|
40
|
+
home = 'WHATAP_HOME'
|
|
41
|
+
whatap_home = os.environ.get(home,'')
|
|
42
|
+
|
|
43
|
+
# Unix sockets are not available on Windows
|
|
44
|
+
if conf.unix_socket_enabled == True and sys.platform != 'win32':
|
|
45
|
+
unix_socket_path = os.path.join(whatap_home, conf.unix_socket)
|
|
46
|
+
while not os.path.exists(unix_socket_path):
|
|
47
|
+
time.sleep(0.1)
|
|
48
|
+
with cls.socket_lock:
|
|
49
|
+
tmp_socket_path = os.path.join(whatap_home,'run', 'whatap.{}.sock'.format(os.getpid()))
|
|
50
|
+
if os.path.exists(tmp_socket_path):
|
|
51
|
+
os.remove(tmp_socket_path)
|
|
52
|
+
prefix=os.path.split(tmp_socket_path)[0]
|
|
53
|
+
if not os.path.exists(prefix):
|
|
54
|
+
os.makedirs(prefix)
|
|
55
|
+
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
|
56
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, PacketEnum.PACKET_SEND_BUFFER_SIZE)
|
|
57
|
+
sock.bind(tmp_socket_path)
|
|
58
|
+
sock.connect(unix_socket_path)
|
|
59
|
+
sock.settimeout(cls.read_timeout)
|
|
60
|
+
if cls.s:
|
|
61
|
+
try:
|
|
62
|
+
cls.s.close()
|
|
63
|
+
except Exception as e:
|
|
64
|
+
print(e)
|
|
65
|
+
cls.s = sock
|
|
66
|
+
else:
|
|
67
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
68
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, PacketEnum.PACKET_SEND_BUFFER_SIZE)
|
|
69
|
+
sock.connect((PacketEnum.SERVER, int(conf.net_udp_port)))
|
|
70
|
+
sock.settimeout(cls.read_timeout)
|
|
71
|
+
if cls.s:
|
|
72
|
+
try:
|
|
73
|
+
cls.s.close()
|
|
74
|
+
except Exception as e:
|
|
75
|
+
print(e)
|
|
76
|
+
cls.s = sock
|
|
77
|
+
return cls.s
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def udp_llm(cls):
|
|
81
|
+
"""Initialize UDP socket for LLM Go Agent."""
|
|
82
|
+
llm_port = int(getattr(conf, 'llm_net_udp_port', 0))
|
|
83
|
+
if not llm_port:
|
|
84
|
+
return None
|
|
85
|
+
try:
|
|
86
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
87
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, PacketEnum.PACKET_SEND_BUFFER_SIZE)
|
|
88
|
+
sock.connect((PacketEnum.SERVER, llm_port))
|
|
89
|
+
sock.settimeout(cls.read_timeout)
|
|
90
|
+
if cls.llm_s:
|
|
91
|
+
try:
|
|
92
|
+
cls.llm_s.close()
|
|
93
|
+
except Exception:
|
|
94
|
+
pass
|
|
95
|
+
cls.llm_s = sock
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logging.debug('WHATAP: LLM UDP init failed: %s' % e, extra={'id': 'WA920'}, exc_info=True)
|
|
98
|
+
return cls.llm_s
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def send_packet(cls, packet_type, ctx, datas=[]):
|
|
102
|
+
import whatap.net.udp_thread
|
|
103
|
+
try:
|
|
104
|
+
if sum( [ len(x) for x in cls.buffer_arr]) + sum( [len(x) if isinstance(x, (str, bytes)) else 4 for x in datas ] ) +PacketEnum.PACKET_BODY_REQUIRED_SIZE + 1 > PacketEnum.PACKET_BUFFER_SIZE:
|
|
105
|
+
cls.send(packet_type, ctx)
|
|
106
|
+
|
|
107
|
+
if not datas:
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
dout = DataOutputX()
|
|
111
|
+
start_header_buffer_size = dout.size()
|
|
112
|
+
|
|
113
|
+
# header
|
|
114
|
+
dout.writeByte(packet_type)
|
|
115
|
+
dout.writeInt(PacketEnum.PACKET_VERSION)
|
|
116
|
+
dout.writeInt(0)
|
|
117
|
+
|
|
118
|
+
# body
|
|
119
|
+
start_body_buffer_size = dout.size()
|
|
120
|
+
|
|
121
|
+
if ctx:
|
|
122
|
+
dout.writeLong(ctx.id)
|
|
123
|
+
dout.writeLong(ctx.start_time)
|
|
124
|
+
dout.writeInt(ctx.elapsed)
|
|
125
|
+
dout.writeLong(ctx.getCpuTime())
|
|
126
|
+
dout.writeLong(ctx.getMemory())
|
|
127
|
+
dout.writeInt(os.getpid())
|
|
128
|
+
dout.writeLong(ctx.thread_id)
|
|
129
|
+
|
|
130
|
+
diff = PacketEnum.PACKET_BUFFER_SIZE \
|
|
131
|
+
- sum( [len(x) for x in cls.buffer_arr]) - (PacketEnum.PACKET_BODY_REQUIRED_SIZE + 1)
|
|
132
|
+
for data in datas:
|
|
133
|
+
data = str(data)[:PacketEnum.DATA_SIZE_LIMIT]
|
|
134
|
+
diff -= len(data)
|
|
135
|
+
if diff < 0:
|
|
136
|
+
data = ' '
|
|
137
|
+
logging.debug('message too long.', extra={'id': 'WA999'},
|
|
138
|
+
exc_info=False)
|
|
139
|
+
dout.writeUTF(data)
|
|
140
|
+
|
|
141
|
+
with cls.thread_lock:
|
|
142
|
+
dout.writeToPos(
|
|
143
|
+
start_header_buffer_size + PacketEnum.PACKET_HEADER_LEN_POS,
|
|
144
|
+
dout.size() - start_body_buffer_size)
|
|
145
|
+
|
|
146
|
+
cls.buffer_arr.append(dout.buffer.getvalue())
|
|
147
|
+
|
|
148
|
+
if not ctx or packet_type == PacketTypeEnum.TX_START \
|
|
149
|
+
or packet_type == PacketTypeEnum.TX_END:
|
|
150
|
+
cls.send(packet_type, ctx)
|
|
151
|
+
except Exception as e:
|
|
152
|
+
logging.debug(e, extra={'id': 'WA913'}, exc_info=True)
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def send(cls, packet_type, ctx):
|
|
156
|
+
if not cls.buffer_arr:
|
|
157
|
+
if packet_type == PacketTypeEnum.TX_END and ctx:
|
|
158
|
+
TraceContextManager.end(ctx.id)
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
sendbuf = b''.join(cls.buffer_arr)
|
|
162
|
+
|
|
163
|
+
# APM Go Agent 전송
|
|
164
|
+
if cls.s:
|
|
165
|
+
try:
|
|
166
|
+
cls.s.send(sendbuf)
|
|
167
|
+
except ConnectionRefusedError as e:
|
|
168
|
+
logging.debug(e, extra={'id': 'WA911'}, exc_info=True)
|
|
169
|
+
cls.udp()
|
|
170
|
+
except Exception as e:
|
|
171
|
+
logging.debug(e, extra={'id': 'WA912'}, exc_info=True)
|
|
172
|
+
if len(cls.buffer_arr) > 1:
|
|
173
|
+
overflow = cls.buffer_arr.pop()
|
|
174
|
+
cls.send(packet_type, ctx)
|
|
175
|
+
cls.buffer_arr.append(overflow)
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
# LLM Go Agent에도 APM 트랜잭션 데이터 전송 (tx correlation용)
|
|
179
|
+
if not cls.llm_s and getattr(conf, 'llm_enabled', False):
|
|
180
|
+
llm_port = int(getattr(conf, 'llm_net_udp_port', 0))
|
|
181
|
+
if llm_port:
|
|
182
|
+
cls.udp_llm()
|
|
183
|
+
if cls.llm_s:
|
|
184
|
+
try:
|
|
185
|
+
cls.llm_s.send(sendbuf)
|
|
186
|
+
except ConnectionRefusedError:
|
|
187
|
+
cls.udp_llm()
|
|
188
|
+
except Exception:
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
if packet_type == PacketTypeEnum.TX_END and ctx:
|
|
192
|
+
TraceContextManager.end(ctx.id)
|
|
193
|
+
cls.buffer_arr = []
|
|
194
|
+
|
|
195
|
+
@classmethod
|
|
196
|
+
def get(cls):
|
|
197
|
+
from whatap.conf.configure import Configure as conf
|
|
198
|
+
sockets = [s for s in (cls.s, cls.llm_s) if s]
|
|
199
|
+
if not sockets:
|
|
200
|
+
return
|
|
201
|
+
try:
|
|
202
|
+
import select
|
|
203
|
+
readable, _, _ = select.select(sockets, [], [], cls.read_timeout)
|
|
204
|
+
for sock in readable:
|
|
205
|
+
try:
|
|
206
|
+
received = sock.recv(1024)
|
|
207
|
+
if received:
|
|
208
|
+
cls.handle(received.decode().split(','))
|
|
209
|
+
except socket.error:
|
|
210
|
+
pass
|
|
211
|
+
except socket.error as serr:
|
|
212
|
+
if serr.errno == errno.EAGAIN:
|
|
213
|
+
conf.init(display=False)
|
|
214
|
+
else:
|
|
215
|
+
raise serr
|
|
216
|
+
|
|
217
|
+
@classmethod
|
|
218
|
+
def getThreadStackFaultHandler(cls, thread_id):
|
|
219
|
+
stackhelper.StackParser.parse()
|
|
220
|
+
for line, line_num, method_name in stackhelper.StackParser.find(thread_id):
|
|
221
|
+
if 'whatap' + os.sep + 'trace' in line or 'threading.py' in line:
|
|
222
|
+
continue
|
|
223
|
+
yield '{} ({}:{})\n'.format(method_name, line, line_num)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@classmethod
|
|
227
|
+
def getThreadStack(cls, frame):
|
|
228
|
+
for stack in traceback.extract_stack(frame):
|
|
229
|
+
line = stack[0]
|
|
230
|
+
line_num = stack[1]
|
|
231
|
+
method_name = stack[2]
|
|
232
|
+
|
|
233
|
+
if 'whatap' + os.sep + 'trace' in line or 'threading.py' in line:
|
|
234
|
+
continue
|
|
235
|
+
yield '{} ({}:{})\n'.format(method_name, line, line_num)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@classmethod
|
|
239
|
+
def handle(cls, received):
|
|
240
|
+
param_id, request, extra = tuple(received)
|
|
241
|
+
param_id = int(param_id)
|
|
242
|
+
thread_id = 0
|
|
243
|
+
pid = 0
|
|
244
|
+
if(extra != ''):
|
|
245
|
+
extra_datas = extra.replace(' ', ', ').split(', ')
|
|
246
|
+
thread_id, pid = TraceContextManager.parseThreadId(int(extra_datas[1]))
|
|
247
|
+
|
|
248
|
+
data = ''
|
|
249
|
+
if not param_id:
|
|
250
|
+
# active stack pack
|
|
251
|
+
datas = []
|
|
252
|
+
data = extra.replace(' ', ', ')
|
|
253
|
+
|
|
254
|
+
frame = sys._current_frames().get(thread_id)
|
|
255
|
+
if not frame:
|
|
256
|
+
return
|
|
257
|
+
|
|
258
|
+
if conf.threadstack_faulthandler:
|
|
259
|
+
for stack in cls.getThreadStackFaultHandler(thread_id):
|
|
260
|
+
data += stack
|
|
261
|
+
datas.append(data)
|
|
262
|
+
else:
|
|
263
|
+
for stack in cls.getThreadStack(frame):
|
|
264
|
+
data += stack
|
|
265
|
+
datas.append(data)
|
|
266
|
+
|
|
267
|
+
cls.send_packet(PacketTypeEnum.ACTIVE_STACK, None, datas)
|
|
268
|
+
else:
|
|
269
|
+
# param pack
|
|
270
|
+
# format: "[packetType], [ctx], [datas: xxxx xxxx xxxx]"
|
|
271
|
+
datas = [str(param_id), request]
|
|
272
|
+
if param_id == ParamDef.MODULE_DEPENDENCY:
|
|
273
|
+
|
|
274
|
+
data = _get_installed_packages()
|
|
275
|
+
datas.append(data)
|
|
276
|
+
elif param_id == ParamDef.GET_ACTIVE_TRANSACTION_DETAIL:
|
|
277
|
+
data = extra.replace(' ', ', ')
|
|
278
|
+
|
|
279
|
+
frame = sys._current_frames().get(thread_id)
|
|
280
|
+
if not frame:
|
|
281
|
+
return
|
|
282
|
+
|
|
283
|
+
for stack in traceback.extract_stack(frame):
|
|
284
|
+
line = stack[0]
|
|
285
|
+
line_num = stack[1]
|
|
286
|
+
method_name = stack[2]
|
|
287
|
+
|
|
288
|
+
if line.find('whatap/trace') > -1:
|
|
289
|
+
continue
|
|
290
|
+
data += '{} ({}:{})\n'.format(method_name, line, line_num)
|
|
291
|
+
|
|
292
|
+
datas.append(data)
|
|
293
|
+
elif param_id == ParamDef.GET_ACTIVE_STATS:
|
|
294
|
+
stats, httpc_detail = TraceContextManager.getActiveStats()
|
|
295
|
+
result = ','.join([str(x) for x in stats])
|
|
296
|
+
for host_hash, d in httpc_detail.items():
|
|
297
|
+
result += '|{}:{}:{}:{}:{}'.format(
|
|
298
|
+
host_hash, d['actx'],
|
|
299
|
+
d['acts'][0], d['acts'][1], d['acts'][2])
|
|
300
|
+
# LLM meter count
|
|
301
|
+
from whatap.conf.configure import Configure as conf
|
|
302
|
+
if getattr(conf, 'llm_enabled', False):
|
|
303
|
+
from whatap.llm.stats.meter import Meter
|
|
304
|
+
llm_count = Meter.get_and_reset()
|
|
305
|
+
if llm_count > 0:
|
|
306
|
+
result += '|LLM:{}'.format(llm_count)
|
|
307
|
+
datas = [result]
|
|
308
|
+
cls.send_packet(PacketTypeEnum.ACTIVE_STATS, None, datas)
|
|
309
|
+
return
|
|
310
|
+
elif param_id == ParamDef.GET_DBCONN_POOL:
|
|
311
|
+
|
|
312
|
+
stats = TraceContextManager.getDBConnPool()
|
|
313
|
+
if stats:
|
|
314
|
+
pid = os.getpid()
|
|
315
|
+
strdbpool = '{}|{{}}|{{}}|{{}}'.format(pid)
|
|
316
|
+
datas = [','.join([strdbpool.format( k,act,inact) for k,(act,inact) in stats.items()])]
|
|
317
|
+
cls.send_packet(PacketTypeEnum.DBCONN_POOL, None, datas)
|
|
318
|
+
|
|
319
|
+
return
|
|
320
|
+
|
|
321
|
+
cls.send_packet(PacketTypeEnum.TX_PARAM, None, datas)
|
|
322
|
+
|
|
323
|
+
@classmethod
|
|
324
|
+
def send_relaypack(cls, packbytes):
|
|
325
|
+
packet_type = PacketTypeEnum.RELAY_PACK
|
|
326
|
+
import whatap.net.udp_thread
|
|
327
|
+
try:
|
|
328
|
+
if sum( [ len(x) for x in cls.buffer_arr]) + PacketEnum.PACKET_BODY_REQUIRED_SIZE + 1 > PacketEnum.PACKET_BUFFER_SIZE:
|
|
329
|
+
cls.send(packet_type, None)
|
|
330
|
+
|
|
331
|
+
if not packbytes:
|
|
332
|
+
return
|
|
333
|
+
|
|
334
|
+
dout = DataOutputX()
|
|
335
|
+
|
|
336
|
+
# header
|
|
337
|
+
dout.writeByte(packet_type)
|
|
338
|
+
dout.writeInt(PacketEnum.PACKET_VERSION)
|
|
339
|
+
dout.writeIntBytes(packbytes)
|
|
340
|
+
|
|
341
|
+
with cls.thread_lock:
|
|
342
|
+
cls.buffer_arr.append(dout.toByteArray())
|
|
343
|
+
except Exception as e:
|
|
344
|
+
print(e)
|
|
345
|
+
logging.debug(e, extra={'id': 'WA262'}, exc_info=True)
|
|
346
|
+
|
|
347
|
+
@classmethod
|
|
348
|
+
def send_llm_relaypack(cls, packbytes):
|
|
349
|
+
"""LLM 데이터를 LLM Go Agent 소켓으로 버퍼링 후 전송. 48KB 누적 시 자동 flush.
|
|
350
|
+
명시적 flush는 flush_llm_buffer()를 사용한다."""
|
|
351
|
+
if not packbytes:
|
|
352
|
+
return
|
|
353
|
+
if not cls.llm_s:
|
|
354
|
+
llm_port = int(getattr(conf, 'llm_net_udp_port', 0))
|
|
355
|
+
if llm_port:
|
|
356
|
+
cls.udp_llm()
|
|
357
|
+
if not cls.llm_s:
|
|
358
|
+
return
|
|
359
|
+
try:
|
|
360
|
+
dout = DataOutputX()
|
|
361
|
+
dout.writeByte(PacketTypeEnum.RELAY_PACK)
|
|
362
|
+
dout.writeInt(PacketEnum.PACKET_VERSION)
|
|
363
|
+
dout.writeIntBytes(packbytes)
|
|
364
|
+
entry = dout.toByteArray()
|
|
365
|
+
|
|
366
|
+
with cls.llm_thread_lock:
|
|
367
|
+
current_size = sum(len(x) for x in cls.llm_buffer_arr)
|
|
368
|
+
if cls.llm_buffer_arr and current_size + len(entry) > PacketEnum.PACKET_BUFFER_SIZE:
|
|
369
|
+
cls._flush_llm_buffer_locked()
|
|
370
|
+
cls.llm_buffer_arr.append(entry)
|
|
371
|
+
except Exception as e:
|
|
372
|
+
logging.debug(e, extra={'id': 'WA921'}, exc_info=True)
|
|
373
|
+
|
|
374
|
+
@classmethod
|
|
375
|
+
def flush_llm_buffer(cls):
|
|
376
|
+
"""LLM 버퍼에 남아있는 데이터를 즉시 전송한다. idle/트랜잭션 경계에서 호출."""
|
|
377
|
+
if not cls.llm_s:
|
|
378
|
+
return
|
|
379
|
+
with cls.llm_thread_lock:
|
|
380
|
+
cls._flush_llm_buffer_locked()
|
|
381
|
+
|
|
382
|
+
@classmethod
|
|
383
|
+
def _flush_llm_buffer_locked(cls):
|
|
384
|
+
"""llm_thread_lock을 이미 보유한 상태에서 호출되어야 한다."""
|
|
385
|
+
if not cls.llm_buffer_arr:
|
|
386
|
+
return
|
|
387
|
+
sendbuf = b''.join(cls.llm_buffer_arr)
|
|
388
|
+
cls.llm_buffer_arr = []
|
|
389
|
+
try:
|
|
390
|
+
cls.llm_s.send(sendbuf)
|
|
391
|
+
except ConnectionRefusedError:
|
|
392
|
+
cls.udp_llm()
|
|
393
|
+
except Exception as e:
|
|
394
|
+
logging.debug(e, extra={'id': 'WA921'}, exc_info=True)
|
whatap/net/udp_thread.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import threading, time
|
|
2
|
+
import logging as logging_module
|
|
3
|
+
|
|
4
|
+
logging = logging_module.getLogger(__name__)
|
|
5
|
+
|
|
6
|
+
def startWhatapThread():
|
|
7
|
+
def __whatap_jobs():
|
|
8
|
+
from whatap.conf.configure import Configure as conf
|
|
9
|
+
from whatap.net.udp_session import UdpSession
|
|
10
|
+
UdpSession.read_timeout = conf.session_read_timeout
|
|
11
|
+
UdpSession.udp()
|
|
12
|
+
# LLM Go Agent UDP 소켓 초기화
|
|
13
|
+
if getattr(conf, 'llm_enabled', False):
|
|
14
|
+
llm_port = int(getattr(conf, 'llm_net_udp_port', 0))
|
|
15
|
+
if llm_port:
|
|
16
|
+
UdpSession.udp_llm()
|
|
17
|
+
|
|
18
|
+
def __udp_recv():
|
|
19
|
+
UdpSession.get()
|
|
20
|
+
|
|
21
|
+
def __config_load():
|
|
22
|
+
now = time.time()
|
|
23
|
+
if now - conf.last_loaded > conf.init_load_interval / 1000:
|
|
24
|
+
conf.init(False)
|
|
25
|
+
|
|
26
|
+
while True:
|
|
27
|
+
for job in (__udp_recv, __config_load):
|
|
28
|
+
try:
|
|
29
|
+
job()
|
|
30
|
+
time.sleep(0)
|
|
31
|
+
except Exception as e:
|
|
32
|
+
if conf.debug:
|
|
33
|
+
logging.debug(e, extra={'id': 'WA914'}, exc_info=True)
|
|
34
|
+
time.sleep(0.05)
|
|
35
|
+
|
|
36
|
+
t = threading.Thread(target=__whatap_jobs)
|
|
37
|
+
t.daemon = True
|
|
38
|
+
t.start()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def __debug():
|
|
42
|
+
from whatap.conf.configure import Configure as conf
|
|
43
|
+
import whatap.util.debug_util as debug_util
|
|
44
|
+
while True:
|
|
45
|
+
|
|
46
|
+
if conf.debug_stack_enabled:
|
|
47
|
+
debug_util.printAllStack()
|
|
48
|
+
time.sleep(10)
|
|
49
|
+
t = threading.Thread(target=__debug)
|
|
50
|
+
t.daemon = True
|
|
51
|
+
t.start()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
startWhatapThread()
|
whatap/pack/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from whatap.pack.pack import Pack
|
|
2
|
+
from whatap.pack.pack_enum import PackEnum
|
|
3
|
+
from whatap.value.map_value import MapValue
|
|
4
|
+
from whatap.io.data_outputx import DataOutputX
|
|
5
|
+
from whatap.util.hash_util import HashUtil as hashutil
|
|
6
|
+
|
|
7
|
+
class LogSinkPack(Pack):
|
|
8
|
+
def __init__(self):
|
|
9
|
+
super(LogSinkPack, self).__init__()
|
|
10
|
+
self.tagHash = 0 # int64
|
|
11
|
+
self.tags = MapValue()
|
|
12
|
+
self.line = 0 # int64
|
|
13
|
+
self.content = None # string
|
|
14
|
+
self.fields = MapValue()
|
|
15
|
+
|
|
16
|
+
def getPackType(self):
|
|
17
|
+
return PackEnum.LOGSINK
|
|
18
|
+
|
|
19
|
+
def write(self, dout):
|
|
20
|
+
super(LogSinkPack, self).write(dout)
|
|
21
|
+
|
|
22
|
+
dout.writeByte(0)
|
|
23
|
+
dout.writeText(self.Category)
|
|
24
|
+
if self.tagHash == 0 and self.tags.size() > 0:
|
|
25
|
+
tagHash, tagBytes = self.resetTagHash()
|
|
26
|
+
self.tagHash = tagHash
|
|
27
|
+
dout.writeDecimal(tagHash)
|
|
28
|
+
dout.write(tagBytes)
|
|
29
|
+
else:
|
|
30
|
+
dout.writeDecimal(self.tagHash)
|
|
31
|
+
dout.writeValue(self.tags)
|
|
32
|
+
dout.writeDecimal(self.line)
|
|
33
|
+
dout.writeText(self.content)
|
|
34
|
+
if self.fields and self.fields.size() > 0:
|
|
35
|
+
dout.writeBoolean(True)
|
|
36
|
+
dout.writeValue(self.fields)
|
|
37
|
+
else:
|
|
38
|
+
dout.writeBoolean(False)
|
|
39
|
+
|
|
40
|
+
def resetTagHash(self) :
|
|
41
|
+
dout = DataOutputX()
|
|
42
|
+
dout.writeValue(self.tags)
|
|
43
|
+
tagBytes = dout.toByteArray()
|
|
44
|
+
tagHash = hashutil.hash(tagBytes)
|
|
45
|
+
return tagHash, tagBytes
|
|
46
|
+
|
|
47
|
+
def read(self, din):
|
|
48
|
+
super(LogSinkPack, self).read(din)
|
|
49
|
+
din.readByte()
|
|
50
|
+
self.Category = din.readText()
|
|
51
|
+
self.tagHash = din.ReadDecimal()
|
|
52
|
+
self.tags = din.readValue()
|
|
53
|
+
self.line = din.readDecimal()
|
|
54
|
+
self.content = din.readText()
|
|
55
|
+
if din.readBool():
|
|
56
|
+
self.fields = din.readValue()
|
|
57
|
+
|
|
58
|
+
return self
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def getLogSinkPack( t = 0,
|
|
62
|
+
category = None,
|
|
63
|
+
tags = {},
|
|
64
|
+
fields = {},
|
|
65
|
+
line=0,
|
|
66
|
+
content = None):
|
|
67
|
+
pack = LogSinkPack()
|
|
68
|
+
pack.time = t
|
|
69
|
+
pack.Category = category
|
|
70
|
+
for k, v in tags.items():
|
|
71
|
+
pack.tags.putString(k, str(v))
|
|
72
|
+
for k, v in fields.items():
|
|
73
|
+
pack.fields.putString(k, str(v))
|
|
74
|
+
pack.line = line
|
|
75
|
+
pack.content = content
|
|
76
|
+
return pack
|
|
77
|
+
|
whatap/pack/pack.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Pack(object):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self.pcode = 0
|
|
7
|
+
self.oid = 0
|
|
8
|
+
self.time = 0
|
|
9
|
+
|
|
10
|
+
def __repr__(self):
|
|
11
|
+
to_str = '{0}: '.format(type(self).__name__)
|
|
12
|
+
|
|
13
|
+
data = self.__dict__.copy()
|
|
14
|
+
for key, value in data.items():
|
|
15
|
+
if key == 'time':
|
|
16
|
+
value = datetime.fromtimestamp(value / 1000) \
|
|
17
|
+
.strftime('%Y-%m-%d %H:%M:%S')
|
|
18
|
+
|
|
19
|
+
to_str += '{0}={1}, '.format(key, value)
|
|
20
|
+
return to_str
|
|
21
|
+
|
|
22
|
+
def getPackType(self):
|
|
23
|
+
return 0
|
|
24
|
+
|
|
25
|
+
def write(self, dout):
|
|
26
|
+
dout.writeDecimal(self.pcode)
|
|
27
|
+
dout.writeInt(self.oid)
|
|
28
|
+
dout.writeLong(self.time)
|
|
29
|
+
|
|
30
|
+
def read(self, din):
|
|
31
|
+
self.pcode = din.readDecimal()
|
|
32
|
+
self.oid = din.readInt()
|
|
33
|
+
self.time = din.readLong()
|
|
34
|
+
return self
|
whatap/pack/pack_enum.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class PackEnum(object):
|
|
2
|
+
PARAMETER = 0x0100
|
|
3
|
+
COUNTER = 0x0200
|
|
4
|
+
COUNTER_1 = 0x0201
|
|
5
|
+
PROFILE = 0x0300
|
|
6
|
+
ACTIVESTACK = 0x0400
|
|
7
|
+
TEXT = 0x0700
|
|
8
|
+
ERROR_SNAP = 0x0800
|
|
9
|
+
ERROR_SNAP_1 = 0x0801
|
|
10
|
+
REALTIME_USER = 0x0f00
|
|
11
|
+
REALTIME_USER_1 = 0x0f01
|
|
12
|
+
|
|
13
|
+
STAT_SERVICE = 0x0900
|
|
14
|
+
STAT_GENERAL = 0x0910
|
|
15
|
+
STAT_SQL = 0x0a00
|
|
16
|
+
STAT_HTTPC = 0x0b00
|
|
17
|
+
STAT_ERROR = 0x0c00
|
|
18
|
+
STAT_METHOD = 0x0e00
|
|
19
|
+
STAT_TOP_SERVICE = 0x1000
|
|
20
|
+
STAT_REMOTE_IP = 0x1100
|
|
21
|
+
STAT_USER_AGENT = 0x1200
|
|
22
|
+
STAT_USER_AGENT_1 = 0x1201
|
|
23
|
+
|
|
24
|
+
EVENT = 0x1400
|
|
25
|
+
HITMAP = 0x1500
|
|
26
|
+
HITMAP_1 = 0x1501
|
|
27
|
+
EXTENSION = 0x1600
|
|
28
|
+
|
|
29
|
+
COMPOSITE = 0x1700
|
|
30
|
+
|
|
31
|
+
LOGSINK = 0x170a
|
|
32
|
+
TAG_COUNT = 0x1601
|
|
33
|
+
@staticmethod
|
|
34
|
+
def create(p):
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
class EventLevel(object):
|
|
38
|
+
FATAL = 30
|
|
39
|
+
WARNING = 20
|
|
40
|
+
INFO = 10
|
|
41
|
+
NONE = 0
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from whatap.pack.pack import Pack
|
|
2
|
+
from whatap.pack.pack_enum import PackEnum
|
|
3
|
+
from whatap.value.map_value import MapValue
|
|
4
|
+
from whatap.io.data_outputx import DataOutputX
|
|
5
|
+
from whatap.util.hash_util import HashUtil as hashutil
|
|
6
|
+
|
|
7
|
+
class TagCountPack(Pack):
|
|
8
|
+
def __init__(self):
|
|
9
|
+
super(TagCountPack, self).__init__()
|
|
10
|
+
self.tagHash = 0 # int64
|
|
11
|
+
self.tags = MapValue()
|
|
12
|
+
self.fields = MapValue()
|
|
13
|
+
|
|
14
|
+
def getPackType(self):
|
|
15
|
+
return PackEnum.TAG_COUNT
|
|
16
|
+
|
|
17
|
+
def write(self, dout):
|
|
18
|
+
super(TagCountPack, self).write(dout)
|
|
19
|
+
|
|
20
|
+
dout.writeByte(0)
|
|
21
|
+
dout.writeText(self.Category)
|
|
22
|
+
if self.tagHash == 0 and self.tags.size() > 0:
|
|
23
|
+
tagHash, tagBytes = self.resetTagHash()
|
|
24
|
+
self.tagHash = tagHash
|
|
25
|
+
dout.writeDecimal(tagHash)
|
|
26
|
+
dout.write(tagBytes)
|
|
27
|
+
else:
|
|
28
|
+
dout.writeDecimal(self.tagHash)
|
|
29
|
+
dout.writeValue(self.tags)
|
|
30
|
+
dout.writeValue(self.fields)
|
|
31
|
+
|
|
32
|
+
def resetTagHash(self) :
|
|
33
|
+
dout = DataOutputX()
|
|
34
|
+
dout.writeValue(self.tags)
|
|
35
|
+
tagBytes = dout.toByteArray()
|
|
36
|
+
tagHash = hashutil.hash(tagBytes)
|
|
37
|
+
return tagHash, tagBytes
|
|
38
|
+
|
|
39
|
+
def read(self, din):
|
|
40
|
+
super(TagCountPack, self).read(din)
|
|
41
|
+
din.readByte()
|
|
42
|
+
self.Category = din.readText()
|
|
43
|
+
self.tagHash = din.ReadDecimal()
|
|
44
|
+
self.tags = din.readValue()
|
|
45
|
+
self.fields = din.readValue()
|
|
46
|
+
|
|
47
|
+
return self
|
|
48
|
+
|
|
49
|
+
def getTagCountPack( t = 0,
|
|
50
|
+
category = None,
|
|
51
|
+
tags = {},
|
|
52
|
+
fields = {}):
|
|
53
|
+
pack = TagCountPack()
|
|
54
|
+
pack.time = t
|
|
55
|
+
pack.Category = category
|
|
56
|
+
for k, v in tags.items():
|
|
57
|
+
pack.tags.putAuto(k, v)
|
|
58
|
+
for k, v in fields.items():
|
|
59
|
+
pack.fields.putAuto(k, v)
|
|
60
|
+
return pack
|
|
61
|
+
|