cavisson-pythonagent 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. cavisson_pythonagent-0.0.1.dist-info/METADATA +32 -0
  2. cavisson_pythonagent-0.0.1.dist-info/RECORD +180 -0
  3. cavisson_pythonagent-0.0.1.dist-info/WHEEL +5 -0
  4. cavisson_pythonagent-0.0.1.dist-info/licenses/LICENSE +19 -0
  5. cavisson_pythonagent-0.0.1.dist-info/top_level.txt +1 -0
  6. pythonagent/__init__.py +22 -0
  7. pythonagent/agent/__init__.py +219 -0
  8. pythonagent/agent/internal/__init__.py +1 -0
  9. pythonagent/agent/internal/agent.py +1651 -0
  10. pythonagent/agent/internal/framesinfo.py +152 -0
  11. pythonagent/agent/internal/heap_dump.py +39 -0
  12. pythonagent/agent/internal/intercept_module.py +70 -0
  13. pythonagent/agent/internal/logs.py +122 -0
  14. pythonagent/agent/internal/metadata/__init__.py +0 -0
  15. pythonagent/agent/internal/metadata/agent_meta_data.py +276 -0
  16. pythonagent/agent/internal/proc_compat.py +75 -0
  17. pythonagent/agent/internal/profile.py +47 -0
  18. pythonagent/agent/internal/provider.py +83 -0
  19. pythonagent/agent/internal/thread_dump.py +85 -0
  20. pythonagent/agent/internal/udp.py +245 -0
  21. pythonagent/agent/internal/udp_message.py +800 -0
  22. pythonagent/agent/probes/Instrumentation/__init__.py +340 -0
  23. pythonagent/agent/probes/Instrumentation/find.py +243 -0
  24. pythonagent/agent/probes/Instrumentation/module_version_resolver.py +155 -0
  25. pythonagent/agent/probes/Instrumentation/new_parser.py +64 -0
  26. pythonagent/agent/probes/Instrumentation/parser.py +129 -0
  27. pythonagent/agent/probes/__init__.py +219 -0
  28. pythonagent/agent/probes/base.py +303 -0
  29. pythonagent/agent/probes/cache/__init__.py +51 -0
  30. pythonagent/agent/probes/cache/redis.py +119 -0
  31. pythonagent/agent/probes/cache/redis_asyncio.py +83 -0
  32. pythonagent/agent/probes/coroutines/__init__.py +1 -0
  33. pythonagent/agent/probes/coroutines/asyncio.py +63 -0
  34. pythonagent/agent/probes/elasticdb/__init__.py +7 -0
  35. pythonagent/agent/probes/elasticdb/aelastic.py +54 -0
  36. pythonagent/agent/probes/frameworks/__init__.py +27 -0
  37. pythonagent/agent/probes/frameworks/agentprofiler.py +105 -0
  38. pythonagent/agent/probes/frameworks/aiohttp_web.py +155 -0
  39. pythonagent/agent/probes/frameworks/aisess.py +151 -0
  40. pythonagent/agent/probes/frameworks/asgi.py +340 -0
  41. pythonagent/agent/probes/frameworks/bottle.py +27 -0
  42. pythonagent/agent/probes/frameworks/cherry.py +25 -0
  43. pythonagent/agent/probes/frameworks/django.py +128 -0
  44. pythonagent/agent/probes/frameworks/falcon.py +21 -0
  45. pythonagent/agent/probes/frameworks/fastapi.py +35 -0
  46. pythonagent/agent/probes/frameworks/flask.py +30 -0
  47. pythonagent/agent/probes/frameworks/pyramid.py +56 -0
  48. pythonagent/agent/probes/frameworks/test.py +108 -0
  49. pythonagent/agent/probes/frameworks/tornado_async_web.py +117 -0
  50. pythonagent/agent/probes/frameworks/tornado_web.py +133 -0
  51. pythonagent/agent/probes/frameworks/wsgi.py +353 -0
  52. pythonagent/agent/probes/grpc/__init__.py +76 -0
  53. pythonagent/agent/probes/grpc/client_interceptor.py +132 -0
  54. pythonagent/agent/probes/grpc/server_interceptor.py +129 -0
  55. pythonagent/agent/probes/havoc/__init__.py +0 -0
  56. pythonagent/agent/probes/havoc/custom_memory_stress.py +186 -0
  57. pythonagent/agent/probes/havoc/custom_thread_stress.py +187 -0
  58. pythonagent/agent/probes/havoc/havoc_constants.py +218 -0
  59. pythonagent/agent/probes/havoc/havoc_manager.py +981 -0
  60. pythonagent/agent/probes/http/__init__.py +49 -0
  61. pythonagent/agent/probes/http/aiohttp_client.py +59 -0
  62. pythonagent/agent/probes/http/boto.py +12 -0
  63. pythonagent/agent/probes/http/httplib.py +110 -0
  64. pythonagent/agent/probes/http/httpx_client.py +116 -0
  65. pythonagent/agent/probes/http/requests.py +15 -0
  66. pythonagent/agent/probes/http/tornado_httpclient.py +85 -0
  67. pythonagent/agent/probes/http/urllib3.py +16 -0
  68. pythonagent/agent/probes/langchain/__init__.py +21 -0
  69. pythonagent/agent/probes/langchain/base_tool.py +95 -0
  70. pythonagent/agent/probes/langchain/langchain_community.py +136 -0
  71. pythonagent/agent/probes/langchain/langchain_core.py +32 -0
  72. pythonagent/agent/probes/langchain/langchain_openai.py +110 -0
  73. pythonagent/agent/probes/logging/__init__.py +106 -0
  74. pythonagent/agent/probes/message_brokers/__init__.py +4 -0
  75. pythonagent/agent/probes/message_brokers/pika.py +126 -0
  76. pythonagent/agent/probes/mongodb/__init__.py +6 -0
  77. pythonagent/agent/probes/mongodb/pymongo.py +286 -0
  78. pythonagent/agent/probes/openai/__init__.py +3 -0
  79. pythonagent/agent/probes/openai/openai.py +797 -0
  80. pythonagent/agent/probes/span.py +101 -0
  81. pythonagent/agent/probes/sql/__init__.py +13 -0
  82. pythonagent/agent/probes/sql/botocores3.py +51 -0
  83. pythonagent/agent/probes/sql/dbapi.py +285 -0
  84. pythonagent/agent/probes/sql/dynamodb.py +90 -0
  85. pythonagent/agent/probes/sql/mysql_connector.py +24 -0
  86. pythonagent/agent/probes/sql/mysql_connector_cext.py +24 -0
  87. pythonagent/agent/probes/sql/mysqldb.py +43 -0
  88. pythonagent/agent/probes/sql/psycopg2.py +174 -0
  89. pythonagent/agent/probes/sql/pymysql.py +25 -0
  90. pythonagent/bootstrap/__init__.py +0 -0
  91. pythonagent/bootstrap/cav_gunicorn.py +26 -0
  92. pythonagent/bootstrap/cavagent_lambda_wrapper.py +291 -0
  93. pythonagent/bootstrap/run.py +47 -0
  94. pythonagent/bootstrap/sitecustomize.py +287 -0
  95. pythonagent/cavisson/netdiagnostics/CavAgent/instrumentationprofile.json +26 -0
  96. pythonagent/cavisson/netdiagnostics/CavAgent/interceptor_points.txt +29 -0
  97. pythonagent/cavisson/netdiagnostics/python/CavAgent/instrumentationprofile.json +42 -0
  98. pythonagent/cavisson/netdiagnostics/python/CavAgent/interceptor_points.txt +29 -0
  99. pythonagent/cavisson/netdiagnostics/python/config/ndsettings.conf +6 -0
  100. pythonagent/config.py +279 -0
  101. pythonagent/find.py +72 -0
  102. pythonagent/find_mod_cls_name.py +54 -0
  103. pythonagent/lang.py +131 -0
  104. pythonagent/lib.py +91 -0
  105. pythonagent/main/__init__.py +0 -0
  106. pythonagent/main/pytrace/__init__.py +79 -0
  107. pythonagent/main/pytrace/commands/__init__.py +0 -0
  108. pythonagent/main/pytrace/commands/auto_discovery.py +25 -0
  109. pythonagent/main/pytrace/commands/run.py +401 -0
  110. pythonagent/main/pytrace/pytrace.py +133 -0
  111. pythonagent/main/wsgi.py +6 -0
  112. pythonagent/main.py +27 -0
  113. pythonagent/run.py +46 -0
  114. pythonagent/sqins.py +8 -0
  115. pythonagent/test.py +73 -0
  116. pythonagent/utils.py +168 -0
  117. pythonagent/vendor/__init__.py +0 -0
  118. pythonagent/vendor/pympler/__init__.py +1 -0
  119. pythonagent/vendor/pympler/asizeof.py +2810 -0
  120. pythonagent/vendor/pympler/charts.py +62 -0
  121. pythonagent/vendor/pympler/classtracker.py +590 -0
  122. pythonagent/vendor/pympler/classtracker_stats.py +780 -0
  123. pythonagent/vendor/pympler/garbagegraph.py +80 -0
  124. pythonagent/vendor/pympler/mprofile.py +97 -0
  125. pythonagent/vendor/pympler/muppy.py +275 -0
  126. pythonagent/vendor/pympler/panels.py +115 -0
  127. pythonagent/vendor/pympler/process.py +238 -0
  128. pythonagent/vendor/pympler/py.typed +0 -0
  129. pythonagent/vendor/pympler/refbrowser.py +451 -0
  130. pythonagent/vendor/pympler/refgraph.py +350 -0
  131. pythonagent/vendor/pympler/summary.py +321 -0
  132. pythonagent/vendor/pympler/tracker.py +267 -0
  133. pythonagent/vendor/pympler/util/__init__.py +0 -0
  134. pythonagent/vendor/pympler/util/bottle.py +3809 -0
  135. pythonagent/vendor/pympler/util/compat.py +23 -0
  136. pythonagent/vendor/pympler/util/stringutils.py +77 -0
  137. pythonagent/vendor/pympler/web.py +346 -0
  138. pythonagent/vendor/werkzeug/__init__.py +20 -0
  139. pythonagent/vendor/werkzeug/_compat.py +228 -0
  140. pythonagent/vendor/werkzeug/_internal.py +473 -0
  141. pythonagent/vendor/werkzeug/_reloader.py +341 -0
  142. pythonagent/vendor/werkzeug/datastructures.py +3120 -0
  143. pythonagent/vendor/werkzeug/debug/__init__.py +498 -0
  144. pythonagent/vendor/werkzeug/debug/console.py +218 -0
  145. pythonagent/vendor/werkzeug/debug/repr.py +297 -0
  146. pythonagent/vendor/werkzeug/debug/tbtools.py +628 -0
  147. pythonagent/vendor/werkzeug/exceptions.py +829 -0
  148. pythonagent/vendor/werkzeug/filesystem.py +64 -0
  149. pythonagent/vendor/werkzeug/formparser.py +584 -0
  150. pythonagent/vendor/werkzeug/http.py +1307 -0
  151. pythonagent/vendor/werkzeug/local.py +420 -0
  152. pythonagent/vendor/werkzeug/middleware/__init__.py +25 -0
  153. pythonagent/vendor/werkzeug/middleware/dispatcher.py +66 -0
  154. pythonagent/vendor/werkzeug/middleware/http_proxy.py +219 -0
  155. pythonagent/vendor/werkzeug/middleware/lint.py +408 -0
  156. pythonagent/vendor/werkzeug/middleware/profiler.py +132 -0
  157. pythonagent/vendor/werkzeug/middleware/proxy_fix.py +169 -0
  158. pythonagent/vendor/werkzeug/middleware/shared_data.py +293 -0
  159. pythonagent/vendor/werkzeug/posixemulation.py +117 -0
  160. pythonagent/vendor/werkzeug/routing.py +2210 -0
  161. pythonagent/vendor/werkzeug/security.py +249 -0
  162. pythonagent/vendor/werkzeug/serving.py +1117 -0
  163. pythonagent/vendor/werkzeug/test.py +1123 -0
  164. pythonagent/vendor/werkzeug/testapp.py +241 -0
  165. pythonagent/vendor/werkzeug/urls.py +1138 -0
  166. pythonagent/vendor/werkzeug/useragents.py +202 -0
  167. pythonagent/vendor/werkzeug/utils.py +778 -0
  168. pythonagent/vendor/werkzeug/wrappers/__init__.py +36 -0
  169. pythonagent/vendor/werkzeug/wrappers/accept.py +50 -0
  170. pythonagent/vendor/werkzeug/wrappers/auth.py +33 -0
  171. pythonagent/vendor/werkzeug/wrappers/base_request.py +673 -0
  172. pythonagent/vendor/werkzeug/wrappers/base_response.py +700 -0
  173. pythonagent/vendor/werkzeug/wrappers/common_descriptors.py +341 -0
  174. pythonagent/vendor/werkzeug/wrappers/cors.py +100 -0
  175. pythonagent/vendor/werkzeug/wrappers/etag.py +304 -0
  176. pythonagent/vendor/werkzeug/wrappers/json.py +145 -0
  177. pythonagent/vendor/werkzeug/wrappers/request.py +49 -0
  178. pythonagent/vendor/werkzeug/wrappers/response.py +84 -0
  179. pythonagent/vendor/werkzeug/wrappers/user_agent.py +14 -0
  180. pythonagent/vendor/werkzeug/wsgi.py +1000 -0
