scout-apm 3.3.0__cp38-cp38-musllinux_1_2_i686.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 (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-38-i386-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 +82 -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)()