structlog-config 0.2.0__py3-none-any.whl → 0.4.0__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.
@@ -3,13 +3,16 @@ from urllib.parse import quote
3
3
 
4
4
  import structlog
5
5
  from fastapi import FastAPI
6
+ from python_ipware import IpWare
6
7
  from starlette.middleware.base import RequestResponseEndpoint
7
8
  from starlette.requests import Request
8
9
  from starlette.responses import Response
9
10
  from starlette.routing import Match, Mount
10
11
  from starlette.types import Scope
12
+ from starlette.websockets import WebSocket
11
13
 
12
14
  log = structlog.get_logger("access_log")
15
+ ipw = IpWare()
13
16
 
14
17
 
15
18
  def get_route_name(app: FastAPI, scope: Scope, prefix: str = "") -> str:
@@ -63,6 +66,42 @@ def get_client_addr(scope: Scope) -> str:
63
66
  return f"{ip}:{port}"
64
67
 
65
68
 
69
+ def client_ip_from_request(request: Request | WebSocket) -> str | None:
70
+ """
71
+ Get the client IP address from the request.
72
+
73
+ Headers are not case-sensitive.
74
+
75
+ Uses ipware library to properly extract client IP from various proxy headers.
76
+ Fallback to direct client connection if no proxy headers found.
77
+ """
78
+ headers = request.headers
79
+
80
+ # TODO this seems really inefficient, we should just rewrite the ipware into this repo :/
81
+ # Convert Starlette headers to format expected by ipware (HTTP_ prefixed)
82
+ # ipware expects headers in WSGI/Django-style meta format where HTTP headers
83
+ # are prefixed with "HTTP_" and dashes become underscores.
84
+ # See: https://github.com/un33k/python-ipware/blob/main/python_ipware/python_ipware.py#L33-L40
85
+ meta_dict = {}
86
+ for name, value in headers.items():
87
+ # Convert header name to HTTP_ prefixed format
88
+ meta_key = f"HTTP_{name.upper().replace('-', '_')}"
89
+ meta_dict[meta_key] = value
90
+
91
+ # Use ipware to extract IP from headers
92
+ ip, trusted_route = ipw.get_client_ip(meta=meta_dict)
93
+ if ip:
94
+ log.debug(
95
+ "extracted client IP from headers", ip=ip, trusted_route=trusted_route
96
+ )
97
+ return str(ip)
98
+
99
+ # Fallback to direct client connection
100
+ host = request.client.host if request.client else None
101
+
102
+ return host
103
+
104
+
66
105
  # TODO we should look at the static asset logic and pull the prefix path from tha
67
106
  def is_static_assets_request(scope: Scope) -> bool:
68
107
  """Check if the request is for static assets. Pretty naive check.
@@ -78,13 +117,27 @@ def is_static_assets_request(scope: Scope) -> bool:
78
117
  or scope["path"].endswith(".js")
79
118
  # .map files are attempted when devtools are enabled
80
119
  or scope["path"].endswith(".js.map")
120
+ or scope["path"].endswith(".ico")
121
+ or scope["path"].endswith(".png")
122
+ or scope["path"].endswith(".jpg")
123
+ or scope["path"].endswith(".jpeg")
124
+ or scope["path"].endswith(".gif")
81
125
  )
82
126
 
83
127
 
84
128
  def add_middleware(
85
129
  app: FastAPI,
86
130
  ) -> None:
87
- """Use this method to add this middleware to your fastapi application."""
131
+ """
132
+ Add better access logging to fastapi:
133
+
134
+ >>> from structlog_config import fastapi_access_logger
135
+ >>> fastapi_access_logger.add_middleware(app)
136
+
137
+ You'll also want to disable the default uvicorn logs:
138
+
139
+ >>> uvicorn.run(..., log_config=None, access_log=False)
140
+ """
88
141
 
89
142
  @app.middleware("http")
90
143
  async def access_log_middleware(
@@ -113,7 +166,7 @@ def add_middleware(
113
166
  method=scope["method"],
114
167
  path=scope["path"],
115
168
  query=scope["query_string"].decode(),
116
- client_ip=get_client_addr(scope),
169
+ client_ip=client_ip_from_request(request),
117
170
  route=route_name,
118
171
  )
119
172
 
@@ -77,6 +77,7 @@ def pretty_traceback_exception_formatter(sio: TextIO, exc_info: ExcInfo) -> None
77
77
  from pretty_traceback.formatting import exc_to_traceback_str
78
78
 
79
79
  _, exc_value, traceback = exc_info
80
+ # TODO support local_stack_only env var support
80
81
  formatted_exception = exc_to_traceback_str(exc_value, traceback, color=not NO_COLOR)
81
82
  sio.write("\n" + formatted_exception)
82
83
 
@@ -97,8 +97,8 @@ def redirect_stdlib_loggers(json_logger: bool):
97
97
  root_logger.propagate = True
98
98
 
99
99
  # TODO there is a JSON-like format that can be used to configure loggers instead :/
100
+ # we should probably transition to using that format instead of this customized mapping
100
101
  std_logging_configuration = {
101
- # "httpcore": {},
102
102
  "httpx": {
103
103
  "levels": {
104
104
  "INFO": "WARNING",
@@ -109,6 +109,12 @@ def redirect_stdlib_loggers(json_logger: bool):
109
109
  "INFO": "WARNING",
110
110
  }
111
111
  },
112
+ # stripe INFO logs are pretty noisy by default
113
+ "stripe": {
114
+ "levels": {
115
+ "INFO": "WARNING",
116
+ }
117
+ },
112
118
  }
113
119
  """
114
120
  These loggers either:
