scout-apm 3.3.0__cp310-cp310-musllinux_1_2_x86_64.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. scout_apm/__init__.py +0 -0
  2. scout_apm/api/__init__.py +197 -0
  3. scout_apm/async_/__init__.py +1 -0
  4. scout_apm/async_/api.py +41 -0
  5. scout_apm/async_/instruments/__init__.py +0 -0
  6. scout_apm/async_/instruments/jinja2.py +13 -0
  7. scout_apm/async_/starlette.py +101 -0
  8. scout_apm/bottle.py +86 -0
  9. scout_apm/celery.py +153 -0
  10. scout_apm/compat.py +104 -0
  11. scout_apm/core/__init__.py +99 -0
  12. scout_apm/core/_objtrace.cpython-310-x86_64-linux-gnu.so +0 -0
  13. scout_apm/core/agent/__init__.py +0 -0
  14. scout_apm/core/agent/commands.py +250 -0
  15. scout_apm/core/agent/manager.py +319 -0
  16. scout_apm/core/agent/socket.py +211 -0
  17. scout_apm/core/backtrace.py +116 -0
  18. scout_apm/core/cli/__init__.py +0 -0
  19. scout_apm/core/cli/core_agent_manager.py +32 -0
  20. scout_apm/core/config.py +404 -0
  21. scout_apm/core/context.py +140 -0
  22. scout_apm/core/error.py +95 -0
  23. scout_apm/core/error_service.py +167 -0
  24. scout_apm/core/metadata.py +66 -0
  25. scout_apm/core/n_plus_one_tracker.py +41 -0
  26. scout_apm/core/objtrace.py +24 -0
  27. scout_apm/core/platform_detection.py +66 -0
  28. scout_apm/core/queue_time.py +99 -0
  29. scout_apm/core/sampler.py +149 -0
  30. scout_apm/core/samplers/__init__.py +0 -0
  31. scout_apm/core/samplers/cpu.py +76 -0
  32. scout_apm/core/samplers/memory.py +23 -0
  33. scout_apm/core/samplers/thread.py +41 -0
  34. scout_apm/core/stacktracer.py +30 -0
  35. scout_apm/core/threading.py +56 -0
  36. scout_apm/core/tracked_request.py +328 -0
  37. scout_apm/core/web_requests.py +167 -0
  38. scout_apm/django/__init__.py +7 -0
  39. scout_apm/django/apps.py +137 -0
  40. scout_apm/django/instruments/__init__.py +0 -0
  41. scout_apm/django/instruments/huey.py +30 -0
  42. scout_apm/django/instruments/sql.py +140 -0
  43. scout_apm/django/instruments/template.py +35 -0
  44. scout_apm/django/middleware.py +211 -0
  45. scout_apm/django/request.py +144 -0
  46. scout_apm/dramatiq.py +42 -0
  47. scout_apm/falcon.py +142 -0
  48. scout_apm/flask/__init__.py +118 -0
  49. scout_apm/flask/sqlalchemy.py +28 -0
  50. scout_apm/huey.py +54 -0
  51. scout_apm/hug.py +40 -0
  52. scout_apm/instruments/__init__.py +21 -0
  53. scout_apm/instruments/elasticsearch.py +263 -0
  54. scout_apm/instruments/jinja2.py +127 -0
  55. scout_apm/instruments/pymongo.py +105 -0
  56. scout_apm/instruments/redis.py +77 -0
  57. scout_apm/instruments/urllib3.py +80 -0
  58. scout_apm/rq.py +85 -0
  59. scout_apm/sqlalchemy.py +38 -0
  60. scout_apm-3.3.0.dist-info/LICENSE +21 -0
  61. scout_apm-3.3.0.dist-info/METADATA +94 -0
  62. scout_apm-3.3.0.dist-info/RECORD +65 -0
  63. scout_apm-3.3.0.dist-info/WHEEL +5 -0
  64. scout_apm-3.3.0.dist-info/entry_points.txt +2 -0
  65. scout_apm-3.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,211 @@
