analytics-platform 0.3.2__tar.gz → 0.3.3__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.
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/PKG-INFO +5 -2
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/README.md +4 -1
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/__init__.py +1 -1
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/core.py +43 -2
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform.egg-info/PKG-INFO +5 -2
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform.egg-info/SOURCES.txt +1 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/pyproject.toml +1 -1
- analytics_platform-0.3.3/tests/test_checkin.py +29 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/LICENSE +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/analytics.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/audit.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/crawlers.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/django.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/errors.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/firewall.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/flags.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/forms.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/inventory.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/maintenance.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/performance.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/posture.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/proxy.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/request_rules.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform/resources.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform.egg-info/dependency_links.txt +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform.egg-info/top_level.txt +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/setup.cfg +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/tests/test_crawlers.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/tests/test_errors.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/tests/test_firewall.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/tests/test_proxy.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/tests/test_request_rules.py +0 -0
- {analytics_platform-0.3.2 → analytics_platform-0.3.3}/tests/test_resources.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: analytics-platform
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Firewall, error monitoring and server analytics for your Python server, run from your analytics dashboard.
|
|
5
5
|
Author: steviec87
|
|
6
6
|
License-Expression: MIT
|
|
@@ -187,7 +187,10 @@ Several services on one site: `Platform(..., server_name="worker")`.
|
|
|
187
187
|
**Several apps on one site** (microservices, a worker, a Node service next to
|
|
188
188
|
this one): install the package in each, with the same `site_id`, `secret` and
|
|
189
189
|
`endpoint`, and a `server_name` of its own. Errors, Server speed, Crawlers and
|
|
190
|
-
Security → Updates then tell the apps apart
|
|
190
|
+
Security → Updates then tell the apps apart, and Health → All systems lists
|
|
191
|
+
each with a light: the package checks in about once a minute, so an app that
|
|
192
|
+
stops reads "Stopped" within ten minutes (`sdk/protocols/checkin.md`).
|
|
193
|
+
Without one, each app is named
|
|
191
194
|
after its machine: two apps on one machine are reported as one, and only the
|
|
192
195
|
last package list sent is kept. A service only your other apps call takes
|
|
193
196
|
`firewall=False, proxy=False, maintenance=False` (its requests all come from
|
|
@@ -161,7 +161,10 @@ Several services on one site: `Platform(..., server_name="worker")`.
|
|
|
161
161
|
**Several apps on one site** (microservices, a worker, a Node service next to
|
|
162
162
|
this one): install the package in each, with the same `site_id`, `secret` and
|
|
163
163
|
`endpoint`, and a `server_name` of its own. Errors, Server speed, Crawlers and
|
|
164
|
-
Security → Updates then tell the apps apart
|
|
164
|
+
Security → Updates then tell the apps apart, and Health → All systems lists
|
|
165
|
+
each with a light: the package checks in about once a minute, so an app that
|
|
166
|
+
stops reads "Stopped" within ten minutes (`sdk/protocols/checkin.md`).
|
|
167
|
+
Without one, each app is named
|
|
165
168
|
after its machine: two apps on one machine are reported as one, and only the
|
|
166
169
|
last package list sent is kept. A service only your other apps call takes
|
|
167
170
|
`firewall=False, proxy=False, maintenance=False` (its requests all come from
|
|
@@ -81,7 +81,7 @@ class Platform:
|
|
|
81
81
|
performance=True, crawlers=True, forms=True, headers=True, audit=True, flags=True, maintenance=True,
|
|
82
82
|
inventory=True,
|
|
83
83
|
posture=True, resources=True, proxy=True, server_name=None):
|
|
84
|
-
self.core = Core(site_id, secret, endpoint, debug)
|
|
84
|
+
self.core = Core(site_id, secret, endpoint, debug, server_name=server_name)
|
|
85
85
|
if not self.core.valid:
|
|
86
86
|
print("[analytics-platform] site_id, secret and endpoint are required; nothing is sent and every request passes")
|
|
87
87
|
settings = {"firewall": firewall, "errors": errors, "analytics": analytics, "performance": performance,
|
|
@@ -10,13 +10,16 @@ start again in a forked worker (gunicorn, uWSGI), where threads don't survive.
|
|
|
10
10
|
import atexit
|
|
11
11
|
import json
|
|
12
12
|
import os
|
|
13
|
+
import platform
|
|
13
14
|
import re
|
|
15
|
+
import socket
|
|
14
16
|
import threading
|
|
17
|
+
import time
|
|
15
18
|
import urllib.error
|
|
16
19
|
import urllib.parse
|
|
17
20
|
import urllib.request
|
|
18
21
|
|
|
19
|
-
VERSION = "0.3.
|
|
22
|
+
VERSION = "0.3.3"
|
|
20
23
|
TIMEOUT = 5
|
|
21
24
|
MIN_SYNC = 30
|
|
22
25
|
RETRY = 60
|
|
@@ -100,9 +103,30 @@ class Periodic:
|
|
|
100
103
|
thread.join(wait)
|
|
101
104
|
|
|
102
105
|
|
|
106
|
+
def _rss_mb():
|
|
107
|
+
"""This process's resident memory in MB, where the platform says (Linux
|
|
108
|
+
/proc; elsewhere the peak, from resource); None when neither can."""
|
|
109
|
+
try:
|
|
110
|
+
with open("/proc/self/statm") as statm:
|
|
111
|
+
return round(int(statm.read().split()[1]) * os.sysconf("SC_PAGE_SIZE") / 1048576)
|
|
112
|
+
except Exception:
|
|
113
|
+
pass
|
|
114
|
+
try:
|
|
115
|
+
import resource
|
|
116
|
+
import sys
|
|
117
|
+
peak = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
|
|
118
|
+
return round(peak / 1048576 if sys.platform == "darwin" else peak / 1024)
|
|
119
|
+
except Exception:
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
|
|
103
123
|
class Core:
|
|
104
|
-
def __init__(self, site_id, secret, endpoint, debug=False):
|
|
124
|
+
def __init__(self, site_id, secret, endpoint, debug=False, server_name=None):
|
|
105
125
|
self.site_id = str(site_id or "")
|
|
126
|
+
# The name this service checks in under (X-Platform-App, on the switches
|
|
127
|
+
# request): server_name, as every module sends it, else the machine's.
|
|
128
|
+
self.server_name = str(server_name or socket.gethostname())[:100]
|
|
129
|
+
self._started = time.time()
|
|
106
130
|
self.secret = str(secret or "")
|
|
107
131
|
self.endpoint = str(endpoint or "").rstrip("/")
|
|
108
132
|
self.valid = bool(re.fullmatch(r"[a-fA-F0-9]{24}", self.site_id) and self.secret and self.endpoint)
|
|
@@ -154,8 +178,25 @@ class Core:
|
|
|
154
178
|
"""False only when the dashboard turned the module off."""
|
|
155
179
|
return self._switches.get(name) is not False
|
|
156
180
|
|
|
181
|
+
def _check_in(self):
|
|
182
|
+
"""This service's check-in, form-encoded so any name is plain ASCII:
|
|
183
|
+
Health -> All systems shows it running and when it was last heard from.
|
|
184
|
+
None when it can't be built: the switches still sync, without it."""
|
|
185
|
+
try:
|
|
186
|
+
fields = {"server": self.server_name, "kind": "process", "pid": os.getpid(),
|
|
187
|
+
"up_s": int(time.time() - self._started), "runtime": "Python " + platform.python_version()}
|
|
188
|
+
rss = _rss_mb()
|
|
189
|
+
if rss is not None:
|
|
190
|
+
fields["rss_mb"] = rss
|
|
191
|
+
return urllib.parse.urlencode(fields) if self.server_name else None
|
|
192
|
+
except Exception:
|
|
193
|
+
return None
|
|
194
|
+
|
|
157
195
|
def _sync_switches(self):
|
|
158
196
|
headers = {"X-Platform-Modules": ",".join(self._loaded)}
|
|
197
|
+
app = self._check_in()
|
|
198
|
+
if app:
|
|
199
|
+
headers["X-Platform-App"] = app
|
|
159
200
|
if self._etag:
|
|
160
201
|
headers["If-None-Match"] = self._etag
|
|
161
202
|
res = self.send("core", "/events/platform/config?site_id=" + self.site_id, method="GET", auth="bearer", headers=headers)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: analytics-platform
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Firewall, error monitoring and server analytics for your Python server, run from your analytics dashboard.
|
|
5
5
|
Author: steviec87
|
|
6
6
|
License-Expression: MIT
|
|
@@ -187,7 +187,10 @@ Several services on one site: `Platform(..., server_name="worker")`.
|
|
|
187
187
|
**Several apps on one site** (microservices, a worker, a Node service next to
|
|
188
188
|
this one): install the package in each, with the same `site_id`, `secret` and
|
|
189
189
|
`endpoint`, and a `server_name` of its own. Errors, Server speed, Crawlers and
|
|
190
|
-
Security → Updates then tell the apps apart
|
|
190
|
+
Security → Updates then tell the apps apart, and Health → All systems lists
|
|
191
|
+
each with a light: the package checks in about once a minute, so an app that
|
|
192
|
+
stops reads "Stopped" within ten minutes (`sdk/protocols/checkin.md`).
|
|
193
|
+
Without one, each app is named
|
|
191
194
|
after its machine: two apps on one machine are reported as one, and only the
|
|
192
195
|
last package list sent is kept. A service only your other apps call takes
|
|
193
196
|
`firewall=False, proxy=False, maintenance=False` (its requests all come from
|
{analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform.egg-info/SOURCES.txt
RENAMED
|
@@ -22,6 +22,7 @@ analytics_platform.egg-info/PKG-INFO
|
|
|
22
22
|
analytics_platform.egg-info/SOURCES.txt
|
|
23
23
|
analytics_platform.egg-info/dependency_links.txt
|
|
24
24
|
analytics_platform.egg-info/top_level.txt
|
|
25
|
+
tests/test_checkin.py
|
|
25
26
|
tests/test_crawlers.py
|
|
26
27
|
tests/test_errors.py
|
|
27
28
|
tests/test_firewall.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "analytics-platform"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.3"
|
|
8
8
|
description = "Firewall, error monitoring and server analytics for your Python server, run from your analytics dashboard."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""The check-in on the switches request (X-Platform-App): what names this
|
|
2
|
+
service on Health -> All systems."""
|
|
3
|
+
import os
|
|
4
|
+
import unittest
|
|
5
|
+
import urllib.parse
|
|
6
|
+
from unittest import mock
|
|
7
|
+
|
|
8
|
+
from analytics_platform.core import Core
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CheckInTest(unittest.TestCase):
|
|
12
|
+
def test_switches_request_says_which_service_asks_and_that_it_runs(self):
|
|
13
|
+
core = Core("a" * 24, "s", "http://collector", server_name="api wörker")
|
|
14
|
+
with mock.patch.object(core, "send", return_value=None) as send:
|
|
15
|
+
core._sync_switches()
|
|
16
|
+
header = send.call_args.kwargs["headers"]["X-Platform-App"]
|
|
17
|
+
fields = dict(urllib.parse.parse_qsl(header))
|
|
18
|
+
self.assertEqual(fields["server"], "api wörker")
|
|
19
|
+
self.assertEqual(fields["kind"], "process")
|
|
20
|
+
self.assertEqual(fields["pid"], str(os.getpid()))
|
|
21
|
+
self.assertTrue(fields["runtime"].startswith("Python "))
|
|
22
|
+
self.assertTrue(header.isascii())
|
|
23
|
+
|
|
24
|
+
def test_default_name_is_the_machines(self):
|
|
25
|
+
self.assertTrue(Core("a" * 24, "s", "http://collector").server_name)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{analytics_platform-0.3.2 → analytics_platform-0.3.3}/analytics_platform.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|