whatap-python 1.8.9.post0__tar.gz → 1.8.14__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.
Files changed (149) hide show
  1. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/PKG-INFO +1 -1
  2. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/__init__.py +1 -1
  3. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/agent/darwin/amd64/whatap_python +0 -0
  4. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/agent/darwin/arm64/whatap_python +0 -0
  5. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/agent/linux/amd64/whatap_python +0 -0
  6. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/agent/linux/arm64/whatap_python +0 -0
  7. whatap_python-1.8.14/whatap/build.py +4 -0
  8. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/conf/configuration.py +4 -1
  9. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/counter/__init__.py +2 -2
  10. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/counter/counter_manager.py +12 -5
  11. whatap_python-1.8.14/whatap/counter/tasks/llm_stat.py +111 -0
  12. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/net/udp_session.py +0 -3
  13. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/fastapi.py +1 -0
  14. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/wsgi.py +31 -226
  15. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/database/cxoracle.py +2 -2
  16. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/database/mysql.py +2 -3
  17. whatap_python-1.8.14/whatap/trace/mod/database/neo4j.py +90 -0
  18. whatap_python-1.8.9.post0/whatap/trace/mod/database/postgresql.py → whatap_python-1.8.14/whatap/trace/mod/database/psycopg2.py +2 -2
  19. whatap_python-1.8.14/whatap/trace/mod/database/psycopg3.py +359 -0
  20. whatap_python-1.8.9.post0/whatap/trace/mod/database/toolkit.py → whatap_python-1.8.14/whatap/trace/mod/database/sqlalchemy.py +5 -5
  21. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/database/sqlite3.py +2 -2
  22. whatap_python-1.8.14/whatap/trace/mod/database/util.py +577 -0
  23. whatap_python-1.8.14/whatap/trace/mod/httpc/httpx.py +36 -0
  24. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/llm/openai.py +25 -1
  25. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/trace_module_definition.py +10 -2
  26. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap_python.egg-info/PKG-INFO +1 -1
  27. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap_python.egg-info/SOURCES.txt +5 -2
  28. whatap_python-1.8.9.post0/whatap/build.py +0 -4
  29. whatap_python-1.8.9.post0/whatap/trace/mod/database/neo4j.py +0 -126
  30. whatap_python-1.8.9.post0/whatap/trace/mod/httpc/httpx.py +0 -24
  31. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/README.md +0 -0
  32. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/setup.cfg +0 -0
  33. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/setup.py +0 -0
  34. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/LICENSE +0 -0
  35. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/README.rst +0 -0
  36. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/__main__.py +0 -0
  37. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/bootstrap/__init__.py +0 -0
  38. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/bootstrap/sitecustomize.py +0 -0
  39. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/conf/__init__.py +0 -0
  40. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/conf/configure.py +0 -0
  41. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/conf/license.py +0 -0
  42. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/control/__init__.py +0 -0
  43. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/counter/tasks/__init__.py +0 -0
  44. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/counter/tasks/openfiledescriptor.py +0 -0
  45. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/io/__init__.py +0 -0
  46. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/io/data_inputx.py +0 -0
  47. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/io/data_outputx.py +0 -0
  48. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/net/__init__.py +0 -0
  49. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/net/async_sender.py +0 -0
  50. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/net/packet_enum.py +0 -0
  51. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/net/packet_type_enum.py +0 -0
  52. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/net/param_def.py +0 -0
  53. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/net/stackhelper.py +0 -0
  54. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/net/udp_thread.py +0 -0
  55. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/pack/__init__.py +0 -0
  56. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/pack/logSinkPack.py +0 -0
  57. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/pack/pack.py +0 -0
  58. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/pack/pack_enum.py +0 -0
  59. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/pack/tagCountPack.py +0 -0
  60. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/scripts/__init__.py +0 -0
  61. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/__init__.py +0 -0
  62. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/__init__.py +0 -0
  63. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/amqp/__init__.py +0 -0
  64. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/amqp/kombu.py +0 -0
  65. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/amqp/pika.py +0 -0
  66. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/__init__.py +0 -0
  67. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/bottle.py +0 -0
  68. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/celery.py +0 -0
  69. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/cherrypy.py +0 -0
  70. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/django.py +0 -0
  71. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/django_asgi.py +0 -0
  72. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/django_py3.py +0 -0
  73. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/flask.py +0 -0
  74. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/frappe.py +0 -0
  75. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/graphql.py +0 -0
  76. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/nameko.py +0 -0
  77. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/odoo.py +0 -0
  78. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/starlette.py +0 -0
  79. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/application/tornado.py +0 -0
  80. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/database/__init__.py +0 -0
  81. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/database/mongo.py +0 -0
  82. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/database/redis.py +0 -0
  83. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/email/__init__.py +0 -0
  84. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/email/smtp.py +0 -0
  85. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/httpc/__init__.py +0 -0
  86. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/httpc/django.py +0 -0
  87. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/httpc/httplib.py +0 -0
  88. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/httpc/requests.py +0 -0
  89. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/httpc/urllib3.py +0 -0
  90. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/llm/__init__.py +0 -0
  91. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/logging.py +0 -0
  92. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/plugin.py +0 -0
  93. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/standalone/__init__.py +0 -0
  94. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/standalone/multiple.py +0 -0
  95. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/mod/standalone/single.py +0 -0
  96. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/simple_trace_context.py +0 -0
  97. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/trace_context.py +0 -0
  98. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/trace_context_manager.py +0 -0
  99. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/trace/trace_import.py +0 -0
  100. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/__init__.py +0 -0
  101. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/bit_util.py +0 -0
  102. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/cardinality/__init__.py +0 -0
  103. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/cardinality/hyperloglog.py +0 -0
  104. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/cardinality/murmurhash.py +0 -0
  105. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/cardinality/registerset.py +0 -0
  106. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/compare_util.py +0 -0
  107. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/date_util.py +0 -0
  108. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/debug_util.py +0 -0
  109. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/escape_literal_sql.py +0 -0
  110. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/hash_util.py +0 -0
  111. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/hexa32.py +0 -0
  112. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/int_set.py +0 -0
  113. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/ip_util.py +0 -0
  114. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/keygen.py +0 -0
  115. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/linked_list.py +0 -0
  116. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/linked_map.py +0 -0
  117. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/metering_util.py +0 -0
  118. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/request_double_queue.py +0 -0
  119. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/request_queue.py +0 -0
  120. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/string_util.py +0 -0
  121. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/throttle_util.py +0 -0
  122. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/util/userid_util.py +0 -0
  123. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/__init__.py +0 -0
  124. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/blob_value.py +0 -0
  125. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/boolean_value.py +0 -0
  126. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/decimal_value.py +0 -0
  127. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/double_summary.py +0 -0
  128. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/double_value.py +0 -0
  129. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/float_array.py +0 -0
  130. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/float_value.py +0 -0
  131. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/int_array.py +0 -0
  132. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/ip4_value.py +0 -0
  133. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/list_value.py +0 -0
  134. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/long_array.py +0 -0
  135. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/long_summary.py +0 -0
  136. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/map_value.py +0 -0
  137. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/null_value.py +0 -0
  138. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/number_value.py +0 -0
  139. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/summary_value.py +0 -0
  140. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/text_array.py +0 -0
  141. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/text_hash_value.py +0 -0
  142. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/text_value.py +0 -0
  143. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/value.py +0 -0
  144. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/value/value_enum.py +0 -0
  145. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap/whatap.conf +0 -0
  146. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap_python.egg-info/dependency_links.txt +0 -0
  147. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap_python.egg-info/entry_points.txt +0 -0
  148. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap_python.egg-info/not-zip-safe +0 -0
  149. {whatap_python-1.8.9.post0 → whatap_python-1.8.14}/whatap_python.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: whatap-python