@@ -1,14 +1,16 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.3
2
2
  Name: structlog-config
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: A comprehensive structlog configuration with sensible defaults for development and production environments, featuring context management, exception formatting, and path prettification.
5
- Project-URL: Repository, https://github.com/iloveitaly/structlog-config
5
+ Keywords: logging,structlog,json-logging,structured-logging
6
+ Author: Michael Bianco
6
7
  Author-email: Michael Bianco <mike@mikebian.co>
7
- Keywords: json-logging,logging,structlog,structured-logging
8
- Requires-Python: >=3.10
9
8
  Requires-Dist: orjson>=3.10.15
10
9
  Requires-Dist: python-decouple-typed>=3.11.0
10
+ Requires-Dist: python-ipware>=3.0.0
11
11
  Requires-Dist: structlog>=25.2.0
12
+ Requires-Python: >=3.10
13
+ Project-URL: Repository, https://github.com/iloveitaly/structlog-config
12
14
  Description-Content-Type: text/markdown
13
15
 
14
16
  # Opinionated Defaults for Structlog
@@ -30,12 +32,43 @@ Here are the main goals:
30
32
  ## Usage
31
33
 
32
34
  ```python
33
- from structlog_config import configure_logging
35
+ from structlog_config import configure_logger
36
+
37
+ log = configure_logger()
38
+
39
+ log.info("the log", key="value")
40
+ ```
41
+
42
+ ## TRACE Logging Level
43
+
44
+ This package adds support for a custom `TRACE` logging level (level 5) that's even more verbose than `DEBUG`. This is useful for extremely detailed debugging scenarios.
45
+
46
+ The `TRACE` level is automatically set up when you call `configure_logger()`. You can use it like any other logging level:
47
+
48
+ ```python
49
+ import logging
50
+ from structlog_config import configure_logger
51
+
52
+ log = configure_logger()
53
+
54
+ # Using structlog
55
+ log.info("This is info")
56
+ log.debug("This is debug")
57
+ log.trace("This is trace") # Most verbose
58
+
59
+ # Using stdlib logging
60
+ logging.trace("Module-level trace message")
61
+ logger = logging.getLogger(__name__)
62
+ logger.trace("Instance trace message")
63
+ ```
64
+
65
+ Set the log level to TRACE using the environment variable:
34
66
 
35
- configure_logging()
67
+ ```bash
68
+ LOG_LEVEL=TRACE
36
69
  ```
37
70
 
38
- ## Stdib Log Management
71
+ ## Stdlib Log Management
39
72
 
40
73
  By default, all stdlib loggers are:
41
74
 
@@ -2,13 +2,13 @@ structlog_config/__init__.py,sha256=DyY4x3_dY_hPNbS1aM7JRCGadTa1dYDIPzgrHu3AP68,
2
2
  structlog_config/constants.py,sha256=O1nPnB29yZdqqaI7aeTUrimA3LOtA5WpP6BGPLWJvj8,510
3
3
  structlog_config/env_config.py,sha256=_EJO0rgAKndRPSh4wuBaH3bui9F3nIpn8FaEkjAjZso,1737
4
4
  structlog_config/environments.py,sha256=JpZYVVDGxEf1EaKdPdn6Jo-4wJK6SqF0ueFl7e2TBvI,612
5
- structlog_config/fastapi_access_logger.py,sha256=31tSlpe50joO5uBPAQzUWGfv_Vs_Z-9dMhuq3hwa9iY,3592
6
- structlog_config/formatters.py,sha256=cprGEjvRFphJixbb0nVCpPn9sfw_Wv4d2vPtKDpM05A,5846
5
+ structlog_config/fastapi_access_logger.py,sha256=REwchxGb5WHbiQi2zYtBPfkGHk-NvCKM2pVwJlwG5H8,5464
6
+ structlog_config/formatters.py,sha256=ll0Y0QeRs1DMmD-ft1n1zA4Vn2STRSK-mOrczYB2OjE,5898
7
7
  structlog_config/levels.py,sha256=z1fTpvCCbAwcFK2k7rHWh_p-FqfFh4yIWCTZ1MNf_4U,993
8
8
  structlog_config/packages.py,sha256=asxrzLR-iRYAbkoSYutyTdIRcruTjHgkzfe2pjm2VFM,519
9
- structlog_config/stdlib_logging.py,sha256=oUbd-3joR89hOXTbZxgrsSOyhBMjEyW6h68qQpCXabU,7120
9
+ structlog_config/stdlib_logging.py,sha256=Wnn59oRBIqn708CpR-akqVcG9ccSfCMLh56_7wxZRH0,7350
10
10
  structlog_config/trace.py,sha256=dBaSynxmw4Wg79wSHqYEMoByvv--v_oQw61dRdg4xUI,2016
11
11
  structlog_config/warnings.py,sha256=gKEcuHWqH0BaKitJtQkv-uJ0Z3uCH5nn6k8qpqjR-RM,998
12
- structlog_config-0.2.0.dist-info/METADATA,sha256=Rq5mcJeFR_0SgIinFLTfSeIXZsGphXDJsvJkJNC-ltY,4606
13
- structlog_config-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- structlog_config-0.2.0.dist-info/RECORD,,
12
+ structlog_config-0.4.0.dist-info/WHEEL,sha256=4n27za1eEkOnA7dNjN6C5-O2rUiw6iapszm14Uj-Qmk,79
13
+ structlog_config-0.4.0.dist-info/METADATA,sha256=R2XtpZuBtPdum5qHcmmCoXbSFoQPm0iv4VQcAX0I7zI,5472
14
+ structlog_config-0.4.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.8.13
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -1,4 +0,0 @@
1
- Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
3
- Root-Is-Purelib: true
4
- Tag: py3-none-any