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,287 @@
1
+ from whatap.conf.configure import Configure as conf
2
+ from whatap.net.packet_type_enum import PacketTypeEnum
3
+ import whatap.net.async_sender as async_sender
4
+ from whatap.trace.trace_handler import trace_handler
5
+ from whatap.trace.trace_error import interceptor_error, interceptor_step_error
6
+ from whatap.trace.mod.application.wsgi import interceptor
7
+ from whatap.trace.trace_context_manager import TraceContextManager
8
+ from whatap.trace.trace_context import TraceContext
9
+ from whatap.util.date_util import DateUtil
10
+ from whatap.util.userid_util import UseridUtil as userid_util
11
+ import whatap.util.throttle_util as throttle_util
12
+ import time
13
+
14
+ def blocking_handler():
15
+ def handler(func):
16
+ from django.http import HttpResponse
17
+ def wrapper(*args, **kwargs):
18
+ if conf.throttle_enabled and args and len(args) > 2:
19
+ remote_ip = userid_util.getRemoteAddr(args)
20
+ req = args[1]
21
+ path = req['PATH_INFO']
22
+ if throttle_util.isblocking(remote_ip, path):
23
+ if conf.reject_event_enabled:
24
+ ctx = TraceContextManager.getLocalContext()
25
+ if not ctx:
26
+ ctx = TraceContext()
27
+
28
+ throttle_util.sendrejectevent(ctx, path, remote_ip)
29
+ if conf.throttle_blocked_forward:
30
+ response = HttpResponse(status=302)
31
+ response['Location'] = conf.throttle_blocked_forward
32
+ return response
33
+ status = '403 Forbidden'
34
+ start_response= args[2]
35
+ start_response(status, [])
36
+ response = HttpResponse(content=conf.throttle_blocked_message, status=403)
37
+ return response
38
+ return func(*args, **kwargs)
39
+
40
+ return wrapper
41
+
42
+ return handler
43
+
44
+ def instrument(module):
45
+ def wrapper(fn):
46
+ @trace_handler(fn, True)
47
+ @blocking_handler()
48
+ def trace(*args, **kwargs):
49
+ django_instance = args[0]
50
+ environ = args[1]
51
+ original_start_response = args[2]
52
+
53
+ def custom_start_response(status, response_headers, exc_info=None):
54
+ ctx = TraceContextManager.getLocalContext()
55
+ ctx.status = status[:3]
56
+ return original_start_response(status, response_headers, exc_info)
57
+
58
+ new_args = (django_instance, environ, custom_start_response)
59
+ callback = interceptor(fn, *new_args, **kwargs)
60
+ return callback
61
+
62
+ return trace
63
+
64
+ module.WSGIHandler.__call__ = wrapper(module.WSGIHandler.__call__)
65
+
66
+ def instrument_asgi(module):
67
+ def wrapper(fn):
68
+ @trace_handler(fn, True)
69
+ @blocking_handler()
70
+ def trace(*args, **kwargs):
71
+ callback = interceptor(fn, *args, **kwargs)
72
+
73
+ return callback
74
+
75
+ return trace
76
+
77
+ module.ASGIHandler.__call__ = wrapper(module.ASGIHandler.__call__)
78
+
79
+ try:
80
+ from whatap.trace.mod.application.django_py3 import \
81
+ instrument_handlers_async, interceptor_async,\
82
+ trace_handler_async, blocking_handler_async,\
83
+ parseHeaders as parseHeadersAsync,\
84
+ WHATAP_CTX
85
+
86
+ django_py3_loaded = True
87
+ except Exception as e:
88
+ print("application.django error:",e)
89
+ django_py3_loaded = False
90
+
91
+ def instrument_handlers_channels(module):
92
+ def wrapper(fn):
93
+ @trace_handler_async(fn, True)
94
+ @blocking_handler_async()
95
+ async def trace(*args, **kwargs):
96
+ if django_py3_loaded:
97
+ callback = await interceptor_async(fn, *args, **kwargs)
98
+ else:
99
+ callback = await fn(*args, **kwargs)
100
+
101
+ return callback
102
+
103
+ return trace
104
+
105
+ module.AsgiHandler.__call__ = wrapper(module.AsgiHandler.__call__)
106
+
107
+
108
+ def instrument_handlers_base(module):
109
+ def wrapper(fn):
110
+ @trace_handler(fn)
111
+ def trace(*args, **kwargs):
112
+ request = args[1]
113
+ ctx = TraceContextManager.getLocalContext()
114
+ callback = fn(*args, **kwargs)
115
+
116
+ if conf.trace_auto_normalize_enabled:
117
+ resolver_match = request.resolver_match
118
+ if resolver_match:
119
+
120
+ path = request.path
121
+ for key, value in resolver_match.kwargs.items():
122
+ path = path.replace(resolver_match.kwargs[key],
123
+ '{' + key + '}')
124
+
125
+ start_time = DateUtil.nowSystem()
126
+ ctx.start_time = start_time
127
+ ctx.service_name = path
128
+
129
+ if hasattr(resolver_match, 'view_name'):
130
+ type_name = 'View' \
131
+ if resolver_match._func_path != resolver_match.view_name \
132
+ else 'Function'
133
+ desc = '{0}: {1}'.format(type_name,
134
+ resolver_match._func_path)
135
+ datas = [' ', ' ', desc]
136
+ ctx.elapsed = DateUtil.nowSystem() - start_time
137
+ async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx,
138
+ datas)
139
+
140
+ return callback
141
+
142
+ return trace
143
+ if hasattr(module.BaseHandler, 'apply_response_fixes'):
144
+ module.BaseHandler.apply_response_fixes = wrapper(
145
+ module.BaseHandler.apply_response_fixes)
146
+
147
+ def wrapper(fn):
148
+ @trace_handler(fn)
149
+ def trace(*args, **kwargs):
150
+ callback = fn(*args, **kwargs)
151
+
152
+ e = args[3]
153
+ status_code = callback.status_code
154
+ errors = [e[0].__name__,
155
+ e[1].args[1] if len(
156
+ e[1].args) > 1 \
157
+ else repr(e[1].args[0])]
158
+
159
+ interceptor_error(status_code, errors)
160
+
161
+ return callback
162
+
163
+ return trace
164
+ if hasattr(module.BaseHandler, 'handle_uncaught_exception'):
165
+ module.BaseHandler.handle_uncaught_exception = wrapper(
166
+ module.BaseHandler.handle_uncaught_exception)
167
+
168
+ def populateLocalContextAsync(*args, **kwargs):
169
+ if len(args) < 2:
170
+ return
171
+ request = args[1]
172
+ if hasattr(request, 'scope') and WHATAP_CTX in request.scope:
173
+ ctx = request.scope[WHATAP_CTX]
174
+ ctx.thread_id = TraceContextManager.getCurrentThreadId()
175
+ TraceContextManager.setLocalContext(ctx)
176
+
177
+ def get_response_wrapper(fn):
178
+ @trace_handler(fn, preload=populateLocalContextAsync)
179
+ def trace(*args, **kwargs):
180
+ request = args[1]
181
+ ctx = TraceContextManager.getLocalContext()
182
+ callback = fn(*args, **kwargs)
183
+
184
+ if ctx and conf.trace_user_enabled:
185
+ if not conf.trace_user_using_ip:
186
+ userid_util.setUserId(request, callback, ctx._rawuserid )
187
+
188
+ return callback
189
+
190
+ return trace
191
+ module.BaseHandler.get_response = get_response_wrapper(
192
+ module.BaseHandler.get_response)
193
+
194
+ def wrapper_async(fn):
195
+ @trace_handler_async(fn)
196
+ async def trace(*args, **kwargs):
197
+ ## asgi applicaiton 에서 threading.local()을 사용할 경우 데이터가 침해 될 수 있다.
198
+ ## contextvars 를 이용하여 현재 실행중인 코루틴 콘텍스트를 유지한다.
199
+ request = args[1]
200
+ scope = request.scope
201
+ ctx = scope.get(WHATAP_CTX)
202
+ callback = await fn(*args, **kwargs)
203
+ ctx.asgi_response = callback
204
+ if ctx and conf.trace_user_enabled:
205
+ if not conf.trace_user_using_ip:
206
+ userid_util.setUserId(request, callback, ctx._rawuserid)
207
+
208
+ return callback
209
+ return trace
210
+ if hasattr(module, 'BaseHandler') and hasattr(module.BaseHandler, 'get_response_async'):
211
+ module.BaseHandler.get_response_async = wrapper_async(module.BaseHandler.get_response_async)
212
+ # if django_py3_loaded:
213
+ # instrument_handlers_async(module)
214
+
215
+ def instrument_generic_base(module):
216
+ def wrapper(fn):
217
+ @trace_handler(fn)
218
+ def trace(*args, **kwargs):
219
+ self = args[0]
220
+ ctx = TraceContextManager.getLocalContext()
221
+ start_time = DateUtil.nowSystem()
222
+ ctx.start_time = start_time
223
+ desc = '{0}.{1}'.format(self.__module__, type(self).__name__)
224
+ datas = [' ', ' ', desc]
225
+
226
+ ctx.elapsed = DateUtil.nowSystem() - start_time
227
+ async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
228
+
229
+ try:
230
+ callback = fn(*args, **kwargs)
231
+ except Exception as e:
232
+ interceptor_step_error(e, ctx = ctx)
233
+ raise e
234
+ return callback
235
+
236
+ return trace
237
+
238
+ module.View.dispatch = wrapper(module.View.dispatch)
239
+
240
+
241
+
242
+
243
+ # Django==1.10
244
+
245
+ def instrument_urls_base(module):
246
+ def wrapper(fn):
247
+ @trace_handler(fn)
248
+ def trace(*args, **kwargs):
249
+ callback = fn(*args, **kwargs)
250
+ return callback
251
+
252
+ return trace
253
+
254
+ module.reverse = wrapper(module.reverse)
255
+
256
+
257
+ def instrument_handlers_exception(module):
258
+ def wrapper(fn):
259
+ @trace_handler(fn)
260
+ def trace(*args, **kwargs):
261
+ print("exception handler called ", args, kwargs)
262
+ callback = fn(*args, **kwargs)
263
+ return callback
264
+
265
+ return trace
266
+
267
+ module.convert_exception_to_response.convert_exception_to_response = wrapper(
268
+ module.convert_exception_to_response.convert_exception_to_response)
269
+
270
+ def instrument_handlers_static(module):
271
+ def get_response_wrapper(fn):
272
+ @trace_handler(fn)
273
+ def trace(*args, **kwargs):
274
+ callback = fn(*args, **kwargs)
275
+
276
+ ctx = TraceContextManager.getLocalContext()
277
+ if ctx:
278
+ ctx.userid = 0
279
+
280
+ return callback
281
+
282
+ return trace
283
+
284
+ module.StaticFilesHandler.get_response = get_response_wrapper(
285
+ module.StaticFilesHandler.get_response)
286
+
287
+
@@ -0,0 +1,266 @@
1
+ import logging as logging_module
2
+ import os
3
+ import traceback
4
+ from whatap.conf.configure import Configure as conf
5
+ from whatap.net import async_sender
6
+ from whatap.trace.trace_context_manager import TraceContextManager
7
+ from whatap.trace.trace_context import TraceContext
8
+ from whatap.trace.mod.application.wsgi import isIgnore, start_interceptor, end_interceptor
9
+ from whatap.util.hash_util import HashUtil as hash_util
10
+ from whatap.util.userid_util import UseridUtil
11
+ from whatap.util.date_util import DateUtil
12
+ from whatap.util.hexa32 import Hexa32 as hexa32
13
+ from whatap.util.keygen import KeyGen
14
+ import whatap.util.throttle_util as throttle_util
15
+ from whatap.net.packet_type_enum import PacketTypeEnum
16
+ from whatap import logging
17
+ import sys
18
+
19
+ from whatap.util.frame_util import get_current_frame
20
+
21
+ SCOPE_ARGS_LENGTH = 2
22
+ HEADER = 'headers'
23
+ PATH = 'path'
24
+ USER_AGENT = 'user-agent'
25
+ REFERER = 'referer'
26
+ CLIENT = 'client'
27
+ COOKIE = 'cookie'
28
+ HOST = 'host'
29
+ QUERY_STRING = 'query_string'
30
+ WHATAP_CTX = '__whatap__ctx'
31
+ logger = logging_module.getLogger(__name__)
32
+
33
+ def blocking_handler_asgi():
34
+ def handler(func):
35
+ from django.http import HttpResponse, HttpResponseRedirect
36
+ async def wrapper(instance, scope, receive, send):
37
+ args = (instance, scope, receive, send)
38
+ scope, headers = parseHeaders(args)
39
+ assert scope, headers
40
+ if conf.throttle_enabled:
41
+ remote_ip = parseRemoteAddr(scope, headers)
42
+ path = scope.get(PATH)
43
+ if throttle_util.isblocking(remote_ip, path):
44
+ if conf.reject_event_enabled:
45
+ ctx = TraceContextManager.getLocalContext()
46
+ if not ctx:
47
+ ctx = TraceContext()
48
+ throttle_util.sendrejectevent(ctx, path, remote_ip)
49
+
50
+ if conf.throttle_blocked_forward:
51
+ response = HttpResponseRedirect(status=302, redirect_to=conf.throttle_blocked_forward)
52
+
53
+ if path == conf.throttle_blocked_forward:
54
+ response = HttpResponse(content=conf.throttle_blocked_message, status=200)
55
+
56
+ return await instance.send_response(response, send)
57
+
58
+ response = HttpResponse(content=conf.throttle_blocked_message, status=403)
59
+ return await instance.send_response(response, send)
60
+ return await func(instance, scope, receive, send)
61
+ return wrapper
62
+ return handler
63
+
64
+ def trace_handler_asgi(fn):
65
+ def handler(func):
66
+ async def wrapper(instance, scope, receive, send):
67
+ if scope["type"] != "http":
68
+ await fn(instance, scope, receive, send)
69
+ try:
70
+ await func(instance, scope, receive, send)
71
+ except Exception as e:
72
+ logging.debug(e, extra={'id': 'WA917'}, exc_info=True)
73
+ print(e, dict(extra={'id': 'WA917'}, exc_info=True))
74
+ import traceback
75
+ traceback.print_exc()
76
+ return wrapper
77
+ return handler
78
+
79
+ def trace_handler_async(fn):
80
+ def handler(func):
81
+ async def wrapper(*args, **kwargs):
82
+ try:
83
+ response = await func(*args, **kwargs)
84
+ except Exception as e:
85
+ logging.debug(e, extra={'id': 'WA917'}, exc_info=True)
86
+ print(e, dict(extra={'id': 'WA917'}, exc_info=True))
87
+ import traceback
88
+ traceback.print_exc()
89
+ return await fn(*args, **kwargs)
90
+ else:
91
+ return response
92
+ return wrapper
93
+ return handler
94
+
95
+ def parseServiceName(environ):
96
+ return environ.get('PATH_INFO', '')
97
+
98
+ def parseHeaders(args):
99
+ headers = {}
100
+ if len(args) > SCOPE_ARGS_LENGTH:
101
+ scope = args[1]
102
+ if HEADER in scope:
103
+ for arg in scope[HEADER]:
104
+ headers[str(arg[0].decode()).lower()] = str(arg[1].decode())
105
+
106
+ return scope, headers
107
+ return None, None
108
+
109
+ def parseRemoteAddr(scope, headers):
110
+ remoteIp = ''
111
+ if CLIENT in scope:
112
+ remoteIp = scope.get(CLIENT)[0]
113
+ if conf.trace_http_client_ip_header_key:
114
+ header_val = headers.get(conf.trace_http_client_ip_header_key, '')
115
+ remoteIp = header_val.split(',')[0].strip()
116
+
117
+ return remoteIp
118
+
119
+ def getUserId(scope, headers, defValue):
120
+ try:
121
+ if conf.user_header_ticket:
122
+ ticket = headers.get(conf.user_header_ticket, "")
123
+ if ticket:
124
+ return hash_util.hashFromString(ticket), ticket
125
+ return 0, ""
126
+ cookie = headers.get(COOKIE, "")
127
+ if cookie:
128
+ if len(cookie) >= conf.trace_user_cookie_limit:
129
+ return hash_util.hashFromString(defValue) if defValue else 0, defValue or ""
130
+
131
+ x1 = cookie.find(UseridUtil.WHATAP_R)
132
+ if x1 >= 0:
133
+ x2 = cookie.find(';', x1)
134
+ if x2 > 0:
135
+ value = cookie[x1 + len(UseridUtil.WHATAP_R) + 1: x2]
136
+ else:
137
+ value = cookie[x1 + len(UseridUtil.WHATAP_R) + 1:]
138
+ return hexa32.toLong32(value), value
139
+ userid = KeyGen.next()
140
+ return userid, hexa32.toString32(userid)
141
+ except Exception:
142
+ exc_type, exc_value, exc_traceback = sys.exc_info()
143
+ logger.debug("A502", 10, str(exc_value))
144
+ return hash_util.hashFromString(defValue) if defValue else 0, defValue or ""
145
+
146
+ def interceptor_error_asgi(ctx, status_code, errors):
147
+ ctx.status = int(status_code/ 100)
148
+ if ctx.status >= 4:
149
+ ctx.error = 1
150
+
151
+ error = ''
152
+ frame = get_current_frame(ctx)
153
+ if not frame:
154
+ return
155
+
156
+ for stack in traceback.extract_stack(frame):
157
+ line = stack[0]
158
+ line_num = stack[1]
159
+ method_name = stack[2]
160
+
161
+ if 'whatap' + os.sep + 'trace' in line or 'threading.py' in line:
162
+ continue
163
+ error += '{} ({}:{})\n'.format(method_name, line, line_num)
164
+
165
+ errors.append(error)
166
+
167
+ # errors.append(''.join(traceback.format_list(traceback.extract_stack(sys._current_frames()[ctx.thread.ident]))))
168
+ async_sender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors)
169
+
170
+ async def interceptor_asgi(fn, *args, **kwargs):
171
+ scope, headers = parseHeaders(args)
172
+ if scope == None and headers == None:
173
+ return await fn(*args, **kwargs)
174
+
175
+ ctx = TraceContext()
176
+ ctx.host = headers.get(HOST, '').split(':')[0]
177
+ ctx.service_name = scope.get(PATH)
178
+
179
+ ctx.remoteIp = parseRemoteAddr(scope, headers)
180
+
181
+ ctx.userAgentString = headers.get(USER_AGENT, '')
182
+ ctx.referer = headers.get(REFERER, '')
183
+
184
+ if conf.trace_user_enabled:
185
+ if conf.trace_user_using_ip:
186
+ ctx.userid = parseRemoteAddr(scope, headers)
187
+ else:
188
+ ctx.userid, ctx._rawuserid = getUserId(scope, headers, ctx.remoteIp)
189
+
190
+ mstt = headers.get(
191
+ conf._trace_mtrace_caller_key.lower().replace('-', '_'), '')
192
+
193
+ if mstt:
194
+ ctx.setTransfer(mstt)
195
+ if conf.stat_mtrace_enabled:
196
+ val = headers.get(
197
+ conf._trace_mtrace_info_key.lower().replace('-', '_'), '')
198
+ if val and len(val):
199
+ ctx.setTransferInfo(val)
200
+ pass
201
+
202
+ myid = headers.get(
203
+ conf._trace_mtrace_callee_key.lower().replace('-', '_'), '')
204
+ if myid:
205
+ ctx.setTxid(myid)
206
+ caller_poid = headers.get(
207
+ conf._trace_mtrace_caller_poid_key.upper().replace('-', '_'), '')
208
+
209
+ if caller_poid:
210
+ ctx.mcaller_poid = caller_poid
211
+
212
+ try:
213
+ if isIgnore(ctx.service_name):
214
+ ctx.is_ignored = True
215
+ return fn(*args, **kwargs)
216
+ except Exception as e:
217
+ pass
218
+
219
+ start_interceptor(ctx)
220
+ try:
221
+ scope[WHATAP_CTX] = ctx
222
+ await fn(*args, **kwargs)
223
+ response = ctx.asgi_response
224
+
225
+ query_string = str(scope.get(QUERY_STRING, ''))
226
+ if query_string:
227
+ ctx.service_name += '?{}'.format(query_string)
228
+
229
+ if ctx.service_name.find('.') > -1 and ctx.service_name.split('.')[
230
+ 1] in conf.web_static_content_extensions:
231
+ ctx.isStaticContents = 'true'
232
+
233
+ if response:
234
+ status_code = response.status_code
235
+ errors = [response.__class__.__name__, response.reason_phrase]
236
+ interceptor_error_asgi(ctx, status_code, errors)
237
+
238
+
239
+ if conf.profile_http_header_enabled:
240
+ keys = []
241
+ for key, value in headers.items():
242
+ keys.append(key)
243
+ keys.sort()
244
+
245
+ text = ''
246
+ for key in keys:
247
+ text += '{}={}\n'.format(key.lower(),
248
+ headers[key])
249
+
250
+ datas = ['HTTP-HEADERS', 'HTTP-HEADERS', text]
251
+ ctx.start_time = DateUtil.nowSystem()
252
+ async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
253
+ finally:
254
+ if ctx:
255
+ end_interceptor(ctx=ctx)
256
+
257
+ def instrument_asgi(module):
258
+ def wrapper(fn):
259
+ @trace_handler_asgi(fn)
260
+ @blocking_handler_asgi()
261
+ async def trace(instance, scope, receive, send):
262
+ await interceptor_asgi(fn, instance, scope, receive, send)
263
+ return trace
264
+
265
+ ### module 있는지 체크 하고 파일을 분리해야함.
266
+ module.ASGIHandler.__call__ = wrapper(module.ASGIHandler.__call__)