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.
Files changed (227) hide show
  1. whatap/LICENSE +0 -0
  2. whatap/README.rst +49 -0
  3. whatap/__init__.py +923 -0
  4. whatap/__main__.py +4 -0
  5. whatap/agent/darwin/amd64/whatap_python +0 -0
  6. whatap/agent/darwin/arm64/whatap_python +0 -0
  7. whatap/agent/linux/amd64/whatap_python +0 -0
  8. whatap/agent/linux/arm64/whatap_python +0 -0
  9. whatap/agent/windows/whatap_python.exe +0 -0
  10. whatap/bootstrap/__init__.py +0 -0
  11. whatap/bootstrap/sitecustomize.py +19 -0
  12. whatap/build.py +4 -0
  13. whatap/conf/__init__.py +0 -0
  14. whatap/conf/configuration.py +280 -0
  15. whatap/conf/configure.py +105 -0
  16. whatap/conf/license.py +49 -0
  17. whatap/control/__init__.py +0 -0
  18. whatap/counter/__init__.py +14 -0
  19. whatap/counter/counter_manager.py +45 -0
  20. whatap/counter/tasks/__init__.py +3 -0
  21. whatap/counter/tasks/base_task.py +26 -0
  22. whatap/counter/tasks/llm_evaluator_task.py +501 -0
  23. whatap/counter/tasks/llm_log_sink_task.py +309 -0
  24. whatap/counter/tasks/llm_stat_task.py +78 -0
  25. whatap/counter/tasks/openfiledescriptor.py +67 -0
  26. whatap/io/__init__.py +1 -0
  27. whatap/io/data_inputx.py +161 -0
  28. whatap/io/data_outputx.py +262 -0
  29. whatap/llm/__init__.py +17 -0
  30. whatap/llm/definitions.py +43 -0
  31. whatap/llm/evaluators/__init__.py +136 -0
  32. whatap/llm/evaluators/base.py +114 -0
  33. whatap/llm/evaluators/builtins/__init__.py +91 -0
  34. whatap/llm/evaluators/builtins/answer_relevance.py +46 -0
  35. whatap/llm/evaluators/builtins/combined_judge.py +271 -0
  36. whatap/llm/evaluators/builtins/factuality.py +71 -0
  37. whatap/llm/evaluators/builtins/hallucination.py +97 -0
  38. whatap/llm/evaluators/builtins/llm_judge.py +516 -0
  39. whatap/llm/evaluators/builtins/pii_leak.py +214 -0
  40. whatap/llm/evaluators/builtins/prompt_injection.py +71 -0
  41. whatap/llm/evaluators/builtins/toxicity.py +53 -0
  42. whatap/llm/evaluators/builtins/url_scan.py +194 -0
  43. whatap/llm/evaluators/registry.py +192 -0
  44. whatap/llm/evaluators/sampler.py +83 -0
  45. whatap/llm/evaluators/scope.py +334 -0
  46. whatap/llm/features.py +66 -0
  47. whatap/llm/log_sink_packs/__init__.py +9 -0
  48. whatap/llm/log_sink_packs/llm_input_message.py +16 -0
  49. whatap/llm/log_sink_packs/llm_log_sink_pack.py +72 -0
  50. whatap/llm/log_sink_packs/llm_output_message.py +19 -0
  51. whatap/llm/log_sink_packs/llm_step_eval_status.py +94 -0
  52. whatap/llm/log_sink_packs/llm_step_status.py +118 -0
  53. whatap/llm/log_sink_packs/llm_system_message.py +16 -0
  54. whatap/llm/log_sink_packs/llm_tool_calls.py +44 -0
  55. whatap/llm/log_sink_packs/llm_tool_results.py +16 -0
  56. whatap/llm/log_sink_packs/llm_tx_status.py +108 -0
  57. whatap/llm/pricing.py +236 -0
  58. whatap/llm/prompt_meta.py +288 -0
  59. whatap/llm/providers/__init__.py +0 -0
  60. whatap/llm/providers/anthropic/__init__.py +37 -0
  61. whatap/llm/providers/anthropic/messages/__init__.py +0 -0
  62. whatap/llm/providers/anthropic/messages/messages.py +70 -0
  63. whatap/llm/providers/anthropic/messages/messages_context.py +76 -0
  64. whatap/llm/providers/anthropic/messages/messages_extractor.py +126 -0
  65. whatap/llm/providers/interceptor.py +182 -0
  66. whatap/llm/providers/openai/__init__.py +133 -0
  67. whatap/llm/providers/openai/chat/__init__.py +0 -0
  68. whatap/llm/providers/openai/chat/chat.py +82 -0
  69. whatap/llm/providers/openai/chat/chat_context.py +78 -0
  70. whatap/llm/providers/openai/chat/chat_extractor.py +127 -0
  71. whatap/llm/providers/openai/completions/__init__.py +0 -0
  72. whatap/llm/providers/openai/completions/completions.py +70 -0
  73. whatap/llm/providers/openai/completions/completions_context.py +31 -0
  74. whatap/llm/providers/openai/completions/completions_extractor.py +61 -0
  75. whatap/llm/providers/openai/content_parser.py +41 -0
  76. whatap/llm/providers/openai/embeddings/__init__.py +0 -0
  77. whatap/llm/providers/openai/embeddings/embeddings.py +59 -0
  78. whatap/llm/providers/openai/embeddings/embeddings_context.py +25 -0
  79. whatap/llm/providers/openai/embeddings/embeddings_extractor.py +26 -0
  80. whatap/llm/providers/openai/responses/__init__.py +0 -0
  81. whatap/llm/providers/openai/responses/responses.py +70 -0
  82. whatap/llm/providers/openai/responses/responses_context.py +88 -0
  83. whatap/llm/providers/openai/responses/responses_extractor.py +126 -0
  84. whatap/llm/providers/stream_accumulator.py +73 -0
  85. whatap/llm/stats/__init__.py +35 -0
  86. whatap/llm/stats/active_stat.py +86 -0
  87. whatap/llm/stats/answer_relevance_eval_stat.py +10 -0
  88. whatap/llm/stats/api_status_stat.py +35 -0
  89. whatap/llm/stats/base_stat.py +107 -0
  90. whatap/llm/stats/combined_judge_eval_stat.py +11 -0
  91. whatap/llm/stats/error_stat.py +59 -0
  92. whatap/llm/stats/eval_stat.py +225 -0
  93. whatap/llm/stats/factuality_eval_stat.py +10 -0
  94. whatap/llm/stats/feature_stat.py +104 -0
  95. whatap/llm/stats/finish_stat.py +105 -0
  96. whatap/llm/stats/hallucination_eval_stat.py +10 -0
  97. whatap/llm/stats/meter.py +18 -0
  98. whatap/llm/stats/perf_stat.py +117 -0
  99. whatap/llm/stats/pii_leak_eval_stat.py +12 -0
  100. whatap/llm/stats/prompt_injection_eval_stat.py +10 -0
  101. whatap/llm/stats/token_usage_stat.py +133 -0
  102. whatap/llm/stats/toxicity_eval_stat.py +10 -0
  103. whatap/llm/stats/url_scan_eval_stat.py +12 -0
  104. whatap/net/__init__.py +0 -0
  105. whatap/net/async_sender.py +107 -0
  106. whatap/net/packet_enum.py +44 -0
  107. whatap/net/packet_type_enum.py +31 -0
  108. whatap/net/param_def.py +69 -0
  109. whatap/net/stackhelper.py +87 -0
  110. whatap/net/udp_session.py +394 -0
  111. whatap/net/udp_thread.py +54 -0
  112. whatap/pack/__init__.py +0 -0
  113. whatap/pack/logSinkPack.py +77 -0
  114. whatap/pack/pack.py +34 -0
  115. whatap/pack/pack_enum.py +41 -0
  116. whatap/pack/tagCountPack.py +61 -0
  117. whatap/scripts/__init__.py +208 -0
  118. whatap/trace/__init__.py +12 -0
  119. whatap/trace/mod/__init__.py +0 -0
  120. whatap/trace/mod/amqp/__init__.py +0 -0
  121. whatap/trace/mod/amqp/kombu.py +122 -0
  122. whatap/trace/mod/amqp/pika.py +62 -0
  123. whatap/trace/mod/application/__init__.py +0 -0
  124. whatap/trace/mod/application/bottle.py +34 -0
  125. whatap/trace/mod/application/celery.py +81 -0
  126. whatap/trace/mod/application/cherrypy.py +30 -0
  127. whatap/trace/mod/application/django.py +287 -0
  128. whatap/trace/mod/application/django_asgi.py +266 -0
  129. whatap/trace/mod/application/django_py3.py +251 -0
  130. whatap/trace/mod/application/fastapi/__init__.py +31 -0
  131. whatap/trace/mod/application/fastapi/endpoint.py +73 -0
  132. whatap/trace/mod/application/fastapi/exception_log.py +63 -0
  133. whatap/trace/mod/application/fastapi/instrumentation.py +204 -0
  134. whatap/trace/mod/application/fastapi/scope.py +115 -0
  135. whatap/trace/mod/application/fastapi/transaction.py +67 -0
  136. whatap/trace/mod/application/flask.py +52 -0
  137. whatap/trace/mod/application/frappe.py +224 -0
  138. whatap/trace/mod/application/graphql.py +170 -0
  139. whatap/trace/mod/application/nameko.py +39 -0
  140. whatap/trace/mod/application/odoo.py +63 -0
  141. whatap/trace/mod/application/starlette.py +126 -0
  142. whatap/trace/mod/application/tornado.py +163 -0
  143. whatap/trace/mod/application/wsgi.py +195 -0
  144. whatap/trace/mod/database/__init__.py +0 -0
  145. whatap/trace/mod/database/cxoracle.py +49 -0
  146. whatap/trace/mod/database/mongo.py +169 -0
  147. whatap/trace/mod/database/mysql.py +80 -0
  148. whatap/trace/mod/database/neo4j.py +90 -0
  149. whatap/trace/mod/database/psycopg2.py +45 -0
  150. whatap/trace/mod/database/psycopg3.py +359 -0
  151. whatap/trace/mod/database/redis.py +122 -0
  152. whatap/trace/mod/database/sqlalchemy.py +213 -0
  153. whatap/trace/mod/database/sqlite3.py +130 -0
  154. whatap/trace/mod/database/util.py +630 -0
  155. whatap/trace/mod/email/__init__.py +0 -0
  156. whatap/trace/mod/email/smtp.py +78 -0
  157. whatap/trace/mod/httpc/__init__.py +0 -0
  158. whatap/trace/mod/httpc/django.py +31 -0
  159. whatap/trace/mod/httpc/httplib.py +70 -0
  160. whatap/trace/mod/httpc/httpx.py +62 -0
  161. whatap/trace/mod/httpc/requests.py +20 -0
  162. whatap/trace/mod/httpc/urllib3.py +27 -0
  163. whatap/trace/mod/httpc/util.py +388 -0
  164. whatap/trace/mod/logging.py +161 -0
  165. whatap/trace/mod/plugin.py +84 -0
  166. whatap/trace/mod/standalone/__init__.py +0 -0
  167. whatap/trace/mod/standalone/multiple.py +293 -0
  168. whatap/trace/mod/standalone/single.py +135 -0
  169. whatap/trace/simple_trace_context.py +18 -0
  170. whatap/trace/trace_context.py +212 -0
  171. whatap/trace/trace_context_manager.py +244 -0
  172. whatap/trace/trace_error.py +84 -0
  173. whatap/trace/trace_handler.py +89 -0
  174. whatap/trace/trace_import.py +91 -0
  175. whatap/trace/trace_module_definition.py +156 -0
  176. whatap/util/__init__.py +0 -0
  177. whatap/util/bit_util.py +49 -0
  178. whatap/util/cardinality/__init__.py +0 -0
  179. whatap/util/cardinality/hyperloglog.py +84 -0
  180. whatap/util/cardinality/murmurhash.py +20 -0
  181. whatap/util/cardinality/registerset.py +60 -0
  182. whatap/util/compare_util.py +19 -0
  183. whatap/util/date_util.py +55 -0
  184. whatap/util/debug_util.py +73 -0
  185. whatap/util/escape_literal_sql.py +233 -0
  186. whatap/util/frame_util.py +20 -0
  187. whatap/util/hash_util.py +103 -0
  188. whatap/util/hexa32.py +66 -0
  189. whatap/util/int_set.py +199 -0
  190. whatap/util/ip_util.py +63 -0
  191. whatap/util/keygen.py +11 -0
  192. whatap/util/linked_list.py +113 -0
  193. whatap/util/linked_map.py +359 -0
  194. whatap/util/metering_util.py +103 -0
  195. whatap/util/request_double_queue.py +68 -0
  196. whatap/util/request_queue.py +60 -0
  197. whatap/util/string_util.py +20 -0
  198. whatap/util/throttle_util.py +99 -0
  199. whatap/util/userid_util.py +134 -0
  200. whatap/value/__init__.py +1 -0
  201. whatap/value/blob_value.py +38 -0
  202. whatap/value/boolean_value.py +33 -0
  203. whatap/value/decimal_value.py +36 -0
  204. whatap/value/double_summary.py +86 -0
  205. whatap/value/double_value.py +33 -0
  206. whatap/value/float_array.py +42 -0
  207. whatap/value/float_value.py +34 -0
  208. whatap/value/int_array.py +42 -0
  209. whatap/value/ip4_value.py +50 -0
  210. whatap/value/list_value.py +105 -0
  211. whatap/value/long_array.py +44 -0
  212. whatap/value/long_summary.py +83 -0
  213. whatap/value/map_value.py +154 -0
  214. whatap/value/null_value.py +21 -0
  215. whatap/value/number_value.py +33 -0
  216. whatap/value/summary_value.py +39 -0
  217. whatap/value/text_array.py +58 -0
  218. whatap/value/text_hash_value.py +37 -0
  219. whatap/value/text_value.py +43 -0
  220. whatap/value/value.py +26 -0
  221. whatap/value/value_enum.py +80 -0
  222. whatap/whatap.conf +14 -0
  223. whatap_python-2.1.0.dist-info/METADATA +87 -0
  224. whatap_python-2.1.0.dist-info/RECORD +227 -0
  225. whatap_python-2.1.0.dist-info/WHEEL +5 -0
  226. whatap_python-2.1.0.dist-info/entry_points.txt +6 -0
  227. 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)
@@ -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()
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
@@ -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
+