1
+ # coding=utf-8
2
+
3
+ import json
4
+ import logging
5
+ import os
6
+ import socket
7
+ import struct
8
+ import threading
9
+ import time
10
+
11
+ from scout_apm.compat import queue
12
+ from scout_apm.core.agent.commands import Register
13
+ from scout_apm.core.agent.manager import get_socket_path
14
+ from scout_apm.core.config import scout_config
15
+ from scout_apm.core.threading import SingletonThread
16
+
17
+ # Time unit - monkey-patched in tests to make them run faster
18
+ SECOND = 1
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class CoreAgentSocketThread(SingletonThread):
24
+ _instance_lock = threading.Lock()
25
+ _stop_event = threading.Event()
26
+ _command_queue = queue.Queue(maxsize=500)
27
+
28
+ @classmethod
29
+ def _on_stop(cls):
30
+ super(CoreAgentSocketThread, cls)._on_stop()
31
+ # Unblock _command_queue.get()
32
+ try:
33
+ cls._command_queue.put(None, False)
34
+ except queue.Full:
35
+ pass
36
+
37
+ @classmethod
38
+ def send(cls, command):
39
+ try:
40
+ cls._command_queue.put(command, False)
41
+ except queue.Full as exc:
42
+ # TODO mark the command as not queued?
43
+ logger.debug("CoreAgentSocketThread error on send: %r", exc, exc_info=exc)
44
+
45
+ cls.ensure_started()
46
+
47
+ @classmethod
48
+ def wait_until_drained(cls, timeout_seconds=2.0, callback=None):
49
+ interval_seconds = min(timeout_seconds, 0.05)
50
+ start = time.time()
51
+ while True:
52
+ queue_size = cls._command_queue.qsize()
53
+ queue_empty = queue_size == 0
54
+ elapsed = time.time() - start
55
+ if queue_empty or elapsed >= timeout_seconds:
56
+ break
57
+
58
+ if callback is not None:
59
+ callback(queue_size)
60
+ callback = None
61
+
62
+ cls.ensure_started()
63
+
64
+ time.sleep(interval_seconds)
65
+ return queue_empty
66
+
67
+ def run(self):
68
+ self.socket_path = get_socket_path()
69
+ self.socket = self.make_socket()
70
+
71
+ try:
72
+ self._connect()
73
+ self._register()
74
+ while True:
75
+ try:
76
+ body = self._command_queue.get(block=True, timeout=1 * SECOND)
77
+ except queue.Empty:
78
+ body = None
79
+
80
+ if body is not None:
81
+ result = self._send(body)
82
+ if result:
83
+ self._command_queue.task_done()
84
+ else:
85
+ # Something was wrong with the socket.
86
+ self._disconnect()
87
+ self._connect()
88
+ self._register()
89
+
90
+ # Check for stop event after each read. This allows opening,
91
+ # sending, and then immediately stopping. We do this for
92
+ # the metadata event at application start time.
93
+ if self._stop_event.is_set():
94
+ logger.debug("CoreAgentSocketThread stopping.")
95
+ break
96
+ except Exception as exc:
97
+ logger.debug("CoreAgentSocketThread exception: %r", exc, exc_info=exc)
98
+ finally:
99
+ self.socket.close()
100
+ logger.debug("CoreAgentSocketThread stopped.")
101
+
102
+ def _send(self, command):
103
+ msg = command.message()
104
+
105
+ try:
106
+ data = json.dumps(msg)
107
+ except (ValueError, TypeError) as exc:
108
+ logger.debug(
109
+ "Exception when serializing command message: %r", exc, exc_info=exc
110
+ )
111
+ return False
112
+
113
+ full_data = struct.pack(">I", len(data)) + data.encode("utf-8")
114
+ try:
115
+ self.socket.sendall(full_data)
116
+ except OSError as exc:
117
+ logger.debug(
118
+ (
119
+ "CoreAgentSocketThread exception on _send:"
120
+ + " %r on PID: %s on thread: %s"
121
+ ),
122
+ exc,
123
+ os.getpid(),
124
+ threading.current_thread(),
125
+ exc_info=exc,
126
+ )
127
+ return False
128
+
129
+ # TODO do something with the response sent back in reply to command
130
+ self._read_response()
131
+
132
+ return True
133
+
134
+ def _read_response(self):
135
+ try:
136
+ raw_size = self.socket.recv(4)
137
+ if len(raw_size) != 4:
138
+ # Ignore invalid responses
139
+ return None
140
+ size = struct.unpack(">I", raw_size)[0]
141
+ message = bytearray(0)
142
+
143
+ while len(message) < size:
144
+ recv = self.socket.recv(size)
145
+ message += recv
146
+
147
+ return message
148
+ except OSError as exc:
149
+ logger.debug(
150
+ "CoreAgentSocketThread error on read response: %r", exc, exc_info=exc
151
+ )
152
+ return None
153
+
154
+ def _register(self):
155
+ self._send(
156
+ Register(
157
+ app=scout_config.value("name"),
158
+ key=scout_config.value("key"),
159
+ hostname=scout_config.value("hostname"),
160
+ )
161
+ )
162
+
163
+ def _connect(self, connect_attempts=5, retry_wait_secs=1):
164
+ for attempt in range(1, connect_attempts + 1):
165
+ logger.debug(
166
+ (
167
+ "CoreAgentSocketThread attempt %d, connecting to %s, "
168
+ + "PID: %s, Thread: %s"
169
+ ),
170
+ attempt,
171
+ self.socket_path,
172
+ os.getpid(),
173
+ threading.current_thread(),
174
+ )
175
+ try:
176
+ self.socket.connect(self.get_socket_address())
177
+ self.socket.settimeout(3 * SECOND)
178
+ logger.debug("CoreAgentSocketThread connected")
179
+ return
180
+ except socket.error as exc:
181
+ logger.debug(
182
+ "CoreAgentSocketThread connection error: %r", exc, exc_info=exc
183
+ )
184
+ # Return without waiting when reaching the maximum number of attempts.
185
+ if attempt == connect_attempts:
186
+ raise
187
+ time.sleep(retry_wait_secs * SECOND)
188
+
189
+ def _disconnect(self):
190
+ logger.debug("CoreAgentSocketThread disconnecting from %s", self.socket_path)
191
+ try:
192
+ self.socket.close()
193
+ except socket.error as exc:
194
+ logger.debug(
195
+ "CoreAgentSocketThread exception on disconnect: %r", exc, exc_info=exc
196
+ )
197
+ finally:
198
+ self.socket = self.make_socket()
199
+
200
+ def make_socket(self):
201
+ if self.socket_path.is_tcp:
202
+ family = socket.AF_INET
203
+ else:
204
+ family = socket.AF_UNIX
205
+ return socket.socket(family, socket.SOCK_STREAM)
206
+
207
+ def get_socket_address(self):
208
+ if self.socket_path.is_tcp:
209
+ host, _, port = self.socket_path.tcp_address.partition(":")
210
+ return host, int(port)
211
+ return self.socket_path
@@ -0,0 +1,116 @@
1
+ # coding=utf-8
2
+
3
+ import itertools
4
+ import os
5
+ import sys
6
+ import sysconfig
7
+ import traceback
8
+ import warnings
9
+ from logging import getLogger
10
+
11
+ logger = getLogger(__name__)
12
+
13
+ # Maximum non-Scout frames to target retrieving
14
+ LIMIT = 50
15
+ # How many upper frames from inside Scout to ignore
16
+ IGNORED = 1
17
+
18
+
19
+ def filter_frames(frames):
20
+ """Filter the stack trace frames down to non-library code."""
21
+ paths = sysconfig.get_paths()
22
+ library_paths = {paths["purelib"], paths["platlib"]}
23
+ for frame in frames:
24
+ if not any(frame["file"].startswith(exclusion) for exclusion in library_paths):
25
+ yield frame
26
+
27
+
28
+ def module_filepath(module, filepath):
29
+ """Get the filepath relative to the base module."""
30
+ root_module_name = module.split(".", 1)[0]
31
+ if root_module_name == module:
32
+ return os.path.basename(filepath)
33
+
34
+ module_dir = None
35
+ root_module = sys.modules[root_module_name]
36
+ try:
37
+ if root_module.__file__:
38
+ module_dir = root_module.__file__.rsplit(os.sep, 2)[0]
39
+ elif root_module.__path__ and isinstance(root_module.__path__, (list, tuple)):
40
+ # Default to using the first path specified for the module.
41
+ module_dir = root_module.__path__[0].rsplit(os.sep, 1)[0]
42
+ if len(root_module.__path__) > 1:
43
+ logger.debug(
44
+ "{} has {} paths. Use the first and ignore the rest.".format(
45
+ root_module, len(root_module.__path__)
46
+ )
47
+ )
48
+ except Exception as exc:
49
+ logger.debug(
50
+ "Error processing module {} and filepath {}".format(root_module, filepath),
51
+ exc_info=exc,
52
+ )
53
+
54
+ return filepath.split(module_dir, 1)[-1].lstrip(os.sep) if module_dir else filepath
55
+
56
+
57
+ def filepaths(frame):
58
+ """Get the filepath for frame."""
59
+ module = frame.f_globals.get("__name__", None)
60
+ filepath = frame.f_code.co_filename
61
+
62
+ if filepath.endswith(".pyc"):
63
+ filepath = filepath[:-1]
64
+
65
+ return filepath, (module_filepath(module, filepath) if module else filepath)
66
+
67
+
68
+ def stacktrace_walker(tb):
69
+ """Iterate over each frame of the stack downards for exceptions."""
70
+ for frame, lineno in traceback.walk_tb(tb):
71
+ name = frame.f_code.co_name
72
+ full_path, relative_path = filepaths(frame)
73
+ yield {
74
+ "file": relative_path,
75
+ "full_path": full_path,
76
+ "line": lineno,
77
+ "function": name,
78
+ }
79
+
80
+
81
+ def backtrace_walker():
82
+ """Iterate over each frame of the stack upwards.
83
+
84
+ Taken from python3/traceback.ExtractSummary.extract to support
85
+ iterating over the entire stack, but without creating a large
86
+ data structure.
87
+ """
88
+ start_frame = sys._getframe().f_back
89
+ for frame, lineno in traceback.walk_stack(start_frame):
90
+ name = frame.f_code.co_name
91
+ full_path, relative_path = filepaths(frame)
92
+ yield {
93
+ "file": relative_path,
94
+ "full_path": full_path,
95
+ "line": lineno,
96
+ "function": name,
97
+ }
98
+
99
+
100
+ def capture_backtrace():
101
+ walker = filter_frames(backtrace_walker())
102
+ return list(itertools.islice(walker, LIMIT))
103
+
104
+
105
+ def capture_stacktrace(tb):
106
+ walker = stacktrace_walker(tb)
107
+ return list(reversed(list(itertools.islice(walker, LIMIT))))
108
+
109
+
110
+ def capture():
111
+ warnings.warn(
112
+ "capture is deprecated, instead use capture_backtrace instead.",
113
+ DeprecationWarning,
114
+ stacklevel=2,
115
+ )
116
+ return capture_backtrace()
File without changes
@@ -0,0 +1,32 @@
1
+ # coding=utf-8
2
+
3
+ import argparse
4
+ import logging
5
+
6
+ from scout_apm.core import CoreAgentManager
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def main(argv=None):
12
+ parser = argparse.ArgumentParser()
13
+ parser.add_argument(
14
+ "-v", "--verbose", help="increase output verbosity", action="count"
15
+ )
16
+
17
+ subparsers = parser.add_subparsers(
18
+ title="subcommands", description="valid subcommands", dest="subparser"
19
+ )
20
+ subparsers.add_parser("download")
21
+ subparsers.add_parser("launch")
22
+
23
+ args = parser.parse_args(argv)
24
+
25
+ if args.verbose is not None:
26
+ if args.verbose >= 2:
27
+ logging.basicConfig(level=logging.DEBUG)
28
+ else:
29
+ logging.basicConfig(level=logging.INFO)
30
+
31
+ core_agent_manager = CoreAgentManager()
32
+ getattr(core_agent_manager, args.subparser)()