whatap-python 2.1.4rc1__tar.gz → 2.2.1rc1__tar.gz
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_python-2.1.4rc1 → whatap_python-2.2.1rc1}/PKG-INFO +1 -1
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/__init__.py +311 -106
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/agent/linux/amd64/whatap_python +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/agent/linux/arm64/whatap_python +0 -0
- whatap_python-2.2.1rc1/whatap/build.py +4 -0
- whatap_python-2.2.1rc1/whatap/conf/configuration.py +444 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/conf/configure.py +55 -0
- whatap_python-2.2.1rc1/whatap/counter/__init__.py +36 -0
- whatap_python-2.2.1rc1/whatap/counter/tasks/base_task.py +35 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/counter/tasks/llm_evaluator_task.py +56 -10
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/counter/tasks/llm_log_sink_task.py +85 -19
- whatap_python-2.2.1rc1/whatap/counter/tasks/llm_stat_task.py +138 -0
- whatap_python-2.2.1rc1/whatap/llm/__init__.py +70 -0
- whatap_python-2.2.1rc1/whatap/llm/api/__init__.py +20 -0
- whatap_python-2.2.1rc1/whatap/llm/api/agent_name.py +83 -0
- {whatap_python-2.1.4rc1/whatap/llm → whatap_python-2.2.1rc1/whatap/llm/api}/prompt_meta.py +31 -4
- whatap_python-2.2.1rc1/whatap/llm/api/session.py +175 -0
- whatap_python-2.2.1rc1/whatap/llm/api/turn.py +88 -0
- whatap_python-2.2.1rc1/whatap/llm/api/user.py +176 -0
- whatap_python-2.2.1rc1/whatap/llm/api/workflow.py +115 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/__init__.py +4 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/base.py +15 -11
- whatap_python-2.2.1rc1/whatap/llm/evaluators/builtins/_json_repair.py +106 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/combined_judge.py +7 -4
- whatap_python-2.2.1rc1/whatap/llm/evaluators/builtins/judge_factories.py +90 -0
- whatap_python-2.2.1rc1/whatap/llm/evaluators/builtins/llm_judge.py +248 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/pii_leak.py +5 -20
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/url_scan.py +21 -22
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/registry.py +91 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/scope.py +22 -2
- whatap_python-2.2.1rc1/whatap/llm/instrument/__init__.py +32 -0
- whatap_python-2.2.1rc1/whatap/llm/instrument/_boundary.py +492 -0
- whatap_python-2.2.1rc1/whatap/llm/instrument/agent.py +35 -0
- whatap_python-2.2.1rc1/whatap/llm/instrument/llm_api.py +89 -0
- whatap_python-2.2.1rc1/whatap/llm/instrument/workflow.py +42 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_log_sink_pack.py +32 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_tx_status.py +51 -0
- whatap_python-2.2.1rc1/whatap/llm/orchestration/__init__.py +16 -0
- whatap_python-2.2.1rc1/whatap/llm/orchestration/hermes/__init__.py +143 -0
- whatap_python-2.2.1rc1/whatap/llm/orchestration/hermes/gateway.py +172 -0
- whatap_python-2.2.1rc1/whatap/llm/orchestration/hermes/kanban.py +353 -0
- whatap_python-2.2.1rc1/whatap/llm/orchestration/langgraph/__init__.py +134 -0
- whatap_python-2.2.1rc1/whatap/llm/pricing.py +187 -0
- whatap_python-2.2.1rc1/whatap/llm/providers/anthropic/__init__.py +72 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/anthropic/messages/messages_context.py +48 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/anthropic/messages/messages_extractor.py +53 -0
- whatap_python-2.2.1rc1/whatap/llm/providers/anthropic/messages/messages_stream.py +162 -0
- whatap_python-2.2.1rc1/whatap/llm/providers/interceptor.py +349 -0
- whatap_python-2.2.1rc1/whatap/llm/providers/openai/__init__.py +110 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/chat/chat_context.py +6 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/content_parser.py +4 -1
- whatap_python-2.2.1rc1/whatap/llm/providers/stream_accumulator.py +287 -0
- whatap_python-2.2.1rc1/whatap/llm/providers/workflow.py +681 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/__init__.py +6 -0
- whatap_python-2.2.1rc1/whatap/llm/stats/active_agent_stat.py +93 -0
- whatap_python-2.2.1rc1/whatap/llm/stats/active_stat.py +83 -0
- whatap_python-2.2.1rc1/whatap/llm/stats/actx_stat.py +80 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/base_stat.py +6 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/eval_stat.py +1 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/pii_leak_eval_stat.py +2 -1
- whatap_python-2.2.1rc1/whatap/llm/stats/traj_actx_stat.py +84 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/url_scan_eval_stat.py +2 -1
- whatap_python-2.2.1rc1/whatap/net/async_sender.py +313 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/net/packet_enum.py +1 -1
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/net/param_def.py +11 -0
- whatap_python-2.2.1rc1/whatap/net/udp_session.py +790 -0
- whatap_python-2.2.1rc1/whatap/net/udp_thread.py +103 -0
- whatap_python-2.2.1rc1/whatap/trace/mod/application/fastapi/instrumentation.py +403 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/fastapi/scope.py +17 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/wsgi.py +19 -1
- whatap_python-2.2.1rc1/whatap/trace/mod/hook_service_patterns.py +26 -0
- whatap_python-2.2.1rc1/whatap/trace/mod/httpc/util.py +689 -0
- whatap_python-2.2.1rc1/whatap/trace/mod/pattern_targets.py +137 -0
- whatap_python-2.2.1rc1/whatap/trace/mod/plugin.py +47 -0
- whatap_python-2.2.1rc1/whatap/trace/mod/process/subprocess_spawn.py +390 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/standalone/multiple.py +2 -1
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/standalone/single.py +2 -1
- whatap_python-2.2.1rc1/whatap/trace/mod/thread_context.py +202 -0
- whatap_python-2.2.1rc1/whatap/trace/mod/workflow_plugin.py +33 -0
- whatap_python-2.2.1rc1/whatap/trace/mtrace_env.py +368 -0
- whatap_python-2.2.1rc1/whatap/trace/patch.py +98 -0
- whatap_python-2.2.1rc1/whatap/trace/trace_context.py +330 -0
- whatap_python-2.2.1rc1/whatap/trace/trace_context_manager.py +1005 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/trace_error.py +8 -2
- whatap_python-2.2.1rc1/whatap/trace/trace_import.py +136 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/trace_module_definition.py +28 -0
- whatap_python-2.2.1rc1/whatap/util/cardinality/__init__.py +0 -0
- whatap_python-2.2.1rc1/whatap/util/native_process.py +458 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/PKG-INFO +1 -1
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/SOURCES.txt +32 -3
- whatap_python-2.1.4rc1/tests/test_stream_ttft.py +0 -192
- whatap_python-2.1.4rc1/whatap/build.py +0 -4
- whatap_python-2.1.4rc1/whatap/conf/configuration.py +0 -284
- whatap_python-2.1.4rc1/whatap/counter/__init__.py +0 -14
- whatap_python-2.1.4rc1/whatap/counter/tasks/base_task.py +0 -26
- whatap_python-2.1.4rc1/whatap/counter/tasks/llm_stat_task.py +0 -78
- whatap_python-2.1.4rc1/whatap/llm/__init__.py +0 -17
- whatap_python-2.1.4rc1/whatap/llm/evaluators/builtins/llm_judge.py +0 -516
- whatap_python-2.1.4rc1/whatap/llm/pricing.py +0 -236
- whatap_python-2.1.4rc1/whatap/llm/providers/anthropic/__init__.py +0 -37
- whatap_python-2.1.4rc1/whatap/llm/providers/interceptor.py +0 -232
- whatap_python-2.1.4rc1/whatap/llm/providers/openai/__init__.py +0 -133
- whatap_python-2.1.4rc1/whatap/llm/providers/stream_accumulator.py +0 -139
- whatap_python-2.1.4rc1/whatap/llm/stats/active_stat.py +0 -86
- whatap_python-2.1.4rc1/whatap/llm/stats/meter.py +0 -18
- whatap_python-2.1.4rc1/whatap/net/async_sender.py +0 -107
- whatap_python-2.1.4rc1/whatap/net/udp_session.py +0 -396
- whatap_python-2.1.4rc1/whatap/net/udp_thread.py +0 -54
- whatap_python-2.1.4rc1/whatap/trace/mod/application/fastapi/instrumentation.py +0 -204
- whatap_python-2.1.4rc1/whatap/trace/mod/hook_service_patterns.py +0 -72
- whatap_python-2.1.4rc1/whatap/trace/mod/httpc/util.py +0 -388
- whatap_python-2.1.4rc1/whatap/trace/mod/plugin.py +0 -84
- whatap_python-2.1.4rc1/whatap/trace/trace_context.py +0 -212
- whatap_python-2.1.4rc1/whatap/trace/trace_context_manager.py +0 -255
- whatap_python-2.1.4rc1/whatap/trace/trace_import.py +0 -99
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/README.md +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/pyproject.toml +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/setup.cfg +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/setup.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/LICENSE +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/README.rst +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/__main__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/agent/darwin/amd64/whatap_python +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/agent/darwin/arm64/whatap_python +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/agent/windows/whatap_python.exe +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/bootstrap/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/bootstrap/sitecustomize.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/conf/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/conf/license.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/control/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/counter/counter_manager.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/counter/tasks/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/counter/tasks/openfiledescriptor.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/io/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/io/data_inputx.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/io/data_outputx.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/definitions.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/answer_relevance.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/factuality.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/hallucination.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/prompt_injection.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/toxicity.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/sampler.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/features.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_input_message.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_output_message.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_step_eval_status.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_step_status.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_system_message.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_tool_calls.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_tool_results.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/anthropic/messages/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/anthropic/messages/messages.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/chat/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/chat/chat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/chat/chat_extractor.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/completions/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/completions/completions.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/completions/completions_context.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/completions/completions_extractor.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/embeddings/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/embeddings/embeddings.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/embeddings/embeddings_context.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/embeddings/embeddings_extractor.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/responses/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/responses/responses.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/responses/responses_context.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/responses/responses_extractor.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/answer_relevance_eval_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/api_status_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/combined_judge_eval_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/error_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/factuality_eval_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/feature_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/finish_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/hallucination_eval_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/perf_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/prompt_injection_eval_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/token_usage_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/toxicity_eval_stat.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/net/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/net/packet_type_enum.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/net/stackhelper.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/pack/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/pack/logSinkPack.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/pack/pack.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/pack/pack_enum.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/pack/tagCountPack.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/scripts/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/amqp/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/amqp/kombu.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/amqp/pika.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/bottle.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/celery.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/cherrypy.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/django.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/django_asgi.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/django_py3.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/fastapi/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/fastapi/endpoint.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/fastapi/exception_log.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/fastapi/transaction.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/flask.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/frappe.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/graphql.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/nameko.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/odoo.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/starlette.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/tornado.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/cxoracle.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/mongo.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/mysql.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/neo4j.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/psycopg2.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/psycopg3.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/redis.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/sqlalchemy.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/sqlite3.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/email/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/email/smtp.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/django.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/httplib.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/httpx.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/requests.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/urllib3.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/logging.py +0 -0
- {whatap_python-2.1.4rc1/whatap/trace/mod/standalone → whatap_python-2.2.1rc1/whatap/trace/mod/process}/__init__.py +0 -0
- {whatap_python-2.1.4rc1/whatap/util → whatap_python-2.2.1rc1/whatap/trace/mod/standalone}/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/simple_trace_context.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/trace_handler.py +0 -0
- {whatap_python-2.1.4rc1/whatap/util/cardinality → whatap_python-2.2.1rc1/whatap/util}/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/bit_util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/cardinality/hyperloglog.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/cardinality/murmurhash.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/cardinality/registerset.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/compare_util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/date_util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/debug_util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/escape_literal_sql.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/frame_util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/hash_util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/hexa32.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/int_set.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/ip_util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/keygen.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/linked_list.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/linked_map.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/metering_util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/request_double_queue.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/request_queue.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/string_util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/throttle_util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/userid_util.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/__init__.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/blob_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/boolean_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/decimal_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/double_summary.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/double_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/float_array.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/float_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/int_array.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/ip4_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/list_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/long_array.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/long_summary.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/map_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/null_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/number_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/summary_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/text_array.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/text_hash_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/text_value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/value.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/value_enum.py +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/whatap.conf +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/dependency_links.txt +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/entry_points.txt +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/not-zip-safe +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/requires.txt +0 -0
- {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/top_level.txt +0 -0
|
@@ -117,6 +117,37 @@ def whatap_print(*args):
|
|
|
117
117
|
message = " ".join(args)
|
|
118
118
|
print(message)
|
|
119
119
|
|
|
120
|
+
|
|
121
|
+
def whatap_error_log(msg, log_id='WHATAP_AGENT_ERROR'):
|
|
122
|
+
"""에이전트 시작 오류를 프로세스 로그(stdout)와 hook.log 양쪽에 동일하게 노출한다.
|
|
123
|
+
|
|
124
|
+
hook.log FileHandler 는 Configure.load(display=True)->Logger() 시점에 붙는데,
|
|
125
|
+
go() 스레드가 config() 보다 먼저 도는 경로(자동 attach)나 start_agent 경로에서는
|
|
126
|
+
아직 핸들러가 없을 수 있다. 그 경우에도 오류가 유실되지 않도록, 핸들러가 없으면
|
|
127
|
+
whatap-hook.log 에 직접 append 한다."""
|
|
128
|
+
# 1) 프로세스 로그 (사용자 애플리케이션 stdout)
|
|
129
|
+
whatap_print(msg)
|
|
130
|
+
|
|
131
|
+
# 2) hook.log
|
|
132
|
+
try:
|
|
133
|
+
if logging.handlers:
|
|
134
|
+
logging.error(msg, extra={'id': log_id})
|
|
135
|
+
return
|
|
136
|
+
except Exception:
|
|
137
|
+
pass
|
|
138
|
+
|
|
139
|
+
# FileHandler 미부착: whatap-hook.log 에 직접 기록
|
|
140
|
+
try:
|
|
141
|
+
logs_dir = os.environ.get('WHATAP_LOGS') or \
|
|
142
|
+
os.path.join(os.environ.get('WHATAP_HOME', '.'), 'logs')
|
|
143
|
+
if not os.path.exists(logs_dir):
|
|
144
|
+
os.makedirs(logs_dir)
|
|
145
|
+
with open(os.path.join(logs_dir, LOG_FILE_NAME), 'a') as f:
|
|
146
|
+
f.write('[{0}] : {1} - {2}\n'.format(
|
|
147
|
+
time.strftime(LOGGING_DATE_FORMAT), log_id, msg))
|
|
148
|
+
except Exception as e:
|
|
149
|
+
whatap_print('WHATAP: hook.log write failed: {}'.format(e))
|
|
150
|
+
|
|
120
151
|
class Logger(object):
|
|
121
152
|
def __init__(self):
|
|
122
153
|
self.logger = logging
|
|
@@ -291,6 +322,18 @@ def config(home):
|
|
|
291
322
|
from whatap.conf.license import License
|
|
292
323
|
conf.PCODE = License.getProjectCode(conf.license)
|
|
293
324
|
|
|
325
|
+
# LLM 채널은 자기 프로젝트 코드를 가진다. LLM 트랜잭션(is_llm=1)은 LLM Go Agent
|
|
326
|
+
# 소켓으로만 나가므로 결합 좌표에 APM PCODE 를 실으면 서버가 호출자를 엉뚱한
|
|
327
|
+
# 프로젝트에서 찾는다(conf.pcode_of 참조). getProjectCode 는 키 문자열 파싱이라
|
|
328
|
+
# 서버 왕복이 없고, apm_enabled=false 인 LLM 전용 배포에서도 해소된다.
|
|
329
|
+
llm_key = str(getattr(conf, 'llm_accesskey', '') or '').strip()
|
|
330
|
+
if llm_key:
|
|
331
|
+
try:
|
|
332
|
+
from whatap.conf.license import License
|
|
333
|
+
conf.LLM_PCODE = License.getProjectCode(llm_key)
|
|
334
|
+
except Exception as e:
|
|
335
|
+
whatap_print('WHATAP: LLM PCODE RESOLVE ERROR: {}'.format(e))
|
|
336
|
+
|
|
294
337
|
import whatap.counter
|
|
295
338
|
|
|
296
339
|
hooks(home)
|
|
@@ -366,47 +409,61 @@ def hooks(home):
|
|
|
366
409
|
from whatap.trace.mod.logging import instrument_logging
|
|
367
410
|
instrument_logging(logging_module)
|
|
368
411
|
|
|
412
|
+
# 한 모듈에 여러 패턴 계열(스텝=instrument_plugin / 워크플로우=instrument_workflow
|
|
413
|
+
# / 트랜잭션=instrument_service)이 공존할 수 있도록, IMPORT_HOOKS[모듈]에
|
|
414
|
+
# 인스트루먼터를 각자의 class_defs 와 함께 리스트로 누적한다. 과거엔 단일 dict 라
|
|
415
|
+
# 뒤 계열이 앞을 덮어써서 같은 모듈에 두 패턴(예: predict=트랜잭션 + 헬퍼=스텝)을
|
|
416
|
+
# 못 걸었다.
|
|
417
|
+
def _register_pattern_hook(module_name, class_def, def_name, mod_path):
|
|
418
|
+
if not PLUGIN.get(module_name):
|
|
419
|
+
PLUGIN[module_name] = []
|
|
420
|
+
if class_def not in PLUGIN[module_name]:
|
|
421
|
+
PLUGIN[module_name].append(class_def) # 호환용(외부 참조)
|
|
422
|
+
existing = IMPORT_HOOKS.get(module_name)
|
|
423
|
+
if isinstance(existing, list):
|
|
424
|
+
entry_list = existing
|
|
425
|
+
elif isinstance(existing, dict):
|
|
426
|
+
entry_list = [existing]
|
|
427
|
+
IMPORT_HOOKS[module_name] = entry_list
|
|
428
|
+
else:
|
|
429
|
+
entry_list = []
|
|
430
|
+
IMPORT_HOOKS[module_name] = entry_list
|
|
431
|
+
for e in entry_list:
|
|
432
|
+
if isinstance(e, dict) and e.get('def') == def_name \
|
|
433
|
+
and e.get('module') == mod_path:
|
|
434
|
+
e.setdefault('class_defs', []).append(class_def)
|
|
435
|
+
return
|
|
436
|
+
entry_list.append({'def': def_name, 'module': mod_path,
|
|
437
|
+
'class_defs': [class_def]})
|
|
438
|
+
|
|
369
439
|
if conf.hook_method_patterns:
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
module_name, class_def
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
'mod',
|
|
390
|
-
key)}
|
|
391
|
-
|
|
392
|
-
# 서비스(트랜잭션) 진입점 자동 래핑 (conf 방법). hook_method_patterns 와 같은
|
|
393
|
-
# import-hook 플러밍, 래퍼만 트랜잭션 open/close (whatap.trace.mod.hook_service_patterns).
|
|
394
|
-
# @method_profiling 을 소스 수정 없이 conf 로 적용하는 경로.
|
|
440
|
+
for pattern in conf.hook_method_patterns.split(','):
|
|
441
|
+
pattern = pattern.strip()
|
|
442
|
+
if not pattern or ':' not in pattern:
|
|
443
|
+
continue
|
|
444
|
+
module_name, class_def = pattern.split(':', 1)
|
|
445
|
+
_register_pattern_hook(module_name.strip(), class_def.strip(),
|
|
446
|
+
'instrument_plugin', 'whatap.trace.mod.plugin')
|
|
447
|
+
|
|
448
|
+
# LLM 워크플로우 경계 자동 래핑 (conf 방법). 래퍼만 워크플로우 open/close.
|
|
449
|
+
if conf.workflow_method_patterns:
|
|
450
|
+
for pattern in conf.workflow_method_patterns.split(','):
|
|
451
|
+
pattern = pattern.strip()
|
|
452
|
+
if not pattern or ':' not in pattern:
|
|
453
|
+
continue
|
|
454
|
+
module_name, class_def = pattern.split(':', 1)
|
|
455
|
+
_register_pattern_hook(module_name.strip(), class_def.strip(),
|
|
456
|
+
'instrument_workflow', 'whatap.trace.mod.workflow_plugin')
|
|
457
|
+
|
|
458
|
+
# 서비스(트랜잭션) 진입점 자동 래핑 (conf 방법). @method_profiling 의 conf 버전.
|
|
395
459
|
if conf.hook_service_patterns:
|
|
396
460
|
for pattern in conf.hook_service_patterns.split(','):
|
|
397
461
|
pattern = pattern.strip()
|
|
398
462
|
if not pattern or ':' not in pattern:
|
|
399
463
|
continue
|
|
400
464
|
module_name, class_def = pattern.split(':', 1)
|
|
401
|
-
module_name
|
|
402
|
-
|
|
403
|
-
if not PLUGIN.get(module_name):
|
|
404
|
-
PLUGIN[module_name] = []
|
|
405
|
-
PLUGIN[module_name].append(class_def)
|
|
406
|
-
# 대상 모듈이 PLUGIN 에 등록되면 ImportFinder 가 class_defs 패키징.
|
|
407
|
-
IMPORT_HOOKS[module_name] = {
|
|
408
|
-
'def': 'instrument_service',
|
|
409
|
-
'module': 'whatap.trace.mod.hook_service_patterns'}
|
|
465
|
+
_register_pattern_hook(module_name.strip(), class_def.strip(),
|
|
466
|
+
'instrument_service', 'whatap.trace.mod.hook_service_patterns')
|
|
410
467
|
|
|
411
468
|
if conf.standalone_enabled:
|
|
412
469
|
if conf.standalone_type == 'multiple-transaction':
|
|
@@ -420,9 +477,43 @@ def hooks(home):
|
|
|
420
477
|
logging.debug(e, extra={'id': 'PLUGIN ERROR'})
|
|
421
478
|
finally:
|
|
422
479
|
sys.meta_path.insert(0, ImportFinder())
|
|
480
|
+
_instrument_already_imported()
|
|
423
481
|
logging.debug('WHATAP AGENT START!', extra={'id': 'WA000'})
|
|
424
482
|
|
|
425
483
|
|
|
484
|
+
def _instrument_already_imported():
|
|
485
|
+
"""finder 설치 시점에 **이미 import 돼 있던** 훅 대상을 직접 계측한다.
|
|
486
|
+
|
|
487
|
+
``ImportFinder.find_spec`` 은 import 시스템이 ``sys.modules`` 미스일 때만 호출하므로,
|
|
488
|
+
finder 설치보다 먼저 로드된 모듈은 훅이 영원히 안 걸린다. 그리고 이 에이전트는 자기
|
|
489
|
+
부팅에 필요한 모듈(``subprocess`` — 최상단 import, 데몬 기동에 사용)을 finder 설치
|
|
490
|
+
**전에** 로드하므로, 그 대상은 DEFINITION 에 등록해도 계측되지 않는다.
|
|
491
|
+
|
|
492
|
+
앱이 우회할 수 없는 종류의 구멍이다(앱이 할 수 있는 건 에이전트 내부 instrument_*
|
|
493
|
+
함수를 대신 호출하는 것뿐이고, 그건 자동계측을 쓰는 의미를 없앤다).
|
|
494
|
+
|
|
495
|
+
``load_module`` 을 그대로 재사용하므로 정상 import 경로와 완전히 같은 처리를 받는다
|
|
496
|
+
(엔트리 리스트/class_defs 패키징 포함). instrument_* 는 멱등이어야 한다 — 이 스윕과 이후의
|
|
497
|
+
재-import 가 같은 대상을 두 번 건드릴 수 있다.
|
|
498
|
+
"""
|
|
499
|
+
try:
|
|
500
|
+
from whatap.trace.trace_import import load_module
|
|
501
|
+
except Exception as e:
|
|
502
|
+
logging.info("pre-imported sweep unavailable: %s: %s",
|
|
503
|
+
e.__class__.__name__, e, exc_info=True)
|
|
504
|
+
return
|
|
505
|
+
for fullname in list(IMPORT_HOOKS.keys()):
|
|
506
|
+
module = sys.modules.get(fullname)
|
|
507
|
+
if module is None:
|
|
508
|
+
continue
|
|
509
|
+
try:
|
|
510
|
+
load_module(module, fullname)
|
|
511
|
+
except Exception as e:
|
|
512
|
+
# 계측 실패는 조용히 넘기면 "데이터가 안 보인다" 로만 드러난다.
|
|
513
|
+
logging.info("failed to inject pre-imported %s: %s: %s",
|
|
514
|
+
fullname, e.__class__.__name__, e, exc_info=True)
|
|
515
|
+
|
|
516
|
+
|
|
426
517
|
def agent():
|
|
427
518
|
home = 'WHATAP_HOME'
|
|
428
519
|
whatap_home = os.environ.get(home)
|
|
@@ -446,14 +537,28 @@ def agent():
|
|
|
446
537
|
llm_enabled = preview_whatap_conf("llm_enabled")
|
|
447
538
|
|
|
448
539
|
if apm_enabled == 'true':
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
540
|
+
if sys.platform.startswith('linux'):
|
|
541
|
+
# Publish native ownership before a short-lived interpreter
|
|
542
|
+
# can exit. This does not wait for collector/TCP readiness.
|
|
543
|
+
go()
|
|
544
|
+
else:
|
|
545
|
+
t = threading.Thread(target=go)
|
|
546
|
+
t.setDaemon(True)
|
|
547
|
+
t.start()
|
|
452
548
|
|
|
453
549
|
if llm_enabled == 'true':
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
550
|
+
# llm_enabled=true 라도 LLM 채널 자격증명(llm_accesskey)이 없으면 LLM
|
|
551
|
+
# 에이전트를 띄우지 않는다. (env 로 플래그만 켜진 경우의 오작동 방지 —
|
|
552
|
+
# llm_accesskey/LLM 서버 호스트 없이 LLM 모드로 도는 것을 막는다.)
|
|
553
|
+
llm_accesskey = (preview_whatap_conf("llm_accesskey")
|
|
554
|
+
or os.environ.get("llm_accesskey"))
|
|
555
|
+
if llm_accesskey:
|
|
556
|
+
t = threading.Thread(target=go, kwargs={'llm': True})
|
|
557
|
+
t.setDaemon(True)
|
|
558
|
+
t.start()
|
|
559
|
+
else:
|
|
560
|
+
whatap_print("WHATAP: llm_enabled=true 이나 llm_accesskey 가 "
|
|
561
|
+
"없어 LLM 에이전트 기동을 건너뜁니다")
|
|
457
562
|
|
|
458
563
|
config(home)
|
|
459
564
|
|
|
@@ -471,7 +576,71 @@ AGENT_NAME = 'whatap_python'
|
|
|
471
576
|
|
|
472
577
|
|
|
473
578
|
|
|
579
|
+
def _native_agent_spec(opts):
|
|
580
|
+
from whatap.util.native_process import AgentSpec
|
|
581
|
+
source = os.path.join(os.path.dirname(__file__), 'agent', 'linux',
|
|
582
|
+
ARCH[platform.machine()], AGENT_NAME)
|
|
583
|
+
env = os.environ.copy()
|
|
584
|
+
env.update(opts)
|
|
585
|
+
return AgentSpec(os.environ['WHATAP_HOME'], source, env)
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def _native_agent_log_path():
|
|
589
|
+
return os.path.join(os.environ.get('WHATAP_HOME', '.'), 'logs', AGENT_NAME + '.log')
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def _open_native_agent_log():
|
|
593
|
+
# managed 경로는 PIPE 를 읽지 않으므로 네이티브 stdout/stderr 를 파일로 남겨
|
|
594
|
+
# 기동 직후 실패 원인을 추적할 수 있게 한다.
|
|
595
|
+
try:
|
|
596
|
+
log_path = _native_agent_log_path()
|
|
597
|
+
os.makedirs(os.path.dirname(log_path), exist_ok=True)
|
|
598
|
+
return open(log_path, 'ab')
|
|
599
|
+
except Exception as e:
|
|
600
|
+
whatap_print('WHATAP: native agent log open failed: %s' % e)
|
|
601
|
+
return subprocess.DEVNULL
|
|
602
|
+
|
|
603
|
+
|
|
474
604
|
def go(batch=False, opts={}, llm=False):
|
|
605
|
+
if sys.platform.startswith('linux') and not batch and not llm:
|
|
606
|
+
from whatap.util.native_process import FAILED, ensure_agent_running
|
|
607
|
+
|
|
608
|
+
def on_child_exit(pid, exit_code):
|
|
609
|
+
whatap_error_log(
|
|
610
|
+
'WHATAP: native APM exited (PID: {}, exit code: {}, log: {})'.format(
|
|
611
|
+
pid, exit_code, _native_agent_log_path()),
|
|
612
|
+
log_id='WHATAP_AGENT_EXITED')
|
|
613
|
+
|
|
614
|
+
try:
|
|
615
|
+
spec = _native_agent_spec(opts)
|
|
616
|
+
result = ensure_agent_running(
|
|
617
|
+
spec, lambda: _start_go(batch, opts, llm, managed=True),
|
|
618
|
+
on_child_exit=on_child_exit)
|
|
619
|
+
if result.status == FAILED:
|
|
620
|
+
whatap_error_log('WHATAP: native APM startup failed',
|
|
621
|
+
log_id='WHATAP_AGENT_START_FAILED')
|
|
622
|
+
else:
|
|
623
|
+
whatap_print('WHATAP: native APM {} (PID: {})'.format(
|
|
624
|
+
result.status, result.pid))
|
|
625
|
+
except Exception as e:
|
|
626
|
+
whatap_error_log('WHATAP: native APM startup failed: {}'.format(e),
|
|
627
|
+
log_id='WHATAP_AGENT_START_FAILED')
|
|
628
|
+
return
|
|
629
|
+
return _start_go(batch, opts, llm)
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def _start_go(batch=False, opts={}, llm=False, managed=False):
|
|
633
|
+
# LLM 에이전트는 Linux/macOS 를 지원한다(전용 whatap_python 이
|
|
634
|
+
# $WHATAP_HOME/<os>/<arch> 에 배포됨 — linux/darwin × amd64/arm64).
|
|
635
|
+
# 그 외 OS(Windows 등)는 전용 바이너리가 없으므로 우회적 "binary not found"
|
|
636
|
+
# 대신 명시적으로 LLM 기동을 차단하고 알린다.
|
|
637
|
+
if llm and platform.system().lower() not in ('linux', 'darwin'):
|
|
638
|
+
whatap_error_log(
|
|
639
|
+
'WHATAP: LLM agent is supported on Linux/macOS only '
|
|
640
|
+
'(current OS: {0}); skipping LLM agent start'.format(platform.system()),
|
|
641
|
+
log_id='WHATAP_LLM_UNSUPPORTED_OS')
|
|
642
|
+
return
|
|
643
|
+
|
|
475
644
|
newenv=os.environ.copy()
|
|
476
645
|
newenv['WHATAP_VERSION'] = build.version
|
|
477
646
|
newenv['whatap.start'] = str(DateUtil.now())
|
|
@@ -514,7 +683,7 @@ def go(batch=False, opts={}, llm=False):
|
|
|
514
683
|
return ''
|
|
515
684
|
|
|
516
685
|
pid = read_file(home, file_name)
|
|
517
|
-
if pid and get_pname(pid).find('whatap_python') >= 0:
|
|
686
|
+
if not managed and pid and get_pname(pid).find('whatap_python') >= 0:
|
|
518
687
|
if sys.platform == 'win32':
|
|
519
688
|
# Windows: Don't kill, just skip
|
|
520
689
|
whatap_print('WHATAP: Agent already running (PID: {}). Skipping duplicate execution.'.format(pid))
|
|
@@ -532,47 +701,78 @@ def go(batch=False, opts={}, llm=False):
|
|
|
532
701
|
|
|
533
702
|
# Windows uses .exe extension
|
|
534
703
|
agent_binary_name = AGENT_NAME + '.exe' if sys.platform == 'win32' else AGENT_NAME
|
|
535
|
-
dest_agent = os.path.join(home_path, agent_binary_name)
|
|
536
704
|
|
|
537
|
-
#
|
|
538
|
-
#
|
|
539
|
-
#
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
705
|
+
# LLM mode (Unix/Linux) runs a dedicated whatap_python binary that ships
|
|
706
|
+
# separately under $WHATAP_HOME/<os>/<arch>/ (e.g. $WHATAP_HOME/linux/amd64).
|
|
707
|
+
# It is executed in place from there rather than deployed from the bundled
|
|
708
|
+
# package into $WHATAP_HOME/whatap_python, which the APM agent uses (the two
|
|
709
|
+
# are now distinct binaries and would otherwise clobber each other).
|
|
710
|
+
run_in_place = llm and sys.platform != 'win32'
|
|
711
|
+
|
|
712
|
+
if run_in_place:
|
|
713
|
+
source_cwd = os.path.join(home_path, platform.system().lower(), ARCH[platform.machine()])
|
|
714
|
+
source_agent = os.path.join(source_cwd, agent_binary_name)
|
|
715
|
+
if not os.path.exists(source_agent):
|
|
716
|
+
raise FileNotFoundError(
|
|
717
|
+
'LLM agent binary not found: {0} '
|
|
718
|
+
'(place the LLM {1} under $WHATAP_HOME/{2}/{3}/)'.format(
|
|
719
|
+
source_agent, agent_binary_name,
|
|
720
|
+
platform.system().lower(), ARCH[platform.machine()]))
|
|
721
|
+
# Ensure executable (best effort; ignore on read-only locations)
|
|
722
|
+
if not os.access(source_agent, os.X_OK):
|
|
723
|
+
try:
|
|
724
|
+
os.chmod(source_agent, 0o755)
|
|
725
|
+
except OSError as e:
|
|
726
|
+
whatap_print('WHATAP: chmod LLM agent binary failed: %s' % e)
|
|
727
|
+
agent_cmd = source_agent
|
|
552
728
|
else:
|
|
553
|
-
|
|
729
|
+
dest_agent = os.path.join(home_path, agent_binary_name)
|
|
554
730
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
731
|
+
# Try to remove existing binary
|
|
732
|
+
# Windows: File may still be locked even after waiting, ignore error and use existing binary
|
|
733
|
+
# Unix/Linux: Removal should always succeed
|
|
734
|
+
needs_deployment = False
|
|
735
|
+
if os.path.exists(dest_agent):
|
|
736
|
+
try:
|
|
737
|
+
os.remove(dest_agent)
|
|
738
|
+
needs_deployment = True
|
|
739
|
+
except (OSError, PermissionError) as e:
|
|
740
|
+
if sys.platform == 'win32':
|
|
741
|
+
# Windows: File is still locked, use existing binary
|
|
742
|
+
pass
|
|
743
|
+
else:
|
|
744
|
+
# Unix/Linux: This should not happen
|
|
745
|
+
raise
|
|
746
|
+
else:
|
|
747
|
+
needs_deployment = True
|
|
561
748
|
|
|
562
|
-
|
|
749
|
+
# Windows doesn't use architecture subdirectories (only single x64 binary for now)
|
|
750
|
+
if sys.platform == 'win32':
|
|
751
|
+
source_cwd = os.path.join(os.path.dirname(__file__), 'agent', 'windows')
|
|
752
|
+
else:
|
|
753
|
+
source_cwd = os.path.join(os.path.join(os.path.dirname(__file__), 'agent'), platform.system().lower(),
|
|
754
|
+
ARCH[platform.machine()])
|
|
563
755
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
#
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
756
|
+
source_agent = os.path.join(source_cwd, agent_binary_name)
|
|
757
|
+
|
|
758
|
+
# Deploy agent binary only if needed
|
|
759
|
+
if needs_deployment:
|
|
760
|
+
# Try symlink first (Unix/Linux or Windows with proper permissions)
|
|
761
|
+
try:
|
|
762
|
+
os.symlink(source_agent, dest_agent)
|
|
763
|
+
except (OSError, NotImplementedError):
|
|
764
|
+
# Fallback to copy on Windows or when symlink fails
|
|
765
|
+
import shutil
|
|
766
|
+
shutil.copy2(source_agent, dest_agent)
|
|
767
|
+
# Make executable on Unix-like systems
|
|
768
|
+
if sys.platform != 'win32':
|
|
769
|
+
os.chmod(dest_agent, 0o755)
|
|
770
|
+
|
|
771
|
+
# Windows uses absolute path, Unix/Linux uses relative path
|
|
772
|
+
if sys.platform == 'win32':
|
|
773
|
+
agent_cmd = os.path.join(home_path, agent_binary_name)
|
|
774
|
+
else:
|
|
775
|
+
agent_cmd = './{0}'.format(agent_binary_name)
|
|
576
776
|
|
|
577
777
|
sockfile_path = os.path.join(home_path, 'run')
|
|
578
778
|
if not os.path.exists(sockfile_path):
|
|
@@ -581,12 +781,6 @@ def go(batch=False, opts={}, llm=False):
|
|
|
581
781
|
newenv['WHATAP_PID_FILE'] = file_name
|
|
582
782
|
newenv['PYTHON_PARENT_APP_PID'] = str(os.getpid())
|
|
583
783
|
|
|
584
|
-
# Windows uses absolute path, Unix/Linux uses relative path
|
|
585
|
-
if sys.platform == 'win32':
|
|
586
|
-
agent_cmd = os.path.join(home_path, agent_binary_name)
|
|
587
|
-
else:
|
|
588
|
-
agent_cmd = './{0}'.format(agent_binary_name)
|
|
589
|
-
|
|
590
784
|
# Windows: Run in background with DETACHED_PROCESS
|
|
591
785
|
# Unix/Linux: Run in background with start_new_session
|
|
592
786
|
# 실행 인자 구성
|
|
@@ -638,6 +832,17 @@ def go(batch=False, opts={}, llm=False):
|
|
|
638
832
|
cwd=home_path, env=newenv,
|
|
639
833
|
creationflags=DETACHED_PROCESS,
|
|
640
834
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
835
|
+
elif managed:
|
|
836
|
+
go_out = _open_native_agent_log()
|
|
837
|
+
try:
|
|
838
|
+
process = subprocess.Popen(cmd_args,
|
|
839
|
+
cwd=home_path, env=newenv,
|
|
840
|
+
start_new_session=True,
|
|
841
|
+
stdout=go_out, stderr=go_out)
|
|
842
|
+
finally:
|
|
843
|
+
if go_out is not subprocess.DEVNULL:
|
|
844
|
+
go_out.close()
|
|
845
|
+
return process
|
|
641
846
|
else:
|
|
642
847
|
process = subprocess.Popen(cmd_args,
|
|
643
848
|
cwd=home_path, env=newenv,
|
|
@@ -657,7 +862,10 @@ def go(batch=False, opts={}, llm=False):
|
|
|
657
862
|
except Exception as e:
|
|
658
863
|
import traceback
|
|
659
864
|
traceback.print_exc()
|
|
660
|
-
|
|
865
|
+
# hook.log + 프로세스 로그 양쪽에 동일하게 노출
|
|
866
|
+
whatap_error_log(
|
|
867
|
+
'WHATAP: {0} AGENT ERROR: {1}'.format('LLM' if llm else 'APM', e),
|
|
868
|
+
log_id='WHATAP_LLM_AGENT_ERROR' if llm else 'WHATAP_AGENT_ERROR')
|
|
661
869
|
else:
|
|
662
870
|
whatap_print('WHATAP: AGENT UP! (process name: {}{})\n'.format(AGENT_NAME, ' --llm' if llm else ''))
|
|
663
871
|
|
|
@@ -695,8 +903,8 @@ def _service_trace(fn, service_name=None):
|
|
|
695
903
|
"""fn 을 하나의 트랜잭션(TX_START~TX_END)으로 감싼 래퍼를 반환한다.
|
|
696
904
|
|
|
697
905
|
``@method_profiling`` 데코레이터와 conf ``hook_service_patterns``
|
|
698
|
-
(whatap.trace.mod.hook_service_patterns) 의 **공용 코어**. 여기엔 agent() 부팅이
|
|
699
|
-
|
|
906
|
+
(whatap.trace.mod.hook_service_patterns) 의 **공용 코어**. 여기엔 agent() 부팅이 없다
|
|
907
|
+
(데코레이터/부트 경로가 각자 처리).
|
|
700
908
|
|
|
701
909
|
코루틴 함수는 동기 래퍼로 감싸면 트랜잭션이 start_interceptor → 코루틴 생성 →
|
|
702
910
|
end_interceptor 로 즉시 열렸다 닫히고, 실제 본문은 호출자가 await 할 때 트랜잭션
|
|
@@ -772,26 +980,7 @@ def _service_trace(fn, service_name=None):
|
|
|
772
980
|
|
|
773
981
|
|
|
774
982
|
def method_profiling(fn):
|
|
775
|
-
|
|
776
|
-
# 공개 API 라, 예외 재전파/컨텍스트 정리/async 지원으로 바꾸면 기존 사용자의
|
|
777
|
-
# 동작이 달라진다. 해당 수정은 dev 에 있고 정식 머지 때 함께 들어온다.
|
|
778
|
-
def trace(*args, **kwargs):
|
|
779
|
-
callback = None
|
|
780
|
-
try:
|
|
781
|
-
ctx = TraceContext()
|
|
782
|
-
ctx.service_name=fn.__name__
|
|
783
|
-
start_interceptor(ctx)
|
|
784
|
-
callback = fn(*args, **kwargs)
|
|
785
|
-
except Exception as e:
|
|
786
|
-
ctx = TraceContextManager.getLocalContext()
|
|
787
|
-
interceptor_step_error(e, ctx=ctx)
|
|
788
|
-
interceptor_error_log(ctx.id, e, fn, args, kwargs)
|
|
789
|
-
logging.debug('WHATAP(@method_profiling): ' + str(e),
|
|
790
|
-
extra={'id': 'WA776'}, exc_info=True)
|
|
791
|
-
finally:
|
|
792
|
-
ctx = TraceContextManager.getLocalContext()
|
|
793
|
-
end_interceptor(ctx=ctx)
|
|
794
|
-
return callback
|
|
983
|
+
trace = _service_trace(fn)
|
|
795
984
|
|
|
796
985
|
if not os.environ.get('whatap.enabled'):
|
|
797
986
|
agent()
|
|
@@ -966,6 +1155,22 @@ def get_port_number(port=6600, home=os.environ.get('WHATAP_HOME'), lock_file=Non
|
|
|
966
1155
|
|
|
967
1156
|
|
|
968
1157
|
def configPort():
|
|
1158
|
+
if sys.platform.startswith('linux') and preview_whatap_conf('apm_enabled') == 'true':
|
|
1159
|
+
from whatap.util.native_process import acquire_start_lock, configured_channel, find_existing_agent
|
|
1160
|
+
try:
|
|
1161
|
+
spec = _native_agent_spec({})
|
|
1162
|
+
with acquire_start_lock(spec):
|
|
1163
|
+
if find_existing_agent(spec):
|
|
1164
|
+
# CLI allocation must not rewrite a live bootstrap agent's channel.
|
|
1165
|
+
return configured_channel(spec.home, spec.environment, include_unix=False)[1]
|
|
1166
|
+
return _config_port()
|
|
1167
|
+
except Exception as e:
|
|
1168
|
+
# 락 타임아웃/다중 인스턴스 등은 CLI 를 죽이지 말고 기존 포트 할당으로 폴백
|
|
1169
|
+
whatap_print('WHATAP: live agent check skipped, falling back to port file: %s' % e)
|
|
1170
|
+
return _config_port()
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
def _config_port():
|
|
969
1174
|
# force_net_udp_port가 설정된 경우 lock 파일 무시하고 해당 포트 강제 사용
|
|
970
1175
|
force_port_str = preview_whatap_conf("force_net_udp_port")
|
|
971
1176
|
if force_port_str and force_port_str not in ('false', 'False'):
|
|
Binary file
|
|
Binary file
|