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