whatap-python 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- whatap/LICENSE +0 -0
- whatap/README.rst +49 -0
- whatap/__init__.py +923 -0
- whatap/__main__.py +4 -0
- whatap/agent/darwin/amd64/whatap_python +0 -0
- whatap/agent/darwin/arm64/whatap_python +0 -0
- whatap/agent/linux/amd64/whatap_python +0 -0
- whatap/agent/linux/arm64/whatap_python +0 -0
- whatap/agent/windows/whatap_python.exe +0 -0
- whatap/bootstrap/__init__.py +0 -0
- whatap/bootstrap/sitecustomize.py +19 -0
- whatap/build.py +4 -0
- whatap/conf/__init__.py +0 -0
- whatap/conf/configuration.py +280 -0
- whatap/conf/configure.py +105 -0
- whatap/conf/license.py +49 -0
- whatap/control/__init__.py +0 -0
- whatap/counter/__init__.py +14 -0
- whatap/counter/counter_manager.py +45 -0
- whatap/counter/tasks/__init__.py +3 -0
- whatap/counter/tasks/base_task.py +26 -0
- whatap/counter/tasks/llm_evaluator_task.py +501 -0
- whatap/counter/tasks/llm_log_sink_task.py +309 -0
- whatap/counter/tasks/llm_stat_task.py +78 -0
- whatap/counter/tasks/openfiledescriptor.py +67 -0
- whatap/io/__init__.py +1 -0
- whatap/io/data_inputx.py +161 -0
- whatap/io/data_outputx.py +262 -0
- whatap/llm/__init__.py +17 -0
- whatap/llm/definitions.py +43 -0
- whatap/llm/evaluators/__init__.py +136 -0
- whatap/llm/evaluators/base.py +114 -0
- whatap/llm/evaluators/builtins/__init__.py +91 -0
- whatap/llm/evaluators/builtins/answer_relevance.py +46 -0
- whatap/llm/evaluators/builtins/combined_judge.py +271 -0
- whatap/llm/evaluators/builtins/factuality.py +71 -0
- whatap/llm/evaluators/builtins/hallucination.py +97 -0
- whatap/llm/evaluators/builtins/llm_judge.py +516 -0
- whatap/llm/evaluators/builtins/pii_leak.py +214 -0
- whatap/llm/evaluators/builtins/prompt_injection.py +71 -0
- whatap/llm/evaluators/builtins/toxicity.py +53 -0
- whatap/llm/evaluators/builtins/url_scan.py +194 -0
- whatap/llm/evaluators/registry.py +192 -0
- whatap/llm/evaluators/sampler.py +83 -0
- whatap/llm/evaluators/scope.py +334 -0
- whatap/llm/features.py +66 -0
- whatap/llm/log_sink_packs/__init__.py +9 -0
- whatap/llm/log_sink_packs/llm_input_message.py +16 -0
- whatap/llm/log_sink_packs/llm_log_sink_pack.py +72 -0
- whatap/llm/log_sink_packs/llm_output_message.py +19 -0
- whatap/llm/log_sink_packs/llm_step_eval_status.py +94 -0
- whatap/llm/log_sink_packs/llm_step_status.py +118 -0
- whatap/llm/log_sink_packs/llm_system_message.py +16 -0
- whatap/llm/log_sink_packs/llm_tool_calls.py +44 -0
- whatap/llm/log_sink_packs/llm_tool_results.py +16 -0
- whatap/llm/log_sink_packs/llm_tx_status.py +108 -0
- whatap/llm/pricing.py +236 -0
- whatap/llm/prompt_meta.py +288 -0
- whatap/llm/providers/__init__.py +0 -0
- whatap/llm/providers/anthropic/__init__.py +37 -0
- whatap/llm/providers/anthropic/messages/__init__.py +0 -0
- whatap/llm/providers/anthropic/messages/messages.py +70 -0
- whatap/llm/providers/anthropic/messages/messages_context.py +76 -0
- whatap/llm/providers/anthropic/messages/messages_extractor.py +126 -0
- whatap/llm/providers/interceptor.py +182 -0
- whatap/llm/providers/openai/__init__.py +133 -0
- whatap/llm/providers/openai/chat/__init__.py +0 -0
- whatap/llm/providers/openai/chat/chat.py +82 -0
- whatap/llm/providers/openai/chat/chat_context.py +78 -0
- whatap/llm/providers/openai/chat/chat_extractor.py +127 -0
- whatap/llm/providers/openai/completions/__init__.py +0 -0
- whatap/llm/providers/openai/completions/completions.py +70 -0
- whatap/llm/providers/openai/completions/completions_context.py +31 -0
- whatap/llm/providers/openai/completions/completions_extractor.py +61 -0
- whatap/llm/providers/openai/content_parser.py +41 -0
- whatap/llm/providers/openai/embeddings/__init__.py +0 -0
- whatap/llm/providers/openai/embeddings/embeddings.py +59 -0
- whatap/llm/providers/openai/embeddings/embeddings_context.py +25 -0
- whatap/llm/providers/openai/embeddings/embeddings_extractor.py +26 -0
- whatap/llm/providers/openai/responses/__init__.py +0 -0
- whatap/llm/providers/openai/responses/responses.py +70 -0
- whatap/llm/providers/openai/responses/responses_context.py +88 -0
- whatap/llm/providers/openai/responses/responses_extractor.py +126 -0
- whatap/llm/providers/stream_accumulator.py +73 -0
- whatap/llm/stats/__init__.py +35 -0
- whatap/llm/stats/active_stat.py +86 -0
- whatap/llm/stats/answer_relevance_eval_stat.py +10 -0
- whatap/llm/stats/api_status_stat.py +35 -0
- whatap/llm/stats/base_stat.py +107 -0
- whatap/llm/stats/combined_judge_eval_stat.py +11 -0
- whatap/llm/stats/error_stat.py +59 -0
- whatap/llm/stats/eval_stat.py +225 -0
- whatap/llm/stats/factuality_eval_stat.py +10 -0
- whatap/llm/stats/feature_stat.py +104 -0
- whatap/llm/stats/finish_stat.py +105 -0
- whatap/llm/stats/hallucination_eval_stat.py +10 -0
- whatap/llm/stats/meter.py +18 -0
- whatap/llm/stats/perf_stat.py +117 -0
- whatap/llm/stats/pii_leak_eval_stat.py +12 -0
- whatap/llm/stats/prompt_injection_eval_stat.py +10 -0
- whatap/llm/stats/token_usage_stat.py +133 -0
- whatap/llm/stats/toxicity_eval_stat.py +10 -0
- whatap/llm/stats/url_scan_eval_stat.py +12 -0
- whatap/net/__init__.py +0 -0
- whatap/net/async_sender.py +107 -0
- whatap/net/packet_enum.py +44 -0
- whatap/net/packet_type_enum.py +31 -0
- whatap/net/param_def.py +69 -0
- whatap/net/stackhelper.py +87 -0
- whatap/net/udp_session.py +394 -0
- whatap/net/udp_thread.py +54 -0
- whatap/pack/__init__.py +0 -0
- whatap/pack/logSinkPack.py +77 -0
- whatap/pack/pack.py +34 -0
- whatap/pack/pack_enum.py +41 -0
- whatap/pack/tagCountPack.py +61 -0
- whatap/scripts/__init__.py +208 -0
- whatap/trace/__init__.py +12 -0
- whatap/trace/mod/__init__.py +0 -0
- whatap/trace/mod/amqp/__init__.py +0 -0
- whatap/trace/mod/amqp/kombu.py +122 -0
- whatap/trace/mod/amqp/pika.py +62 -0
- whatap/trace/mod/application/__init__.py +0 -0
- whatap/trace/mod/application/bottle.py +34 -0
- whatap/trace/mod/application/celery.py +81 -0
- whatap/trace/mod/application/cherrypy.py +30 -0
- whatap/trace/mod/application/django.py +287 -0
- whatap/trace/mod/application/django_asgi.py +266 -0
- whatap/trace/mod/application/django_py3.py +251 -0
- whatap/trace/mod/application/fastapi/__init__.py +31 -0
- whatap/trace/mod/application/fastapi/endpoint.py +73 -0
- whatap/trace/mod/application/fastapi/exception_log.py +63 -0
- whatap/trace/mod/application/fastapi/instrumentation.py +204 -0
- whatap/trace/mod/application/fastapi/scope.py +115 -0
- whatap/trace/mod/application/fastapi/transaction.py +67 -0
- whatap/trace/mod/application/flask.py +52 -0
- whatap/trace/mod/application/frappe.py +224 -0
- whatap/trace/mod/application/graphql.py +170 -0
- whatap/trace/mod/application/nameko.py +39 -0
- whatap/trace/mod/application/odoo.py +63 -0
- whatap/trace/mod/application/starlette.py +126 -0
- whatap/trace/mod/application/tornado.py +163 -0
- whatap/trace/mod/application/wsgi.py +195 -0
- whatap/trace/mod/database/__init__.py +0 -0
- whatap/trace/mod/database/cxoracle.py +49 -0
- whatap/trace/mod/database/mongo.py +169 -0
- whatap/trace/mod/database/mysql.py +80 -0
- whatap/trace/mod/database/neo4j.py +90 -0
- whatap/trace/mod/database/psycopg2.py +45 -0
- whatap/trace/mod/database/psycopg3.py +359 -0
- whatap/trace/mod/database/redis.py +122 -0
- whatap/trace/mod/database/sqlalchemy.py +213 -0
- whatap/trace/mod/database/sqlite3.py +130 -0
- whatap/trace/mod/database/util.py +630 -0
- whatap/trace/mod/email/__init__.py +0 -0
- whatap/trace/mod/email/smtp.py +78 -0
- whatap/trace/mod/httpc/__init__.py +0 -0
- whatap/trace/mod/httpc/django.py +31 -0
- whatap/trace/mod/httpc/httplib.py +70 -0
- whatap/trace/mod/httpc/httpx.py +62 -0
- whatap/trace/mod/httpc/requests.py +20 -0
- whatap/trace/mod/httpc/urllib3.py +27 -0
- whatap/trace/mod/httpc/util.py +388 -0
- whatap/trace/mod/logging.py +161 -0
- whatap/trace/mod/plugin.py +84 -0
- whatap/trace/mod/standalone/__init__.py +0 -0
- whatap/trace/mod/standalone/multiple.py +293 -0
- whatap/trace/mod/standalone/single.py +135 -0
- whatap/trace/simple_trace_context.py +18 -0
- whatap/trace/trace_context.py +212 -0
- whatap/trace/trace_context_manager.py +244 -0
- whatap/trace/trace_error.py +84 -0
- whatap/trace/trace_handler.py +89 -0
- whatap/trace/trace_import.py +91 -0
- whatap/trace/trace_module_definition.py +156 -0
- whatap/util/__init__.py +0 -0
- whatap/util/bit_util.py +49 -0
- whatap/util/cardinality/__init__.py +0 -0
- whatap/util/cardinality/hyperloglog.py +84 -0
- whatap/util/cardinality/murmurhash.py +20 -0
- whatap/util/cardinality/registerset.py +60 -0
- whatap/util/compare_util.py +19 -0
- whatap/util/date_util.py +55 -0
- whatap/util/debug_util.py +73 -0
- whatap/util/escape_literal_sql.py +233 -0
- whatap/util/frame_util.py +20 -0
- whatap/util/hash_util.py +103 -0
- whatap/util/hexa32.py +66 -0
- whatap/util/int_set.py +199 -0
- whatap/util/ip_util.py +63 -0
- whatap/util/keygen.py +11 -0
- whatap/util/linked_list.py +113 -0
- whatap/util/linked_map.py +359 -0
- whatap/util/metering_util.py +103 -0
- whatap/util/request_double_queue.py +68 -0
- whatap/util/request_queue.py +60 -0
- whatap/util/string_util.py +20 -0
- whatap/util/throttle_util.py +99 -0
- whatap/util/userid_util.py +134 -0
- whatap/value/__init__.py +1 -0
- whatap/value/blob_value.py +38 -0
- whatap/value/boolean_value.py +33 -0
- whatap/value/decimal_value.py +36 -0
- whatap/value/double_summary.py +86 -0
- whatap/value/double_value.py +33 -0
- whatap/value/float_array.py +42 -0
- whatap/value/float_value.py +34 -0
- whatap/value/int_array.py +42 -0
- whatap/value/ip4_value.py +50 -0
- whatap/value/list_value.py +105 -0
- whatap/value/long_array.py +44 -0
- whatap/value/long_summary.py +83 -0
- whatap/value/map_value.py +154 -0
- whatap/value/null_value.py +21 -0
- whatap/value/number_value.py +33 -0
- whatap/value/summary_value.py +39 -0
- whatap/value/text_array.py +58 -0
- whatap/value/text_hash_value.py +37 -0
- whatap/value/text_value.py +43 -0
- whatap/value/value.py +26 -0
- whatap/value/value_enum.py +80 -0
- whatap/whatap.conf +14 -0
- whatap_python-2.1.0.dist-info/METADATA +87 -0
- whatap_python-2.1.0.dist-info/RECORD +227 -0
- whatap_python-2.1.0.dist-info/WHEEL +5 -0
- whatap_python-2.1.0.dist-info/entry_points.txt +6 -0
- whatap_python-2.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,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__)
|