3
- Version: 1.8.9.post0
3
+ Version: 1.8.14
4
4
  Summary: Monitoring and Profiling Service
5
5
  Home-page: https://www.whatap.io
6
6
  Author: whatap
@@ -57,7 +57,7 @@ def preview_whatap_conf(option_name:str):
57
57
  현재 preview_whatap_conf 를 사용중인 옵션
58
58
  - ignore_whatap_stdout (False)
59
59
  - standalone_enabled (False)
60
- - open_file_descriptor_enabled (False)
60
+ - counter_thread_enabled (False)
61
61
  """
62
62
  value = 'false'
63
63
  try:
@@ -0,0 +1,4 @@
1
+ app = 'Python'
2
+ name = 'whatap-python'
3
+ version = '1.8.14'
4
+ release_date = '20251111'
@@ -215,6 +215,9 @@ Configuration = {
215
215
  "log_unhandled_exception": 'false',
216
216
  "threadstack_faulthandler": False,
217
217
  "max_send_queue_size": 1000,
218
- "open_file_descriptor_enabled":False,
218
+ "open_file_descriptor_enabled": False,
219
219
  "open_file_descriptor_interval":60,
220
+ "counter_thread_enabled": False,
221
+ "llm_stat_enabled" : False,
222
+ "llm_stat_interval" : 5
220
223
  }
@@ -1,9 +1,9 @@
1
1
  from .counter_manager import CounterMgr # CounterMgr 클래스 import
2
2
  from whatap import preview_whatap_conf
3
3
 
4
- open_file_descriptor_enabled = preview_whatap_conf("open_file_descriptor_enabled")
4
+ counter_thread_enabled = preview_whatap_conf("counter_thread_enabled")
5
5
 
6
- if open_file_descriptor_enabled != 'false':
6
+ if counter_thread_enabled != 'false':
7
7
  mgr = CounterMgr()
8
8
  mgr.setDaemon(True)
9
9
  mgr.start()
@@ -3,20 +3,27 @@ import time
3
3
  import logging
4
4
  from threading import Thread
5
5
  from .tasks.openfiledescriptor import OpenFileDescriptorTask
6
+ from .tasks.llm_stat import LLMStatTask
6
7
 
7
8
  #현재 디렉토리 아래 tasks 가 있고 그안의 openfiledescriptor.py 파일에 OpenFileDescriptorTask 클래스를 import 하고싶어.
8
9
 
9
10
  class CounterMgr(Thread):
11
+ _instance = None
10
12
  def __init__(self):
11
13
  super(CounterMgr, self).__init__() # Thread 초기화
12
14
  self.tasks = list()
13
15
  self.last_executed = {} # 각 task의 마지막 실행 시간을 기록하기 위한 딕셔너리
14
-
16
+ CounterMgr._instance = self
17
+
15
18
  def run(self):
16
- # OpenFileDescriptorTask 인스턴스 생성 후 tasks에 추가
17
- task = OpenFileDescriptorTask()
18
- self.tasks.append(task)
19
- self.last_executed[task.name()] = 0 # 각 task의 마지막 실행 시간을 초기화
19
+ ofd_task = OpenFileDescriptorTask()
20
+ self.tasks.append(ofd_task)
21
+ self.last_executed[ofd_task.name()] = 0 # 각 task의 마지막 실행 시간을 초기화
22
+
23
+ llm_task = LLMStatTask()
24
+ self.tasks.append(llm_task)
25
+ self.last_executed[llm_task.name()] = 0 # 각 task의 마지막 실행 시간을 초기화
26
+
20
27
  while True:
21
28
  current_time = time.time() # 현재 시간을 초 단위로 가져옴
22
29
  time.sleep(1) # 0.1초마다 확인 (부하 줄이기)
@@ -0,0 +1,111 @@
1
+ import whatap.net.async_sender as async_sender
2
+ import whatap.io as whatapio
3
+ from whatap.pack import tagCountPack
4
+ from whatap.pack.tagCountPack import TagCountPack
5
+ from whatap.util.hash_util import HashUtil
6
+ from whatap import DateUtil
7
+
8
+ import os
9
+ import time
10
+ from typing import List, Dict, Tuple
11
+ from collections import defaultdict
12
+
13
+ currentpid = os.getpid()
14
+
15
+
16
+ class LLMStatTask:
17
+ def __init__(self):
18
+ self.llm_stats = {
19
+ 'model_calls': defaultdict(int),
20
+ 'model_prompt_tokens': defaultdict(int),
21
+ 'model_completion_tokens': defaultdict(int)
22
+ }
23
+
24
+ def name(self):
25
+ return "LLMStatTask"
26
+
27
+ def interval(self):
28
+ from whatap.conf.configure import Configure as conf
29
+ return int(getattr(conf, 'llm_stat_interval', 5))
30
+
31
+ def process(self):
32
+ from whatap.conf.configure import Configure as conf
33
+ enabled = getattr(conf, 'llm_stat_enabled', False)
34
+ if not enabled:
35
+ return
36
+
37
+ stats = self.get_current_stats()
38
+
39
+ if not stats['model_calls']:
40
+ return
41
+
42
+ try:
43
+ p = TagCountPack()
44
+ p.time = DateUtil.now() // 1000 * 1000
45
+ p.Category = "llm_stat"
46
+ p.tags.putAuto("pid", currentpid)
47
+ p.tags.putAuto("!rectype", 2)
48
+
49
+ model_id_list = p.fields.newList("@id")
50
+ model_name_list = p.fields.newList("model_name")
51
+ call_count_list = p.fields.newList("call_count")
52
+ prompt_tokens_list = p.fields.newList("prompt_tokens")
53
+ completion_tokens_list = p.fields.newList("completion_tokens")
54
+ total_tokens_list = p.fields.newList("total_tokens")
55
+
56
+ for model_name, count in stats['model_calls'].items():
57
+ prompt_tokens = stats['model_prompt_tokens'][model_name]
58
+ completion_tokens = stats['model_completion_tokens'][model_name]
59
+ total_tokens = prompt_tokens + completion_tokens
60
+
61
+ model_id_list.addLong(HashUtil.hashFromString(model_name))
62
+ model_name_list.addString(model_name)
63
+ call_count_list.addLong(count)
64
+ prompt_tokens_list.addLong(prompt_tokens)
65
+ completion_tokens_list.addLong(completion_tokens)
66
+ total_tokens_list.addLong(total_tokens)
67
+
68
+
69
+ p.pcode = getattr(conf, 'PCODE', 0)
70
+ bout = whatapio.DataOutputX()
71
+ bout.writePack(p, None)
72
+ packbytes = bout.toByteArray()
73
+
74
+ async_sender.send_relaypack(packbytes)
75
+ self.reset_stats()
76
+
77
+ except Exception as e:
78
+ import traceback
79
+ traceback.print_exc()
80
+
81
+
82
+ def get_current_stats(self) -> Dict:
83
+ return {
84
+ 'model_calls': dict(self.llm_stats['model_calls']),
85
+ 'model_prompt_tokens': dict(self.llm_stats['model_prompt_tokens']),
86
+ 'model_completion_tokens': dict(self.llm_stats['model_completion_tokens'])
87
+ }
88
+
89
+ def prepare_model_data(self, model_calls: Dict[str, int]) -> Tuple[List[str], List[int]]:
90
+ if not model_calls:
91
+ return [], []
92
+
93
+ models = list(model_calls.keys())
94
+ call_count = [model_calls[model] for model in models]
95
+
96
+ return models, call_count
97
+
98
+ def update_stats(self, prompt_tokens: int, completion_tokens: int,
99
+ model_name: str):
100
+ self.llm_stats['model_calls'][model_name] += 1
101
+ self.llm_stats['model_prompt_tokens'][model_name] += prompt_tokens
102
+ self.llm_stats['model_completion_tokens'][model_name] += completion_tokens
103
+
104
+
105
+
106
+ def reset_stats(self):
107
+ self.llm_stats = {
108
+ 'model_calls': defaultdict(int),
109
+ 'model_prompt_tokens': defaultdict(int),
110
+ 'model_completion_tokens': defaultdict(int)
111
+ }
@@ -212,9 +212,6 @@ class UdpSession(object):
212
212
  datas.append(data)
213
213
 
214
214
  cls.send_packet(PacketTypeEnum.ACTIVE_STACK, None, datas)
215
- stats = TraceContextManager.getActiveStats()
216
- datas = [','.join([str(x) for x in stats])]
217
- cls.send_packet(PacketTypeEnum.ACTIVE_STATS, None, datas)
218
215
  else:
219
216
  # param pack
220
217
  # format: "[packetType], [ctx], [datas: xxxx xxxx xxxx]"
@@ -443,6 +443,7 @@ def instrument_applications(module):
443
443
  async def trace(instance, scope, receive, send):
444
444
  if scope["type"] != "http":
445
445
  await fn(instance, scope, receive, send)
446
+ return
446
447
  TraceContext()
447
448
  try:
448
449
  await fn(instance, scope, receive, send)
@@ -77,6 +77,36 @@ def trace_handler(fn, start=False, preload=None):
77
77
 
78
78
  return handler
79
79
 
80
+ def async_trace_handler(fn, start=False, preload=None):
81
+ def handler(func):
82
+ @wraps(func)
83
+ async def wrapper(*args, **kwargs):
84
+ if preload:
85
+ preload(*args, **kwargs)
86
+
87
+ ctx = TraceContextManager.getLocalContext()
88
+
89
+ if not start and not ctx:
90
+ return await fn(*args, **kwargs)
91
+
92
+ try:
93
+ callback = await func(*args, **kwargs)
94
+ except Exception as e:
95
+ if ctx and ctx.error_step == e:
96
+ ctx.error_step = None
97
+ raise e
98
+ raise
99
+ else:
100
+ if ctx and ctx.error_step:
101
+ e = ctx.error_step
102
+ ctx.error_step = None
103
+ raise e
104
+ return callback
105
+
106
+ return wrapper
107
+
108
+ return handler
109
+
80
110
 
81
111
  def start_interceptor(ctx):
82
112
  if conf.dev:
@@ -263,6 +293,7 @@ def interceptor_error(status_code, errors, ctx=None):
263
293
 
264
294
 
265
295
  def interceptor_step_error(e, ctx=None):
296
+ from whatap.trace.mod.database.util import extract_db_error_message
266
297
  if not ctx:
267
298
  ctx = TraceContextManager.getLocalContext()
268
299
  if not ctx:
@@ -301,25 +332,6 @@ def interceptor_step_error(e, ctx=None):
301
332
  async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
302
333
 
303
334
 
304
- def extract_db_error_message(e):
305
- try:
306
- # PostgreSQL (psycopg2)
307
- if hasattr(e, 'pgcode') and hasattr(e, 'pgerror'):
308
- return str(e.pgerror)
309
-
310
- # MySQL (PyMySQL, mysql-connector-python)
311
- if hasattr(e, 'args') and len(e.args) > 1 and isinstance(e.args[1], str):
312
- return e.args[1]
313
-
314
- # 기본 에러 메시지
315
- if hasattr(e, 'args') and len(e.args) > 0:
316
- if isinstance(e.args[0], str):
317
- return e.args[0]
318
- return str(e.args[0])
319
-
320
- return str(e)
321
- except:
322
- return "Unknown database error"
323
335
 
324
336
 
325
337
  def interceptor_httpc_request(fn, httpc_url, *args, **kwargs):
@@ -385,213 +397,6 @@ def interceptor_sock_connect(fn, *args, **kwargs):
385
397
  ctx.socket_connecting = False
386
398
 
387
399
 
388
- def interceptor_db_con(fn, db_info, *args, **kwargs):
389
- ctx = TraceContextManager.getLocalContext()
390
- if not ctx:
391
- return fn(*args, **kwargs)
392
-
393
- start_time = DateUtil.nowSystem()
394
- ctx.start_time = start_time
395
-
396
- ctx.db_opening = True
397
- try:
398
- callback = fn(*args, **kwargs)
399
- finally:
400
- ctx.db_opening = False
401
-
402
- if not kwargs:
403
- kwargs = dict(
404
- x.split('=') for x in re.sub(r'\s*=\s*', '=', args[0]).split())
405
-
406
- db_type = db_info.get('type')
407
-
408
- if db_type == "sqlite":
409
- text = "sqlite:"
410
- else:
411
- text = '{}://'.format(db_type)
412
- text += kwargs.get('user', '')
413
- text += "@"
414
- text += kwargs.get('host', kwargs.get('dsn', ''))
415
- text += '/'
416
-
417
- text += kwargs.get('database', kwargs.get('db', kwargs.get('dbname', '')))
418
- ctx.active_dbc = text
419
- ctx.lctx['dbc'] = text
420
-
421
- ctx.active_dbc = 0
422
-
423
- datas = [text]
424
- ctx.elapsed = DateUtil.nowSystem() - start_time
425
- async_sender.send_packet(PacketTypeEnum.TX_DB_CONN, ctx, datas)
426
-
427
- return callback
428
-
429
-
430
- def addQuoteDict(arg_dict):
431
- quoted_dict = dict()
432
-
433
- for k, v in arg_dict.items():
434
- if isinstance(v, str):
435
- quoted_dict[k] = "'" + v.replace("'", "\\'") + "'"
436
- else:
437
- quoted_dict[k] = v
438
-
439
- return quoted_dict
440
-
441
-
442
- def addQuoteList(arg_list):
443
- quoted_list = list()
444
-
445
- for v in arg_list:
446
- if isinstance(v, str):
447
- quoted_list.append("'" + v.replace("'", "\\'") + "'")
448
- else:
449
- quoted_list.append("'" + str(v) + "'")
450
-
451
- return tuple(quoted_list)
452
-
453
- def neo4jQuery(query,paremeter):
454
- neo4j_query = query
455
-
456
- for key, value in paremeter.items():
457
- placeholder = f"${key}"
458
- replacement = f"'{str(value)}'"
459
- neo4j_query = neo4j_query.replace(placeholder, replacement)
460
-
461
- return neo4j_query
462
-
463
- def sqliteQuery(query, paremeter):
464
- sqlite_query = query
465
-
466
- def quote(v):
467
- if v is None: return "NULL"
468
- if isinstance(v, (bytes, bytearray)): return "X'" + v.hex() + "'"
469
- return "'" + str(v).replace("'", "''") + "'"
470
-
471
- # 이름 기반: :name / @name / $name (단일 세트)
472
- if isinstance(paremeter, dict):
473
- for pfx in (':', '@', '$'):
474
- for k, v in paremeter.items():
475
- sqlite_query = sqlite_query.replace(f"{pfx}{k}", quote(v))
476
- return sqlite_query
477
-
478
- # 포지셔널 + executemany 분기
479
- if isinstance(paremeter, (list, tuple)):
480
- # ── executemany: [tuple|dict, ...] ──
481
- if isinstance(paremeter, list) and paremeter and isinstance(paremeter[0], (tuple, dict)):
482
- stmts = []
483
- for params in paremeter:
484
- sqlite_query = query
485
- if isinstance(params, dict):
486
- for pfx in (':', '@', '$'):
487
- for k, v in params.items():
488
- sqlite_query = sqlite_query.replace(f"{pfx}{k}", quote(v))
489
- else: # tuple
490
- for i, v in enumerate(params, 1):
491
- sqlite_query = sqlite_query.replace(f"?{i}", quote(v))
492
- for v in params:
493
- sqlite_query = sqlite_query.replace('?', quote(v), 1)
494
- stmts.append(sqlite_query)
495
- return ";\n".join(stmts)
496
-
497
- # ── 단일 포지셔널 세트 (tuple 또는 스칼라 list) ──
498
- seq = paremeter if isinstance(paremeter, tuple) else tuple(paremeter)
499
- for i, v in enumerate(seq, 1):
500
- sqlite_query = sqlite_query.replace(f"?{i}", quote(v))
501
- for v in seq:
502
- sqlite_query = sqlite_query.replace('?', quote(v), 1)
503
- return sqlite_query
504
-
505
- return sqlite_query
506
-
507
- def interceptor_db_execute(fn, db_info, *args, **kwargs):
508
- ctx = TraceContextManager.getLocalContext()
509
- # sendDebugProfile(ctx, 'interceptor_db_execute step -1')
510
- self = args[0]
511
- db_type = db_info.get('type')
512
- query = None
513
-
514
- if db_type == "neo4j":
515
- try:
516
- query = neo4jQuery(args[1], kwargs)
517
- except Exception as e:
518
- pass
519
-
520
- elif db_type == "sqlite" and len(args) > 2:
521
- try:
522
- query = sqliteQuery(args[1],args[2])
523
- except Exception as e:
524
- pass
525
-
526
- else:
527
- if len(args) > 2 and type(args[2]) == dict and args[2]:
528
- try:
529
- query = args[1] % addQuoteDict(args[2])
530
- except Exception as e:
531
- pass
532
- if len(args) > 2 and type(args[2]) in (list, tuple) and args[2]:
533
- try:
534
- query = args[1] % addQuoteList(args[2])
535
- except Exception as e:
536
- pass
537
- try:
538
- if not query:
539
- query = args[1].decode()
540
- except Exception as e:
541
- query = args[1]
542
-
543
- if not query:
544
- return fn(*args, **kwargs)
545
-
546
- start_time = DateUtil.nowSystem()
547
- ctx.start_time = start_time
548
- ctx.active_sqlhash = query
549
- try:
550
- callback = fn(*args, **kwargs)
551
- return callback
552
- except Exception as e:
553
- interceptor_step_error(e)
554
- finally:
555
- datas = [ctx.lctx.get('dbc', ''), query, str(self.rowcount)]
556
- ctx.elapsed = DateUtil.nowSystem() - start_time
557
- async_sender.send_packet(PacketTypeEnum.TX_SQL, ctx, datas)
558
- count = self.rowcount
559
-
560
- if (count is not None) and (count > -1):
561
- desc = '{0}: {1}'.format('Fetch count', count)
562
- datas = [' ', ' ', desc]
563
- ctx.elapsed = 0
564
- async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
565
-
566
- ctx.active_sqlhash = 0
567
-
568
-
569
- def interceptor_db_close(fn, *args, **kwargs):
570
- ctx = TraceContextManager.getLocalContext()
571
- ctx.db_opening = False
572
-
573
- if not conf.profile_dbc_close:
574
- try:
575
- return fn(*args, **kwargs)
576
- except Exception as e:
577
- interceptor_step_error(e)
578
- finally:
579
- return
580
- start_time = DateUtil.nowSystem()
581
- ctx.start_time = start_time
582
-
583
- try:
584
- callback = fn(*args, **kwargs)
585
- return callback
586
- except Exception as e:
587
- interceptor_step_error(e)
588
- finally:
589
- text = 'DB: Close Connection.'
590
- datas = [' ', ' ', text]
591
- ctx.elapsed = DateUtil.nowSystem() - start_time
592
- async_sender.send_packet(PacketTypeEnum.TX_MSG, ctx, datas)
593
-
594
-
595
400
  check_seq = 1
596
401
 
597
402
 
@@ -1,6 +1,6 @@
1
1
  from whatap.trace import get_dict
2
- from whatap.trace.mod.application.wsgi import trace_handler, \
3
- interceptor_db_con, interceptor_db_execute, interceptor_db_close
2
+ from whatap.trace.mod.application.wsgi import trace_handler
3
+ from whatap.trace.mod.database.util import interceptor_db_con, interceptor_db_execute, interceptor_db_close
4
4
 
5
5
  db_info = {}
6
6
 
@@ -1,7 +1,6 @@
1
1
  from whatap.trace import get_dict
2
- from whatap.trace.mod.application.wsgi import trace_handler, \
3
- interceptor_db_con, interceptor_db_execute, interceptor_db_close
4
-
2
+ from whatap.trace.mod.application.wsgi import trace_handler
3
+ from whatap.trace.mod.database.util import interceptor_db_con, interceptor_db_execute, interceptor_db_close
5
4
  db_info = {}
6
5
 
7
6
  def instrument_MySQLdb(module):
@@ -0,0 +1,90 @@
1
+ from whatap.trace.mod.application.wsgi import trace_handler
2
+ from whatap.trace.mod.database.util import interceptor_db_con, interceptor_db_execute, interceptor_db_close
3
+
4
+ db_info = {}
5
+
6
+
7
+
8
+
9
+
10
+ def instrument_neo4j(module):
11
+
12
+ if hasattr(module, 'GraphDatabase'):
13
+ orig_driver = module.GraphDatabase.driver
14
+ def wrapper(fn):
15
+ @trace_handler(fn)
16
+ def trace(*args, **kwargs):
17
+ db_info = {'type': 'neo4j'}
18
+
19
+ if args:
20
+ db_info['uri'] = args[0]
21
+
22
+ auth = kwargs.get('auth') or (args[1] if len(args) > 1 else None)
23
+ if auth:
24
+ user = getattr(auth, 'principal', None) or (auth[0] if isinstance(auth, tuple) else None)
25
+ if user:
26
+ db_info['user'] = user
27
+
28
+ callback = interceptor_db_con(fn, db_info, *args, **kwargs)
29
+ return callback
30
+
31
+ return trace
32
+ module.GraphDatabase.driver = wrapper(orig_driver)
33
+
34
+
35
+
36
+ if hasattr(module, 'Driver'):
37
+ orig_driver_close = module.Driver.close
38
+ def wrapper(fn):
39
+ @trace_handler(fn)
40
+ def trace(driver, *args, **kwargs):
41
+ callback = interceptor_db_close(fn, driver, *args, **kwargs)
42
+ return callback
43
+
44
+ return trace
45
+
46
+ module.Driver.close = wrapper(orig_driver_close)
47
+
48
+ orig = module.Session.run
49
+ def wrapper(fn):
50
+ @trace_handler(fn)
51
+ def trace(session, *args, **kwargs):
52
+ db_info = {'type': 'neo4j'}
53
+ try:
54
+ setattr(session, 'rowcount', -1)
55
+ except Exception as e:
56
+ raise e
57
+ callback = interceptor_db_execute(fn, db_info, session, *args, **kwargs)
58
+
59
+ return callback
60
+ return trace
61
+ module.Session.run = wrapper(orig)
62
+
63
+ def wrapper(fn):
64
+ @trace_handler(fn)
65
+ def trace(tx, *args, **kwargs):
66
+ session = None
67
+ try:
68
+ session = tx._on_closed.__self__
69
+ except AttributeError:
70
+ return fn(tx, *args, **kwargs)
71
+ db_info = {'type': 'neo4j'}
72
+ try:
73
+ setattr(tx, 'rowcount', -1)
74
+ except Exception:
75
+ pass
76
+ callback = interceptor_db_execute(fn, db_info, tx, *args, **kwargs)
77
+ return callback
78
+
79
+ return trace
80
+
81
+ tx_classes_to_patch = ['Transaction', 'ManagedTransaction', 'BoltTransaction']
82
+ for class_name in tx_classes_to_patch:
83
+ if hasattr(module, class_name):
84
+ TxClass = getattr(module, class_name)
85
+ if hasattr(TxClass, 'run'):
86
+ original_run = getattr(TxClass, 'run')
87
+ setattr(TxClass, 'run', wrapper(original_run))
88
+
89
+
90
+
@@ -1,6 +1,6 @@
1
1
  from whatap.trace import get_dict
2
- from whatap.trace.mod.application.wsgi import trace_handler, \
3
- interceptor_db_con, interceptor_db_execute, interceptor_db_close
2
+ from whatap.trace.mod.application.wsgi import trace_handler
3
+ from whatap.trace.mod.database.util import interceptor_db_con, interceptor_db_execute, interceptor_db_close
4
4
 
5
5
  db_info = {}
6
6