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,115 @@
|
|
|
1
|
+
"""FastAPI 계측 — ASGI scope/headers 파싱 + TraceContext 주입.
|
|
2
|
+
|
|
3
|
+
TX 패킷을 발사하지 않는다. scope 에서 값을 꺼내 ctx 필드에 옮기기만 한다.
|
|
4
|
+
"""
|
|
5
|
+
import logging as logging_module
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from whatap.conf.configure import Configure as conf
|
|
9
|
+
from whatap.util.hash_util import HashUtil
|
|
10
|
+
from whatap.util.hexa32 import Hexa32
|
|
11
|
+
from whatap.util.keygen import KeyGen
|
|
12
|
+
from whatap.util.userid_util import UseridUtil
|
|
13
|
+
|
|
14
|
+
logger = logging_module.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def parse_headers(scope):
|
|
18
|
+
"""ASGI raw headers(list of [bytes, bytes]) → lowercase str dict."""
|
|
19
|
+
headers = {}
|
|
20
|
+
for item in scope.get('headers') or ():
|
|
21
|
+
if not item or len(item) < 2:
|
|
22
|
+
continue
|
|
23
|
+
name, value = item[0], item[1]
|
|
24
|
+
try:
|
|
25
|
+
k = (name.decode('latin-1').lower()
|
|
26
|
+
if isinstance(name, (bytes, bytearray))
|
|
27
|
+
else str(name).lower())
|
|
28
|
+
v = (value.decode('latin-1')
|
|
29
|
+
if isinstance(value, (bytes, bytearray))
|
|
30
|
+
else str(value))
|
|
31
|
+
except Exception:
|
|
32
|
+
continue
|
|
33
|
+
headers[k] = v
|
|
34
|
+
return headers
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def client_ip(scope, headers):
|
|
38
|
+
"""trace_http_client_ip_header_key → x-forwarded-for → scope['client']."""
|
|
39
|
+
configured = conf.trace_http_client_ip_header_key
|
|
40
|
+
if configured:
|
|
41
|
+
v = headers.get(configured.lower())
|
|
42
|
+
if v:
|
|
43
|
+
return v.split(',')[0].strip()
|
|
44
|
+
xff = headers.get('x-forwarded-for')
|
|
45
|
+
if xff:
|
|
46
|
+
return xff.split(',')[0].strip()
|
|
47
|
+
client = scope.get('client')
|
|
48
|
+
if client:
|
|
49
|
+
return client[0]
|
|
50
|
+
return ''
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def resolve_user_id(headers, fallback_ip):
|
|
54
|
+
"""user_header_ticket → WhaTap 쿠키 → 신규발급. (userid, raw_value)."""
|
|
55
|
+
try:
|
|
56
|
+
if conf.user_header_ticket:
|
|
57
|
+
ticket = headers.get(conf.user_header_ticket)
|
|
58
|
+
if ticket:
|
|
59
|
+
return HashUtil.hashFromString(ticket), ticket
|
|
60
|
+
return 0, ''
|
|
61
|
+
|
|
62
|
+
cookie = headers.get('cookie')
|
|
63
|
+
if cookie:
|
|
64
|
+
if len(cookie) >= conf.trace_user_cookie_limit:
|
|
65
|
+
return _fallback_userid(fallback_ip)
|
|
66
|
+
idx = cookie.find(UseridUtil.WHATAP_R)
|
|
67
|
+
if idx >= 0:
|
|
68
|
+
end = cookie.find(';', idx)
|
|
69
|
+
start = idx + len(UseridUtil.WHATAP_R) + 1
|
|
70
|
+
value = cookie[start:end] if end > 0 else cookie[start:]
|
|
71
|
+
return Hexa32.toLong32(value), value
|
|
72
|
+
|
|
73
|
+
fresh = KeyGen.next()
|
|
74
|
+
return fresh, Hexa32.toString32(fresh)
|
|
75
|
+
except Exception:
|
|
76
|
+
_, exc, _ = sys.exc_info()
|
|
77
|
+
logger.debug('A057', 10, str(exc))
|
|
78
|
+
return _fallback_userid(fallback_ip)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _fallback_userid(fallback_ip):
|
|
82
|
+
if fallback_ip:
|
|
83
|
+
return HashUtil.hashFromString(fallback_ip), fallback_ip
|
|
84
|
+
return 0, ''
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def populate_ctx(ctx, scope, headers):
|
|
88
|
+
"""ctx 의 요청 메타 / 유저 / mtrace 필드 일괄 주입."""
|
|
89
|
+
ctx.host = (headers.get('host') or '').split(':')[0]
|
|
90
|
+
ctx.service_name = scope.get('path') or ''
|
|
91
|
+
ctx.http_method = scope.get('method') or ''
|
|
92
|
+
ctx.remoteIp = client_ip(scope, headers)
|
|
93
|
+
ctx.userAgentString = headers.get('user-agent', '')
|
|
94
|
+
ctx.referer = headers.get('referer', '')
|
|
95
|
+
|
|
96
|
+
if conf.trace_user_enabled:
|
|
97
|
+
if conf.trace_user_using_ip:
|
|
98
|
+
ctx.userid = ctx.remoteIp
|
|
99
|
+
else:
|
|
100
|
+
ctx.userid, ctx._rawuserid = resolve_user_id(headers, ctx.remoteIp)
|
|
101
|
+
|
|
102
|
+
caller = headers.get(conf._trace_mtrace_caller_key.lower(), '')
|
|
103
|
+
if caller:
|
|
104
|
+
ctx.setTransfer(caller)
|
|
105
|
+
if conf.stat_mtrace_enabled:
|
|
106
|
+
info = headers.get(conf._trace_mtrace_info_key.lower(), '')
|
|
107
|
+
if info:
|
|
108
|
+
ctx.setTransferInfo(info)
|
|
109
|
+
callee = headers.get(conf._trace_mtrace_callee_key.lower(), '')
|
|
110
|
+
if callee:
|
|
111
|
+
ctx.setTxid(callee)
|
|
112
|
+
|
|
113
|
+
poid = headers.get(conf._trace_mtrace_caller_poid_key.lower(), '')
|
|
114
|
+
if poid:
|
|
115
|
+
ctx.mcaller_poid = poid
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""FastAPI TX 라이프사이클 — TX_START / TX_END 발사 전담.
|
|
2
|
+
|
|
3
|
+
`start_interceptor` / `end_interceptor` 직접 호출은 이 모듈 두 함수
|
|
4
|
+
(`begin`, `finish`) 안에만 존재한다. 패키지 전체에서 이 두 함수 외로
|
|
5
|
+
라이프사이클 패킷을 발사하지 않는다.
|
|
6
|
+
"""
|
|
7
|
+
from whatap.conf.configure import Configure as conf
|
|
8
|
+
from whatap.net import async_sender
|
|
9
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
10
|
+
from whatap.trace.mod.application.wsgi import (
|
|
11
|
+
end_interceptor,
|
|
12
|
+
isIgnore,
|
|
13
|
+
start_interceptor,
|
|
14
|
+
)
|
|
15
|
+
from whatap.trace.trace_error import interceptor_error
|
|
16
|
+
from whatap.util.date_util import DateUtil
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def is_ignored(service_name):
|
|
20
|
+
try:
|
|
21
|
+
return isIgnore(service_name)
|
|
22
|
+
except Exception:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def begin(ctx):
|
|
27
|
+
"""TX_START 발사."""
|
|
28
|
+
start_interceptor(ctx)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def finish(ctx, scope, headers, status_code):
|
|
32
|
+
"""query_string 반영 / 정적 마킹 / status 에러 / 헤더 프로파일 / TX_END."""
|
|
33
|
+
qs = scope.get('query_string') or b''
|
|
34
|
+
if qs:
|
|
35
|
+
try:
|
|
36
|
+
qs_str = (qs.decode('latin-1')
|
|
37
|
+
if isinstance(qs, (bytes, bytearray))
|
|
38
|
+
else str(qs))
|
|
39
|
+
except Exception:
|
|
40
|
+
qs_str = ''
|
|
41
|
+
if qs_str and '?' not in (ctx.service_name or ''):
|
|
42
|
+
ctx.service_name = (ctx.service_name or '') + '?' + qs_str
|
|
43
|
+
|
|
44
|
+
name = ctx.service_name or ''
|
|
45
|
+
if '.' in name:
|
|
46
|
+
ext = name.rsplit('.', 1)[-1].split('?')[0]
|
|
47
|
+
if ext in conf.web_static_content_extensions:
|
|
48
|
+
ctx.isStaticContents = 'true'
|
|
49
|
+
|
|
50
|
+
if status_code and status_code >= 400:
|
|
51
|
+
interceptor_error(
|
|
52
|
+
status_code,
|
|
53
|
+
['HTTPResponse', 'Status {}'.format(status_code)],
|
|
54
|
+
ctx=ctx,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if conf.profile_http_header_enabled and headers:
|
|
58
|
+
text = ''.join(
|
|
59
|
+
'{}={}\n'.format(k, headers[k]) for k in sorted(headers.keys())
|
|
60
|
+
)
|
|
61
|
+
ctx.start_time = DateUtil.nowSystem()
|
|
62
|
+
async_sender.send_packet(
|
|
63
|
+
PacketTypeEnum.TX_MSG, ctx,
|
|
64
|
+
['HTTP-HEADERS', 'HTTP-HEADERS', text],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
end_interceptor(ctx=ctx)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from whatap.trace.trace_handler import trace_handler
|
|
2
|
+
from whatap.trace.trace_error import interceptor_error
|
|
3
|
+
from whatap.trace.mod.application.wsgi import interceptor
|
|
4
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
5
|
+
|
|
6
|
+
def instrument(module):
|
|
7
|
+
def wrapper(fn):
|
|
8
|
+
@trace_handler(fn, True)
|
|
9
|
+
def trace(*args, **kwargs):
|
|
10
|
+
flask_instance = args[0]
|
|
11
|
+
environ = args[1]
|
|
12
|
+
original_start_response = args[2]
|
|
13
|
+
|
|
14
|
+
def custom_start_response(status, response_headers, exc_info=None):
|
|
15
|
+
ctx = TraceContextManager.getLocalContext()
|
|
16
|
+
ctx.status = status[:3]
|
|
17
|
+
return original_start_response(status, response_headers, exc_info)
|
|
18
|
+
|
|
19
|
+
new_args = (flask_instance, environ, custom_start_response)
|
|
20
|
+
callback = interceptor(fn, *new_args, **kwargs)
|
|
21
|
+
return callback
|
|
22
|
+
|
|
23
|
+
return trace
|
|
24
|
+
|
|
25
|
+
module.Flask.wsgi_app = wrapper(module.Flask.wsgi_app)
|
|
26
|
+
|
|
27
|
+
def wrapper(fn):
|
|
28
|
+
@trace_handler(fn)
|
|
29
|
+
def trace(*args, **kwargs):
|
|
30
|
+
from werkzeug.exceptions import HTTPException
|
|
31
|
+
callback = fn(*args, **kwargs)
|
|
32
|
+
if callback is None:
|
|
33
|
+
e = args[1]
|
|
34
|
+
errors = [e.__class__.__name__]
|
|
35
|
+
status_code = getattr(e, 'code', 500)
|
|
36
|
+
#Flask 레이어의 예외 처리
|
|
37
|
+
if isinstance(e, HTTPException):
|
|
38
|
+
errors.append(e.description)
|
|
39
|
+
|
|
40
|
+
#Flask 예외 객체가 아닌 경우의 예외처리
|
|
41
|
+
#에러 코드와 메세지가 함께 나타날 수 있음.
|
|
42
|
+
else:
|
|
43
|
+
status_code = 500
|
|
44
|
+
errors.append(str(e))
|
|
45
|
+
interceptor_error(status_code, errors)
|
|
46
|
+
|
|
47
|
+
return callback
|
|
48
|
+
|
|
49
|
+
return trace
|
|
50
|
+
if hasattr(module.Flask, '_find_error_handler'):
|
|
51
|
+
module.Flask._find_error_handler = wrapper(
|
|
52
|
+
module.Flask._find_error_handler)
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
from whatap.trace.trace_handler import trace_handler
|
|
2
|
+
from whatap.trace.trace_error import interceptor_error
|
|
3
|
+
from whatap.trace.mod.application.wsgi import \
|
|
4
|
+
start_interceptor, isIgnore, \
|
|
5
|
+
end_interceptor
|
|
6
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
7
|
+
from whatap.conf.configure import Configure as conf
|
|
8
|
+
from whatap.util.keygen import KeyGen
|
|
9
|
+
import whatap.util.bit_util as bit_util
|
|
10
|
+
from whatap.util.hash_util import HashUtil as hash_util
|
|
11
|
+
from whatap.util.date_util import DateUtil
|
|
12
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
13
|
+
from whatap.util.hexa32 import Hexa32 as hexa32
|
|
14
|
+
import whatap.net.async_sender as async_sender
|
|
15
|
+
import logging as logging_module
|
|
16
|
+
import sys
|
|
17
|
+
from whatap.trace.trace_context import TraceContext
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
logger = logging_module.getLogger(__name__)
|
|
21
|
+
def toDjangoHeaderName(src):
|
|
22
|
+
|
|
23
|
+
return 'HTTP_' + src.upper().replace('-','_')
|
|
24
|
+
class UseridUtil(object):
|
|
25
|
+
WHATAP_R = "WHATAP"
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def getUserId(args, defValue):
|
|
29
|
+
try:
|
|
30
|
+
|
|
31
|
+
if conf.user_header_ticket:
|
|
32
|
+
ticket = UseridUtil.getHeader(args, conf.user_header_ticket)
|
|
33
|
+
if ticket:
|
|
34
|
+
return hash_util.hashFromString(ticket), ticket
|
|
35
|
+
return 0,""
|
|
36
|
+
cookie = UseridUtil.getHeader(args, "HTTP_COOKIE")
|
|
37
|
+
if cookie:
|
|
38
|
+
if len(cookie) >= conf.trace_user_cookie_limit :
|
|
39
|
+
return defValue
|
|
40
|
+
|
|
41
|
+
x1 = cookie.find(UseridUtil.WHATAP_R)
|
|
42
|
+
if x1 >= 0:
|
|
43
|
+
x2 = cookie.find(';', x1)
|
|
44
|
+
if x2 > 0:
|
|
45
|
+
value = cookie[x1 + len(UseridUtil.WHATAP_R) + 1: x2]
|
|
46
|
+
else:
|
|
47
|
+
value = cookie[x1 + len(UseridUtil.WHATAP_R) + 1:]
|
|
48
|
+
return hexa32.toLong32(value), value
|
|
49
|
+
userid=KeyGen.next()
|
|
50
|
+
return userid, hexa32.toString32(userid)
|
|
51
|
+
except Exception:
|
|
52
|
+
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
53
|
+
logger.debug("A502",10, str(exc_value))
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def setUserId(request, response, cookieValue):
|
|
57
|
+
try:
|
|
58
|
+
if not conf.user_header_ticket:
|
|
59
|
+
if request.cookies:
|
|
60
|
+
cookie = request.cookies.get(UseridUtil.WHATAP_R)
|
|
61
|
+
else:
|
|
62
|
+
cookie = None
|
|
63
|
+
if not cookie:
|
|
64
|
+
cookieDomain=conf.trace_user_cookie_domain if conf.trace_user_cookie_domain else None
|
|
65
|
+
UseridUtil.setCookie( response, UseridUtil.WHATAP_R, cookieValue, bit_util.INT_MAX_VALUE, "/", cookieDomain )
|
|
66
|
+
except Exception:
|
|
67
|
+
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
68
|
+
logger.debug("A503", 10, str(exc_value))
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def setCookie(response, key, value, max_age, path, domain):
|
|
72
|
+
response.set_cookie(key, value, max_age=max_age, path=path, domain=domain)
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def getRemoteAddr(args):
|
|
76
|
+
if conf.trace_http_client_ip_header_key:
|
|
77
|
+
header_val = UseridUtil.getHeader(args, conf.trace_http_client_ip_header_key)
|
|
78
|
+
if header_val:
|
|
79
|
+
return header_val.split(',')[0].strip()
|
|
80
|
+
x_forwarded_for = UseridUtil.getHeader(args, "x-forwarded-for")
|
|
81
|
+
if x_forwarded_for:
|
|
82
|
+
return x_forwarded_for.split(',')[0].strip()
|
|
83
|
+
return UseridUtil.getHeader(args, "REMOTE_ADDR")
|
|
84
|
+
#return UseridUtil.getHeader(args, "REMOTE_ADDR")
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def getHeader(args, key):
|
|
88
|
+
|
|
89
|
+
if args:
|
|
90
|
+
environ = args[0]
|
|
91
|
+
wsgiHeaderKey = 'HTTP_'+key.upper().replace('-','_')
|
|
92
|
+
return environ.get(key, environ.get(wsgiHeaderKey))
|
|
93
|
+
else:
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def parseServiceName(environ):
|
|
98
|
+
return environ.get('PATH_INFO', '')
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def interceptor(rn_environ, *args, **kwargs):
|
|
102
|
+
if not isinstance(rn_environ, tuple):
|
|
103
|
+
rn_environ = (rn_environ, args[1])
|
|
104
|
+
fn, environ = rn_environ
|
|
105
|
+
|
|
106
|
+
ctx = TraceContext()
|
|
107
|
+
ctx.host = environ.get('HTTP_HOST', '').split(':')[0]
|
|
108
|
+
ctx.service_name = parseServiceName(environ)
|
|
109
|
+
ctx.remoteIp = UseridUtil.getRemoteAddr(args)
|
|
110
|
+
ctx.userAgentString = environ.get('HTTP_USER_AGENT', '')
|
|
111
|
+
ctx.referer = environ.get('HTTP_REFERER', '')
|
|
112
|
+
|
|
113
|
+
if conf.trace_user_enabled:
|
|
114
|
+
if conf.trace_user_using_ip:
|
|
115
|
+
ctx.userid = UseridUtil.getRemoteAddr(args)
|
|
116
|
+
else:
|
|
117
|
+
ctx.userid, ctx._rawuserid = UseridUtil.getUserId(args, ctx.remoteIp)
|
|
118
|
+
|
|
119
|
+
mstt = environ.get('HTTP_{}'.format(
|
|
120
|
+
conf._trace_mtrace_caller_key.upper().replace('-', '_')), '')
|
|
121
|
+
|
|
122
|
+
if mstt:
|
|
123
|
+
ctx.setTransfer(mstt)
|
|
124
|
+
if conf.stat_mtrace_enabled:
|
|
125
|
+
val = environ.get('HTTP_{}'.format(
|
|
126
|
+
conf._trace_mtrace_info_key.upper().replace('-', '_')), '')
|
|
127
|
+
if val and len(val):
|
|
128
|
+
ctx.setTransferInfo(val)
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
myid = environ.get('HTTP_{}'.format(
|
|
132
|
+
conf._trace_mtrace_callee_key.upper().replace('-', '_')), '')
|
|
133
|
+
if myid:
|
|
134
|
+
ctx.setTxid(myid)
|
|
135
|
+
caller_poid = environ.get('HTTP_{}'.format(
|
|
136
|
+
conf._trace_mtrace_caller_poid_key.upper().replace('-', '_')), '')
|
|
137
|
+
|
|
138
|
+
if caller_poid:
|
|
139
|
+
ctx.mcaller_poid = caller_poid
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
if isIgnore(ctx.service_name):
|
|
143
|
+
ctx.is_ignored = True
|
|
144
|
+
TraceContextManager.end(ctx.id)
|
|
145
|
+
return fn(*args, **kwargs)
|
|
146
|
+
except Exception as e:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
start_interceptor(ctx)
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
|
|
153
|
+
callback = fn(*args, **kwargs)
|
|
154
|
+
ctx = TraceContextManager.getLocalContext()
|
|
155
|
+
if ctx:
|
|
156
|
+
query_string = environ.get('QUERY_STRING', '')
|
|
157
|
+
if query_string:
|
|
158
|
+
ctx.service_name += '?{}'.format(query_string)
|
|
159
|
+
|
|
160
|
+
if ctx.service_name.find('.') > -1 and ctx.service_name.split('.')[
|
|
161
|
+
1] in conf.web_static_content_extensions:
|
|
162
|
+
ctx.isStaticContents = 'true'
|
|
163
|
+
|
|
164
|
+
if getattr(callback, 'status_code', None):
|
|
165
|
+
status_code = callback.status_code
|
|
166
|
+
errors = [callback.reason_phrase, callback.__class__.__name__]
|
|
167
|
+
interceptor_error(status_code, errors)
|
|
168
|
+
|
|
169
|
+
if conf.profile_http_header_enabled:
|
|
170
|
+
keys = []
|
|
171
|
+
for key, value in environ.items():
|
|
172
|
+
if key.startswith('HTTP_'):
|
|
173
|
+
keys.append(key)
|
|
174
|
+
keys.sort()
|
|
175
|
+
|
|
176
|
+
text = ''
|
|
177
|
+
for key in keys:
|
|
178
|
+
text += '{}={}\n'.format(key.split('HTTP_')[1].lower(),
|
|
179
|
+
environ[key])
|
|
180
|
+
|
|
181
|
+
datas = ['HTTP-HEADERS', 'HTTP-HEADERS', text]
|
|
182
|
+
ctx.start_time = DateUtil.nowSystem()
|
|
183
|
+
async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
|
|
184
|
+
return callback
|
|
185
|
+
finally:
|
|
186
|
+
ctx = TraceContextManager.getLocalContext()
|
|
187
|
+
if ctx:
|
|
188
|
+
end_interceptor(ctx=ctx)
|
|
189
|
+
|
|
190
|
+
def instrument(module):
|
|
191
|
+
def wrapper(fn):
|
|
192
|
+
@trace_handler(fn, True)
|
|
193
|
+
def trace(*args, **kwargs):
|
|
194
|
+
environ = args[0]
|
|
195
|
+
callback = interceptor((fn, environ), *args, **kwargs)
|
|
196
|
+
|
|
197
|
+
return callback
|
|
198
|
+
|
|
199
|
+
return trace
|
|
200
|
+
if hasattr(module, "application"):
|
|
201
|
+
|
|
202
|
+
module.application = wrapper(module.application)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def run_after_request_hooks_wrapper(fn):
|
|
206
|
+
@trace_handler(fn, True)
|
|
207
|
+
def trace(*args, **kwargs):
|
|
208
|
+
request = args[0]
|
|
209
|
+
response = args[1]
|
|
210
|
+
ctx = TraceContextManager.getLocalContext()
|
|
211
|
+
if ctx and conf.trace_user_enabled:
|
|
212
|
+
if not conf.trace_user_using_ip:
|
|
213
|
+
UseridUtil.setUserId(request, response, ctx._rawuserid )
|
|
214
|
+
callback = fn(*args, **kwargs)
|
|
215
|
+
|
|
216
|
+
return callback
|
|
217
|
+
|
|
218
|
+
return trace
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
if hasattr(module, "run_after_request_hooks"):
|
|
222
|
+
|
|
223
|
+
module.run_after_request_hooks = run_after_request_hooks_wrapper(module.run_after_request_hooks)
|
|
224
|
+
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
from whatap.trace import get_dict
|
|
2
|
+
from whatap.trace.trace_handler import trace_handler
|
|
3
|
+
from whatap.trace.trace_error import interceptor_step_error
|
|
4
|
+
from whatap.trace.mod.application.wsgi import \
|
|
5
|
+
start_interceptor, end_interceptor
|
|
6
|
+
from whatap.trace.trace_context import TraceContext
|
|
7
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
8
|
+
import whatap.net.async_sender as async_sender
|
|
9
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
10
|
+
from whatap.util.date_util import DateUtil
|
|
11
|
+
try:
|
|
12
|
+
from importlib.metadata import version as _get_version
|
|
13
|
+
except ImportError:
|
|
14
|
+
from pkg_resources import get_distribution
|
|
15
|
+
def _get_version(name):
|
|
16
|
+
return get_distribution(name).version
|
|
17
|
+
from graphql.language.ast import OperationDefinitionNode
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def parseServiceName(graphql_doc):
|
|
21
|
+
# GraphQL-core 버전 확인
|
|
22
|
+
graphql_core_version = _get_version("graphql-core")
|
|
23
|
+
|
|
24
|
+
# GraphQL-core 3.x 이상 버전일 경우
|
|
25
|
+
if graphql_core_version.startswith("3") or graphql_core_version.startswith("4"):
|
|
26
|
+
try:
|
|
27
|
+
# OperationDefinitionNode 타입의 노드 필터링 (3.x 이상 버전 로직)
|
|
28
|
+
op_def = [
|
|
29
|
+
i for i in graphql_doc.definitions
|
|
30
|
+
if isinstance(i, OperationDefinitionNode)
|
|
31
|
+
][0]
|
|
32
|
+
except (IndexError, KeyError):
|
|
33
|
+
return "GraphQL unknown operation"
|
|
34
|
+
|
|
35
|
+
# operation의 타입이 OperationType Enum인 경우
|
|
36
|
+
op = op_def.operation.value # Enum 값을 문자열로 변환
|
|
37
|
+
else:
|
|
38
|
+
try:
|
|
39
|
+
# 이전 버전 로직 (OperationDefinition 사용)
|
|
40
|
+
op_def = [
|
|
41
|
+
i for i in graphql_doc.definitions
|
|
42
|
+
if type(i).__name__ == "OperationDefinition"
|
|
43
|
+
][0]
|
|
44
|
+
except (IndexError, KeyError):
|
|
45
|
+
return "GraphQL unknown operation"
|
|
46
|
+
|
|
47
|
+
# operation의 타입이 문자열인 경우
|
|
48
|
+
op = op_def.operation # 문자열 그대로 사용
|
|
49
|
+
|
|
50
|
+
name = op_def.name
|
|
51
|
+
fields = op_def.selection_set.selections if op_def.selection_set else []
|
|
52
|
+
|
|
53
|
+
return "/GraphQL %s %s" % (op.upper(), name.value if name else "+".join([f.name.value for f in fields]))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def intercept_execute(fn, *args, **kwargs):
|
|
57
|
+
ctx = TraceContextManager.getLocalContext()
|
|
58
|
+
if not ctx:
|
|
59
|
+
ctx = TraceContext()
|
|
60
|
+
is_transaction_started = False
|
|
61
|
+
else:
|
|
62
|
+
is_transaction_started = not ctx.is_ignored
|
|
63
|
+
if not is_transaction_started:
|
|
64
|
+
if len(args) > 1 and hasattr(args[1],"definitions"):
|
|
65
|
+
name = parseServiceName(args[1])
|
|
66
|
+
if name:
|
|
67
|
+
ctx.service_name = name
|
|
68
|
+
start_interceptor(ctx)
|
|
69
|
+
start_time = DateUtil.nowSystem()
|
|
70
|
+
try:
|
|
71
|
+
callback = fn(*args, **kwargs)
|
|
72
|
+
return callback
|
|
73
|
+
except Exception as e:
|
|
74
|
+
interceptor_step_error(e)
|
|
75
|
+
finally:
|
|
76
|
+
if not is_transaction_started:
|
|
77
|
+
end_interceptor()
|
|
78
|
+
else:
|
|
79
|
+
text = "graphql.execute"
|
|
80
|
+
payloads = [text, '']
|
|
81
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
82
|
+
async_sender.send_packet(PacketTypeEnum.TX_METHOD, ctx, payloads)
|
|
83
|
+
|
|
84
|
+
def parseDocumentName(op_def):
|
|
85
|
+
op = op_def.operation
|
|
86
|
+
name = op_def.name
|
|
87
|
+
fields = op_def.selection_set.selections
|
|
88
|
+
if not fields:
|
|
89
|
+
fields = []
|
|
90
|
+
return "GraphQL %s %s" % (op.upper(), name if name else "+".join([f.name.value for f in fields]))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def parseSelectionSet(gnode, tokens = [], indent=0):
|
|
94
|
+
nameFound = False
|
|
95
|
+
|
|
96
|
+
if hasattr(gnode, "selection_set"):
|
|
97
|
+
if hasattr(gnode, "name") and hasattr(gnode, "selection_set"):
|
|
98
|
+
if gnode.selection_set:
|
|
99
|
+
tokens.append(" "*indent+gnode.name.value+"{")
|
|
100
|
+
nameFound = True
|
|
101
|
+
else:
|
|
102
|
+
tokens.append(" "*indent+gnode.name.value)
|
|
103
|
+
|
|
104
|
+
if hasattr(gnode, "selection_set"):
|
|
105
|
+
if gnode.selection_set:
|
|
106
|
+
if gnode.selection_set.selections:
|
|
107
|
+
for sel in gnode.selection_set.selections:
|
|
108
|
+
parseSelectionSet(sel, tokens, indent = indent+1)
|
|
109
|
+
if nameFound:
|
|
110
|
+
tokens.append(" "*indent+"}")
|
|
111
|
+
|
|
112
|
+
def parseDocument(defi):
|
|
113
|
+
tokens = []
|
|
114
|
+
if hasattr(defi, "name"):
|
|
115
|
+
tokens.append(defi.name.value+"{")
|
|
116
|
+
|
|
117
|
+
if hasattr(defi, "selection_set"):
|
|
118
|
+
if defi.selection_set:
|
|
119
|
+
if defi.selection_set.selections:
|
|
120
|
+
for sel in defi.selection_set.selections:
|
|
121
|
+
if sel:
|
|
122
|
+
parseSelectionSet(sel, tokens, 1)
|
|
123
|
+
|
|
124
|
+
if hasattr(defi, "name"):
|
|
125
|
+
tokens.append("}")
|
|
126
|
+
tokens.reverse()
|
|
127
|
+
return "\n".join(tokens)
|
|
128
|
+
|
|
129
|
+
def intercept_execute_method( fn, *args, **kwargs):
|
|
130
|
+
ctx = TraceContextManager.getLocalContext()
|
|
131
|
+
start_time = DateUtil.nowSystem()
|
|
132
|
+
try:
|
|
133
|
+
callback = fn(*args, **kwargs)
|
|
134
|
+
return callback
|
|
135
|
+
except Exception as e:
|
|
136
|
+
if ctx:
|
|
137
|
+
interceptor_step_error(e)
|
|
138
|
+
finally:
|
|
139
|
+
if ctx and len(args) > 4 and not args[4] and args[3]:
|
|
140
|
+
operation_definition = args[3][0]
|
|
141
|
+
text = operation_definition.name.value
|
|
142
|
+
arg = parseDocument(operation_definition)
|
|
143
|
+
|
|
144
|
+
payloads = [text, arg]
|
|
145
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
146
|
+
async_sender.send_packet(PacketTypeEnum.TX_METHOD, ctx, payloads)
|
|
147
|
+
|
|
148
|
+
def instrument_graphql(module):
|
|
149
|
+
def wrapper(fn):
|
|
150
|
+
@trace_handler(fn, start=True)
|
|
151
|
+
def trace(*args, **kwargs):
|
|
152
|
+
callback = intercept_execute(fn, *args, **kwargs)
|
|
153
|
+
return callback
|
|
154
|
+
|
|
155
|
+
return trace
|
|
156
|
+
if hasattr(module, 'execute'):
|
|
157
|
+
module.execute = wrapper(module.execute)
|
|
158
|
+
|
|
159
|
+
def wrapper( fn):
|
|
160
|
+
@trace_handler(fn, start=True)
|
|
161
|
+
def trace(*args, **kwargs):
|
|
162
|
+
callback = intercept_execute_method( fn, *args, **kwargs)
|
|
163
|
+
return callback
|
|
164
|
+
return trace
|
|
165
|
+
|
|
166
|
+
# if hasattr(module, 'execute_operation'):
|
|
167
|
+
# module.execute_operation = wrapper(module.execute_operation)
|
|
168
|
+
|
|
169
|
+
if hasattr(module, 'resolve_field'):
|
|
170
|
+
module.resolve_field = wrapper(module.resolve_field)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from whatap.trace import get_dict
|
|
2
|
+
from whatap.trace.trace_handler import trace_handler
|
|
3
|
+
from whatap.trace.trace_error import interceptor_step_error
|
|
4
|
+
from whatap.trace.mod.application.wsgi import \
|
|
5
|
+
start_interceptor, end_interceptor
|
|
6
|
+
from whatap.trace.trace_context import TraceContext
|
|
7
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
8
|
+
import whatap.net.async_sender as async_sender
|
|
9
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
10
|
+
from whatap.util.date_util import DateUtil
|
|
11
|
+
|
|
12
|
+
def intercept_worker(fn, *args, **kwargs):
|
|
13
|
+
ctx = TraceContext()
|
|
14
|
+
worker_ctx = args[1]
|
|
15
|
+
ctx.service_name = worker_ctx.call_id_stack
|
|
16
|
+
start_interceptor(ctx)
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
callback = fn(*args, **kwargs)
|
|
20
|
+
return callback
|
|
21
|
+
except Exception as e:
|
|
22
|
+
interceptor_step_error(e)
|
|
23
|
+
finally:
|
|
24
|
+
end_interceptor()
|
|
25
|
+
|
|
26
|
+
def instrument_nameko_spawn_worker(module):
|
|
27
|
+
def wrapper(fn):
|
|
28
|
+
@trace_handler(fn, start=True)
|
|
29
|
+
def trace(*args, **kwargs):
|
|
30
|
+
callback = intercept_worker(fn, *args, **kwargs)
|
|
31
|
+
return callback
|
|
32
|
+
|
|
33
|
+
return trace
|
|
34
|
+
|
|
35
|
+
if hasattr(module, 'ServiceContainer') and hasattr(module.ServiceContainer, '_run_worker'):
|
|
36
|
+
from eventlet.corolocal import local
|
|
37
|
+
TraceContextManager.local = local()
|
|
38
|
+
module.ServiceContainer._run_worker = wrapper(module.ServiceContainer._run_worker)
|
|
39
|
+
|