@@ -0,0 +1,287 @@
1
+ """Trick Python into loading the agent.
2
+ """
3
+ from __future__ import unicode_literals
4
+
5
+ # ---------------------------------------------------------------------------
6
+ # sitecustomize chaining
7
+ # ---------------------------------------------------------------------------
8
+ # WHY THIS EXISTS
9
+ #
10
+ # The install script puts the agent's site-packages directory on PYTHONPATH:
11
+ #
12
+ # export PYTHONPATH=${NDHOME}/python/site-packages:${PYTHONPATH}
13
+ #
14
+ # At interpreter startup, sys.path is built as:
15
+ # [script's own dir] + [PYTHONPATH entries, in order] + [stdlib / venv site-packages]
16
+ #
17
+ # That puts our directory at sys.path[1] — right after the script's own dir,
18
+ # and ahead of the application's virtualenv, ahead of any co-tool's install
19
+ # location, ahead of basically everything else on the machine.
20
+ #
21
+ # Python auto-imports a module named `sitecustomize` on every interpreter
22
+ # startup if one exists anywhere on sys.path — and it imports ONLY THE FIRST
23
+ # ONE IT FINDS. That auto-load is exactly what lets this agent instrument an
24
+ # application with zero code changes: no import statement, no app code
25
+ # edited, just install + set PYTHONPATH and the agent wires itself in before
26
+ # the app even starts running.
27
+ #
28
+ # But that same "found first, wins, no error if others are skipped" behavior
29
+ # is also what breaks other tools. Because our directory sits ahead of
30
+ # everything else, OUR sitecustomize.py wins the search every time — even on
31
+ # a machine where the monitored application (or a co-tool: Sentry,
32
+ # coverage.py, pytest-cov, virtualenv bootstrapping, etc.) ships its own
33
+ # sitecustomize.py for its own startup logic. Python does not merge them,
34
+ # does not warn, and does not fall through to the next one. Whichever
35
+ # sitecustomize loses the race is just silently never imported — its setup
36
+ # never runs. From the customer's point of view, installing this agent would
37
+ # look like it broke coverage reporting or killed Sentry init, with no error
38
+ # anywhere to explain why.
39
+ #
40
+ # So this agent cannot just let auto-load do its thing — it has to actively
41
+ # go find whatever sitecustomize.py *would* have been loaded if we weren't
42
+ # here, and run it ourselves, in full, before continuing with our own
43
+ # bootstrap. That's the chaining below.
44
+ #
45
+ # HOW IT WORKS
46
+ # 1. CPython adds the module to sys.modules BEFORE executing its body, so
47
+ # sys.modules['sitecustomize'] already points to our (partially-
48
+ # initialised) module object when this code runs.
49
+ # 2. We temporarily remove our bootstrap directory from sys.path so the
50
+ # next import search skips us and is forced to look further down
51
+ # sys.path — i.e. at wherever the app's or co-tool's own
52
+ # sitecustomize.py actually lives.
53
+ # 3. We drop 'sitecustomize' from sys.modules so `import sitecustomize`
54
+ # triggers a fresh file search instead of returning a cache hit (which
55
+ # would just hand back ourselves again).
56
+ # 4. We import the next sitecustomize — it runs in full, doing whatever
57
+ # setup it was originally supposed to do.
58
+ # 5. In `finally` we restore our directory in sys.path at its original
59
+ # position and re-register ourselves in sys.modules so the rest of this
60
+ # file (the agent bootstrap) completes normally, and so that for the
61
+ # rest of the process's lifetime, `sys.modules['sitecustomize']`
62
+ # resolves back to us, not the chained module.
63
+ import sys as _sys
64
+ import os as _os
65
+
66
+ _sc_this_dir = _os.path.dirname(_os.path.abspath(__file__))
67
+ _sc_this_dir_norm = _os.path.normcase(_sc_this_dir)
68
+
69
+ # Step 1 — keep a reference to ourselves (the partially-initialised module)
70
+ _sc_our_module = _sys.modules.get('sitecustomize')
71
+
72
+ # Step 2 — remove our directory from sys.path in-place so that any
73
+ # modifications the chained sitecustomize makes to sys.path are preserved.
74
+ _sc_our_idx = next(
75
+ (i for i, p in enumerate(_sys.path)
76
+ if _os.path.normcase(_os.path.abspath(p)) == _sc_this_dir_norm),
77
+ -1,
78
+ )
79
+ if _sc_our_idx >= 0:
80
+ _sys.path.pop(_sc_our_idx)
81
+
82
+ # Steps 3-4 — import the next sitecustomize (if any)
83
+ _sys.modules.pop('sitecustomize', None)
84
+ try:
85
+ import sitecustomize as _sc_chained # noqa: F401
86
+ except Exception:
87
+ pass # no other sitecustomize exists, or it raised — do not break the agent
88
+ finally:
89
+ # Step 5 — restore our directory and re-register ourselves
90
+ if _sc_our_idx >= 0:
91
+ _sys.path.insert(_sc_our_idx, _sc_this_dir)
92
+ if _sc_our_module is not None:
93
+ _sys.modules['sitecustomize'] = _sc_our_module
94
+
95
+ del _sys, _os, _sc_this_dir, _sc_this_dir_norm, _sc_our_module, _sc_our_idx
96
+ # ---------------------------------------------------------------------------
97
+ # End sitecustomize chaining
98
+ # ---------------------------------------------------------------------------
99
+
100
+ import logging
101
+ import os
102
+ import sys
103
+
104
+ sitecustomize_dir = os.path.dirname(__file__)
105
+ pythonagent_dir = os.path.join(sitecustomize_dir, "pythonagent")
106
+
107
+ _bootstrapped_pid = None
108
+
109
+ # ---------------------------------------------------------------------------
110
+ # /proc-based process helpers (no psutil)
111
+ # ---------------------------------------------------------------------------
112
+
113
+ def _read_proc_cmdline(pid="self"):
114
+ """Return the argv list for *pid* by reading /proc/<pid>/cmdline.
115
+ Returns an empty list if the file cannot be read (non-Linux, no permission).
116
+ """
117
+ try:
118
+ with open("/proc/{}/cmdline".format(pid), "rb") as fh:
119
+ data = fh.read()
120
+ if not data:
121
+ return []
122
+ # argv entries are NUL-separated; strip trailing NUL before splitting
123
+ return data.rstrip(b"\x00").split(b"\x00")
124
+ except (IOError, OSError):
125
+ return []
126
+
127
+ def _read_proc_comm(pid="self"):
128
+ """Return the short process name from /proc/<pid>/comm (Linux only)."""
129
+ try:
130
+ with open("/proc/{}/comm".format(pid), "r") as fh:
131
+ return fh.read().strip().lower()
132
+ except (IOError, OSError):
133
+ return ""
134
+
135
+ def _cmdline_args():
136
+ """Best-effort argv list (bytes decoded to str, lower-cased preserved separately)."""
137
+ raw = _read_proc_cmdline()
138
+ if raw:
139
+ try:
140
+ return [a.decode("utf-8", errors="replace") for a in raw]
141
+ except Exception:
142
+ return []
143
+ # Fallback: sys.argv (may differ from the real cmdline on some platforms)
144
+ return list(sys.argv)
145
+
146
+ def _cmdline_text(args):
147
+ return " ".join(args).lower()
148
+
149
+ def _process_name():
150
+ """Short process name, lower-cased."""
151
+ comm = _read_proc_comm()
152
+ if comm:
153
+ return comm
154
+ # Fallback: basename of argv[0]
155
+ if sys.argv:
156
+ return os.path.basename(sys.argv[0]).lower()
157
+ return ""
158
+
159
+ # ---------------------------------------------------------------------------
160
+ # uWSGI helpers
161
+ # ---------------------------------------------------------------------------
162
+
163
+ def _is_uwsgi_master():
164
+ """Return True when running inside a uWSGI master process.
165
+ uwsgi.worker_id() returns 0 in the master and 1..N in workers.
166
+ """
167
+ try:
168
+ import uwsgi
169
+ return uwsgi.worker_id() == 0
170
+ except (ImportError, AttributeError):
171
+ return False
172
+
173
+ # ---------------------------------------------------------------------------
174
+ # Uvicorn helpers
175
+ # ---------------------------------------------------------------------------
176
+
177
+ def _is_uvicorn_command(args, proc_name):
178
+ if proc_name == "uvicorn":
179
+ return True
180
+ lowered = [a.lower() for a in args]
181
+ if "uvicorn" in lowered:
182
+ return True
183
+ for i, arg in enumerate(lowered):
184
+ if arg == "-m" and i + 1 < len(lowered) and lowered[i + 1] == "uvicorn":
185
+ return True
186
+ return False
187
+
188
+ def _has_uvicorn_supervisor_options(args):
189
+ for i, arg in enumerate(args):
190
+ lo = arg.lower()
191
+ if lo == "--reload":
192
+ return True
193
+ if lo in ("--workers",) or lo.startswith("--workers="):
194
+ return True
195
+ return False
196
+
197
+ def _is_uvicorn_supervisor(args, proc_name):
198
+ return _is_uvicorn_command(args, proc_name) and _has_uvicorn_supervisor_options(args)
199
+
200
+ # ---------------------------------------------------------------------------
201
+ # One-shot bootstrap guard
202
+ # ---------------------------------------------------------------------------
203
+
204
+ def _bootstrap_agent_once(agent, cfg):
205
+ global _bootstrapped_pid
206
+ pid = os.getpid()
207
+ if _bootstrapped_pid == pid:
208
+ return
209
+ cfg.map_uwsgi_environ()
210
+ agent.configure()
211
+ agent.bootstrap()
212
+ _bootstrapped_pid = pid
213
+
214
+ # ---------------------------------------------------------------------------
215
+ # sys.path cleanup for old split-bootstrap layout
216
+ # ---------------------------------------------------------------------------
217
+
218
+ if not os.path.isdir(pythonagent_dir):
219
+ try:
220
+ sys.path.remove(sitecustomize_dir)
221
+ except ValueError:
222
+ pass
223
+
224
+ # ---------------------------------------------------------------------------
225
+ # Main bootstrap
226
+ # ---------------------------------------------------------------------------
227
+
228
+ try:
229
+ import pythonagent.agent as agent
230
+ from pythonagent import config as _config
231
+
232
+ args = _cmdline_args()
233
+ proc_name = _process_name()
234
+ cmd_text = _cmdline_text(args)
235
+
236
+ # --- gunicorn master: workers bootstrap via post_fork() in gunicorn cfg ---
237
+ gunicorn = "gunicorn" in cmd_text or proc_name == "gunicorn"
238
+
239
+ # --- uvicorn supervisor: child workers re-run sitecustomize via spawn ---
240
+ uvicorn_supervisor = _is_uvicorn_supervisor(args, proc_name)
241
+
242
+ # --- uWSGI master: workers bootstrap via @postfork below ---
243
+ uwsgi_master = _is_uwsgi_master()
244
+
245
+ if gunicorn or uvicorn_supervisor or uwsgi_master:
246
+ if gunicorn:
247
+ # Auto-bootstrap the agent in each gunicorn worker process after fork.
248
+ # Patch Worker.init_process at the class level in the master so every
249
+ # forked worker inherits the patch and runs bootstrap() before serving.
250
+ # This eliminates the need for a manual post_fork hook in gunicorn.conf.
251
+ try:
252
+ import gunicorn.workers.base as _gwb
253
+
254
+ _cav_orig_init_process = _gwb.Worker.init_process
255
+
256
+ def _cav_worker_init_process(self):
257
+ _bootstrap_agent_once(agent, _config)
258
+ return _cav_orig_init_process(self)
259
+
260
+ _gwb.Worker.init_process = _cav_worker_init_process
261
+ except Exception:
262
+ pass # gunicorn not installed or incompatible version — no-op
263
+
264
+ else:
265
+ _bootstrap_agent_once(agent, _config)
266
+
267
+ # Register uWSGI postfork hook so each worker bootstraps after fork.
268
+ # uwsgidecorators is only importable inside a uWSGI process.
269
+ try:
270
+ from uwsgidecorators import postfork as _uwsgi_postfork
271
+
272
+ @_uwsgi_postfork
273
+ def _cavisson_uwsgi_postfork():
274
+ try:
275
+ _bootstrap_agent_once(agent, _config)
276
+ except Exception:
277
+ pass
278
+ except (ImportError, AttributeError):
279
+ pass
280
+
281
+ except Exception:
282
+ logger = logging.getLogger("pythonagent.agent")
283
+ logger.exception("Exception in agent startup.")
284
+ finally:
285
+ pass
286
+ # appdynamics.agent.load_sitecustomize()
287
+
@@ -0,0 +1,26 @@
1
+ {
2
+ "modules": [
3
+ {
4
+ "moduleName": "run",
5
+ "moduleLevelMethod": [
6
+ {
7
+ "name": "http_callout",
8
+ "signature": "NA"
9
+ },
10
+ {
11
+ "name": "first",
12
+ "signature": "NA"
13
+ },
14
+ {
15
+ "name": "second",
16
+ "signature": "NA"
17
+ },
18
+ {
19
+ "name": "db_callout",
20
+ "signature": "NA"
21
+ }
22
+ ],
23
+ "classes": []
24
+ }
25
+ ]
26
+ }
@@ -0,0 +1,29 @@
1
+ # Entry points
2
+ flask
3
+ django.core.handlers.wsgi
4
+ django.core.handlers.base
5
+ bottle
6
+ tornado.web
7
+
8
+ # HTTP exit calls
9
+ httplib
10
+ http.client
11
+ urllib3
12
+ requests
13
+ tornado.httpclient
14
+ boto.https_connection
15
+
16
+ # Logging
17
+ logging
18
+
19
+ # SQL exit calls
20
+ psycopg2
21
+ pymysql.connections
22
+ mysql.connector.connection
23
+ MySQLdb.connections
24
+
25
+ # MongoDB
26
+ pymongo
27
+
28
+ #REDIS
29
+ redis.connection
@@ -0,0 +1,42 @@
1
+ {
2
+ "modules": [
3
+ {
4
+ "moduleName": "run",
5
+ "moduleLevelMethod": [
6
+ {
7
+ "name": "http_callout",
8
+ "signature": "NA"
9
+ },
10
+ {
11
+ "name": "first",
12
+ "signature": "NA"
13
+ },
14
+ {
15
+ "name": "second",
16
+ "signature": "NA"
17
+ }
18
+ ],
19
+ "classes": []
20
+ },
21
+ {
22
+ "moduleName": "dynamodb",
23
+ "moduleLevelMethod": [
24
+ {
25
+ "name": "dynamodb_callout",
26
+ "signature": "NA"
27
+ }
28
+ ],
29
+ "classes": []
30
+ },
31
+ {
32
+ "moduleName": "s3",
33
+ "moduleLevelMethod": [
34
+ {
35
+ "name": "s3_callout",
36
+ "signature": "NA"
37
+ }
38
+ ],
39
+ "classes": []
40
+ }
41
+ ]
42
+ }
@@ -0,0 +1,29 @@
1
+ # Entry points
2
+ flask
3
+ django.core.handlers.wsgi
4
+ django.core.handlers.base
5
+ bottle
6
+ tornado.web
7
+
8
+ # HTTP exit calls
9
+ httplib
10
+ http.client
11
+ urllib3
12
+ requests
13
+ tornado.httpclient
14
+ boto.https_connection
15
+
16
+ # Logging
17
+ logging
18
+
19
+ # SQL exit calls
20
+ psycopg2
21
+ pymysql.connections
22
+ mysql.connector.connection
23
+ MySQLdb.connections
24
+
25
+ # MongoDB
26
+ pymongo
27
+
28
+ #REDIS
29
+ redis.connection
@@ -0,0 +1,6 @@
1
+ tier=phpyii
2
+ server=10.10.60.225
3
+ Instance=PythonInstance
4
+ ndcHost=10.10.60.226
5
+ ndcPort=443
6
+ NDC_COMM_PROTOCOL=TCP
pythonagent/config.py ADDED
@@ -0,0 +1,279 @@
1
+
2
+ from __future__ import unicode_literals
3
+ import logging
4
+ import os
5
+ import configparser
6
+ cofig= configparser.RawConfigParser()
7
+ import sys
8
+
9
+ from configparser import ConfigParser
10
+
11
+ try:
12
+ ospath = os.environ.get('NDHOME')
13
+ path = ospath + '/python/config/ndsettings.conf'
14
+ except Exception as e:
15
+ print("ND HOME NOT SET")
16
+ raise e
17
+
18
+ #print("path of NDC config : ", path)
19
+
20
+ #configParser.read(path)
21
+ #path_for_insProfile = configParser.get('PYTHON_AGENT', 'path_profile')
22
+
23
+ myvars = {}
24
+ with open(path) as myfile:
25
+ for line in myfile:
26
+ name, var = line.partition("=")[::2]
27
+ myvars[name.strip()] = var
28
+ #print("myvars----------------------->",myvars)
29
+ #TIER = myvars['tier'].strip('\n')
30
+ #SERVER = myvars['server'].strip('\n')
31
+
32
+
33
+
34
+
35
+ int_or_none = lambda v: int(v) if v != '' else None
36
+ on_off = lambda v: v.lower() in ('on', 'true', 'yes', 'y', 't', '1')
37
+ comma_seperated_list = lambda v: v.replace(' ', '').split(',')
38
+
39
+ keys = lambda d: d.keys()
40
+ values = lambda d: d.values()
41
+ # Configuration Options
42
+
43
+ _CONFIG_OPTIONS_BY_SECTION = {
44
+
45
+ 'agent': {
46
+ 'app': ('APP_NAME', None),
47
+ 'tier': ('TIER_NAME', None),
48
+ 'node': ('NODE_NAME', None),
49
+ 'dir': ('DIR', None),
50
+ },
51
+
52
+ 'wsgi': {
53
+ 'script': ('WSGI_SCRIPT_ALIAS', None),
54
+ 'callable': ('WSGI_CALLABLE_OBJECT', None),
55
+ 'module': ('WSGI_MODULE', None),
56
+ },
57
+
58
+ 'asgi': {
59
+ 'module': ('ASGI_MODULE', None),
60
+ 'callable': ('ASGI_CALLABLE_OBJECT', None),
61
+ },
62
+
63
+ 'log': {
64
+ 'dir': ('LOGS_DIR', None),
65
+ 'level': ('LOGGING_LEVEL', None),
66
+ 'debugging': ('DEBUG_LOG', on_off)
67
+ },
68
+
69
+ 'proxy': {
70
+ 'proxymode': ('PROXY_MODE', None),
71
+ 'proxycontype': ('PROXY_CON_TYPE', None),
72
+ 'proxyip': ('PROXY_IP', None),
73
+ 'proxyport': ('PROXY_PORT', None),
74
+ 'sopath': ('SO_PATH', None),
75
+ },
76
+ }
77
+
78
+
79
+ # Defaults ###########
80
+
81
+
82
+ CONFIG_FILE = ''
83
+
84
+ # Agent
85
+ APP_NAME = 'CavAgent'
86
+ #TIER_NAME = 'test_tier'
87
+ #NODE_NAME = 'test'
88
+ DIR = ospath + '/python/hooksettings'
89
+ try:
90
+ TIER = myvars['tier'].strip('\n')
91
+ SERVER = myvars['server'].strip('\n')
92
+ except:
93
+ TIER = "Default"
94
+ SERVER = "Default"
95
+
96
+ # Logging
97
+ LOGS_DIR = ospath+'/python/logs'
98
+ print("LOGS_DIR", LOGS_DIR)
99
+
100
+ if "CAV_APP_AGENT_LOGGINGLEVEL" in os.environ:
101
+ LOGGING_LEVEL = os.environ.get('CAV_APP_AGENT_LOGGINGLEVEL')
102
+ else:
103
+ LOGGING_LEVEL = "WARNING"
104
+
105
+ DEBUG_LOG = False
106
+
107
+
108
+ # WSGI
109
+ WSGI_MODULE = ''
110
+ #WSGI_SCRIPT_ALIAS = '/home/cavisson/shop/my-shop/wsgi_example_app.py'
111
+ #WSGI_SCRIPT_ALIAS = input("Enter the absolute path of WSGI application script. (command to get 'readlink -f <script_path.py>') : ")
112
+ WSGI_SCRIPT_ALIAS = ''
113
+ WSGI_CALLABLE_OBJECT = ''
114
+
115
+ # ASGI
116
+ ASGI_MODULE = ''
117
+ ASGI_CALLABLE_OBJECT = ''
118
+
119
+ # Controller
120
+ CONTROLLER_HOST = '10.20.0.85'
121
+ CONTROLLER_PORT = '7892'
122
+ SSL_ENABLED = False
123
+ ACCOUNT_NAME = ''
124
+ ACCOUNT_ACCESS_KEY = ''
125
+ HTTP_PROXY_HOST = '10.20.0.79'
126
+ HTTP_PROXY_PORT = '1112'
127
+ HTTP_PROXY_USER = ''
128
+ HTTP_PROXY_PASSWORD_FILE = ''
129
+ '''
130
+ # Proxy Runtime
131
+ PROXY_RUN_DIR = ''
132
+
133
+ # Proxy Control Service
134
+ PROXY_CONTROL_PATH = ''
135
+ PROXY_STARTUP_READ_TIMEOUT_MS = 2000
136
+ PROXY_STARTUP_INITIAL_RETRY_DELAY_MS = 5000
137
+ PROXY_STARTUP_MAX_RETRY_DELAY_MS = 300000
138
+
139
+ # Config Service
140
+ PROXY_CONFIG_SOCKET_NAME = '0'
141
+ CONFIG_SERVICE_RELOAD_INTERVAL_MS = 5000
142
+ CONFIG_SERVICE_MAX_RETRIES = 3
143
+
144
+ # Transaction Service
145
+ PROXY_INFO_SOCKET_NAME = '0'
146
+ PROXY_REPORTING_SOCKET_NAME = '1'
147
+ BT_INFO_REQUEST_TIMEOUT_MS = 100
148
+
149
+ # Snapshot Service
150
+ INCLUDE_AGENT_FRAMES = False
151
+ SNAPSHOT_PROFILER_INTERVAL_MS = 10
152
+ EXIT_CALL_DETAILS_LENGTH = 100
153
+ FORCED_SNAPSHOT_INTERVAL = 10
154
+
155
+ # Transaction Monitor Service
156
+ BT_MAX_DURATION_MS = 30 * 60 * 1000
157
+ BT_ABANDON_THRESHOLD_MULTIPLIER = 6
158
+
159
+ # EUM
160
+ EUM_DISABLE_COOKIE = False
161
+ EUM_USER_AGENT_WHITELIST = ['Mozilla', 'Opera', 'WebKit', 'Nokia']
162
+ '''
163
+ #RUNNING_MODE = 0
164
+
165
+ # Approach 7: mapping from uWSGI native env vars to Cavisson CAV_* equivalents.
166
+ # uWSGI exposes its own config as UWSGI_* env vars when started via env vars.
167
+ # UWSGI_WSGI maps to --wsgi-file; UWSGI_MODULE maps to --module; UWSGI_CALLABLE to --callable.
168
+ _UWSGI_ENV_MAP = {
169
+ 'UWSGI_MODULE': 'CAV_WSGI_MODULE',
170
+ 'UWSGI_WSGI': 'CAV_WSGI_SCRIPT_ALIAS',
171
+ 'UWSGI_CALLABLE': 'CAV_WSGI_CALLABLE_OBJECT',
172
+ }
173
+
174
+
175
+ def map_uwsgi_environ():
176
+ """Translate UWSGI_* env vars into CAV_* equivalents, without overwriting
177
+ values the operator has already set explicitly via CAV_* vars."""
178
+ for uwsgi_var, cav_var in _UWSGI_ENV_MAP.items():
179
+ if uwsgi_var in os.environ and cav_var not in os.environ:
180
+ os.environ[cav_var] = os.environ[uwsgi_var]
181
+
182
+
183
+ def validate_config(config):
184
+
185
+ return True
186
+
187
+
188
+ def parse_environ(environ=None, prefix='CAV_'):
189
+
190
+ logger = logging.getLogger('pythonagent.agent')
191
+ environ = environ if environ is not None else os.environ
192
+
193
+ config = {}
194
+ config_file = environ.get('CAV_CONFIG_FILE')
195
+
196
+ if config_file:
197
+ config = parse_config_file(config_file)
198
+
199
+ #environ['SO_PATH'] = config['SO_PATH']
200
+ option_descrs = {}
201
+
202
+ for options in values(_CONFIG_OPTIONS_BY_SECTION):
203
+ for name, handler in values(options):
204
+ option_descrs[prefix + name] = (name, handler)
205
+
206
+ for option in keys(environ):
207
+ if option not in option_descrs:
208
+ continue
209
+
210
+ name, handler = option_descrs[option]
211
+
212
+ try:
213
+ value = environ[option]
214
+ if handler:
215
+ value = handler(value)
216
+ config[name] = value
217
+ except:
218
+ logger.exception('ignoring %s from environment, parsing value caused exception', option)
219
+
220
+ return config
221
+
222
+
223
+ def parse_config_file(filename):
224
+ return None
225
+ logger = logging.getLogger('pythonagent.agent')
226
+
227
+ try:
228
+ config = {}
229
+ parser = ConfigParser()
230
+
231
+ with open(filename) as fp:
232
+ parser.read_file(fp)
233
+
234
+ for section_name in parser.sections():
235
+ try:
236
+ options_map = _CONFIG_OPTIONS_BY_SECTION[section_name]
237
+ except KeyError: # Unknown section
238
+ logger.warning('%s: skipping unrecognized section [%s]', filename, section_name)
239
+ continue
240
+
241
+ for option_name in parser.options(section_name):
242
+ try:
243
+ env_name, handler = options_map[option_name]
244
+
245
+ value = parser.get(section_name, option_name)
246
+ if handler:
247
+ value = handler(value)
248
+
249
+ config[env_name] = value
250
+ except KeyError: # Unknown option
251
+ logger.warning('%s: skipping unrecognized option %r of section [%s]',
252
+ filename, option_name, section_name)
253
+ except: # Other errors
254
+ logger.exception('%s: parsing value for option %r of section [%s] raised exception',
255
+ filename, option_name, section_name)
256
+
257
+ return config
258
+ except:
259
+ logger.exception('Parsing config file failed.')
260
+
261
+
262
+ def merge(config):
263
+
264
+ #mod = globals()
265
+ #mod.update(config)
266
+ update_computed_defaults()
267
+
268
+
269
+ def update_computed_defaults():
270
+ global LOGS_DIR, PROXY_CONTROL_PATH, PROXY_RUN_DIR
271
+
272
+ PROXY_RUN_DIR = os.path.join(DIR, 'run')
273
+
274
+ #if not PROXY_CONTROL_PATH:
275
+ # PROXY_CONTROL_PATH = os.path.join(PROXY_RUN_DIR, 'comm')
276
+
277
+ if not LOGS_DIR:
278
+ LOGS_DIR = os.path.join(DIR, 'logs')
279
+