apitally 0.14.1__py3-none-any.whl → 0.14.3__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.
@@ -35,6 +35,12 @@ EXCLUDE_PATH_PATTERNS = [
35
35
  r"/ready$",
36
36
  r"/live$",
37
37
  ]
38
+ EXCLUDE_USER_AGENT_PATTERNS = [
39
+ r"health[_- ]?check",
40
+ r"microsoft-azure-application-lb",
41
+ r"googlehc",
42
+ r"kube-probe",
43
+ ]
38
44
  MASK_QUERY_PARAM_PATTERNS = [
39
45
  r"auth",
40
46
  r"api-?key",
@@ -162,7 +168,12 @@ class RequestLogger:
162
168
  if not self.enabled or self.suspend_until is not None:
163
169
  return
164
170
  parsed_url = urlparse(request["url"])
165
- if self._should_exclude_path(request["path"] or parsed_url.path) or self._should_exclude(request, response):
171
+ user_agent = self._get_user_agent(request["headers"])
172
+ if (
173
+ self._should_exclude_path(request["path"] or parsed_url.path)
174
+ or self._should_exclude_user_agent(user_agent)
175
+ or self._should_exclude(request, response)
176
+ ):
166
177
  return
167
178
 
168
179
  query = self._mask_query_params(parsed_url.query) if self.config.log_query_params else ""
@@ -271,6 +282,10 @@ class RequestLogger:
271
282
  patterns = self.config.exclude_paths + EXCLUDE_PATH_PATTERNS
272
283
  return self._match_patterns(url_path, patterns)
273
284
 
285
+ @lru_cache(maxsize=1000)
286
+ def _should_exclude_user_agent(self, user_agent: Optional[str]) -> bool:
287
+ return self._match_patterns(user_agent, EXCLUDE_USER_AGENT_PATTERNS) if user_agent is not None else False
288
+
274
289
  def _mask_query_params(self, query: str) -> str:
275
290
  query_params = parse_qsl(query)
276
291
  masked_query_params = [(k, v if not self._should_mask_query_param(k) else MASKED) for k, v in query_params]
@@ -302,6 +317,10 @@ class RequestLogger:
302
317
  content_type = next((v for k, v in headers if k.lower() == "content-type"), None)
303
318
  return content_type is not None and any(content_type.startswith(t) for t in ALLOWED_CONTENT_TYPES)
304
319
 
320
+ @staticmethod
321
+ def _get_user_agent(headers: List[Tuple[str, str]]) -> Optional[str]:
322
+ return next((v for k, v in headers if k.lower() == "user-agent"), None)
323
+
305
324
 
306
325
  def _check_writable_fs() -> bool:
307
326
  try:
apitally/starlette.py CHANGED
@@ -222,12 +222,18 @@ class ApitallyMiddleware:
222
222
  },
223
223
  )
224
224
 
225
- @staticmethod
226
- def get_path(request: Request) -> Optional[str]:
227
- for route in request.app.routes:
228
- match, _ = route.matches(request.scope)
229
- if match == Match.FULL:
230
- return route.path
225
+ def get_path(self, request: Request, routes: Optional[list[BaseRoute]] = None) -> Optional[str]:
226
+ if routes is None:
227
+ routes = request.app.routes
228
+ for route in routes:
229
+ if hasattr(route, "routes"):
230
+ path = self.get_path(request, routes=route.routes)
231
+ if path is not None:
232
+ return path
233
+ elif hasattr(route, "path"):
234
+ match, _ = route.matches(request.scope)
235
+ if match == Match.FULL:
236
+ return request.scope.get("root_path", "") + route.path
231
237
  return None
232
238
 
233
239
  def get_consumer(self, request: Request) -> Optional[ApitallyConsumer]:
@@ -1,12 +1,13 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: apitally
3
- Version: 0.14.1
3
+ Version: 0.14.3
4
4
  Summary: Simple API monitoring & analytics for REST APIs built with FastAPI, Flask, Django, Starlette and Litestar.
5
5
  Project-URL: Homepage, https://apitally.io
6
6
  Project-URL: Documentation, https://docs.apitally.io
7
7
  Project-URL: Repository, https://github.com/apitally/apitally-py
8
8
  Author-email: Apitally <hello@apitally.io>
9
9
  License: MIT License
10
+ License-File: LICENSE
10
11
  Classifier: Development Status :: 5 - Production/Stable
11
12
  Classifier: Environment :: Web Environment
12
13
  Classifier: Framework :: Django
@@ -68,9 +69,9 @@ Description-Content-Type: text/markdown
68
69
  </picture>
69
70
  </p>
70
71
 
71
- <p align="center"><b>API monitoring made easy.</b></p>
72
+ <p align="center"><b>Analytics, logging & monitoring for REST APIs.</b></p>
72
73
 
73
- <p align="center"><i>Apitally is a simple API monitoring & analytics tool with a focus on data privacy.<br>It is super easy to use for API projects in Python or Node.js and never collects sensitive data.</i></p>
74
+ <p align="center"><i>Apitally helps you understand how your APIs are being used and alerts you when things go wrong.<br>It's super easy to use and designed to protect your data privacy.</i></p>
74
75
 
75
76
  <p align="center">🔗 <b><a href="https://apitally.io" target="_blank">apitally.io</a></b></p>
76
77
 
@@ -100,7 +101,7 @@ the 📚 [documentation](https://docs.apitally.io).
100
101
  ## Key features
101
102
 
102
103
  - Middleware for different frameworks to capture metadata about API endpoints,
103
- requests and responses (no sensitive data is captured)
104
+ requests and responses
104
105
  - Non-blocking clients that aggregate and send captured data to Apitally in
105
106
  regular intervals
106
107
 
@@ -7,18 +7,18 @@ apitally/fastapi.py,sha256=IfKfgsmIY8_AtnuMTW2sW4qnkya61CAE2vBoIpcc9tk,169
7
7
  apitally/flask.py,sha256=Th5LsMsTKkWERPrKfSWPhzrp99tg0pDtKXgtlVLx3eo,9279
8
8
  apitally/litestar.py,sha256=hAH2-OVVXBDVY8LopfIGv30yYwi-71tSEsKd6648CYc,13098
9
9
  apitally/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- apitally/starlette.py,sha256=8WAWQPePJAYy-B5TrxawxAeUqBiSXD15Ay17i2B22jc,12500
10
+ apitally/starlette.py,sha256=VaT4-QVSYC0YX1U5kVI-dGROEd64IbjYU5lx5N16yf8,12852
11
11
  apitally/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  apitally/client/client_asyncio.py,sha256=lRvVKFU6eiapJnB9WrRq6Nuadx-n8NCKwQg-da1vPSM,7064
13
13
  apitally/client/client_base.py,sha256=w5AXAbg3hw5Qds5rovCZFtePB9bHNcJsr9l7kDgbroc,3733
14
14
  apitally/client/client_threading.py,sha256=MbytG8EopF84nmr5ShAZaq-VviSXYnBfBl7cRFRe1Kg,7479
15
15
  apitally/client/consumers.py,sha256=w_AFQhVgdtJVt7pVySBvSZwQg-2JVqmD2JQtVBoMkus,2626
16
16
  apitally/client/logging.py,sha256=QMsKIIAFo92PNBUleeTgsrsQa7SEal-oJa1oOHUr1wI,507
17
- apitally/client/request_logging.py,sha256=4N_JdkkrNVAFhlSAie3Kio3F1ARObYvFOH6D1mFppZg,12361
17
+ apitally/client/request_logging.py,sha256=5i7Gv4yP7iq4tBgw-ppkhlZd_OwMc719ZvWEm16TCvg,13047
18
18
  apitally/client/requests.py,sha256=RdJyvIqQGVHvS-wjpAPUwcO7byOJ6jO8dYqNTU2Furg,3685
19
19
  apitally/client/server_errors.py,sha256=axEhOxqV5SWjk0QCZTLVv2UMIaTfqPc81Typ4DXt66A,4646
20
20
  apitally/client/validation_errors.py,sha256=6G8WYWFgJs9VH9swvkPXJGuOJgymj5ooWA9OwjUTbuM,1964
21
- apitally-0.14.1.dist-info/METADATA,sha256=sWueWK0vQcT_Kss4Gf3aIjx0-WhsisA1WfSPefFivlQ,7577
22
- apitally-0.14.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
23
- apitally-0.14.1.dist-info/licenses/LICENSE,sha256=vbLzC-4TddtXX-_AFEBKMYWRlxC_MN0g66QhPxo8PgY,1065
24
- apitally-0.14.1.dist-info/RECORD,,
21
+ apitally-0.14.3.dist-info/METADATA,sha256=ASnl8PEaJIOpTrMIIzs13F9T5jrnJ0I2gHTOnXEvPiU,7570
22
+ apitally-0.14.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
+ apitally-0.14.3.dist-info/licenses/LICENSE,sha256=vbLzC-4TddtXX-_AFEBKMYWRlxC_MN0g66QhPxo8PgY,1065
24
+ apitally-0.14.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.3
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any