ecs-logging-python
                                
                                 ecs-logging-python copied to clipboard
                                
                                    ecs-logging-python copied to clipboard
                            
                            
                            
                        Cache and expose extractors
This PR caches and exposes the extractors dict for two purposes:
- Loads the dict only during initialization, instead of on every logger invocation.
- Provides a clearer and more scalable way to subclass StdlibFormatter.
This is an adapted use case from the library we use for several services in our company that adds information about errors in HTTP requests :
from typing import TYPE_CHECKING
from functools import lru_cache
from ecs_logging import StdlibFormatter
import httpx
if TYPE_CHECKING:
    from logging import LogRecord
httpx._utils.SENSITIVE_HEADERS.update({
    'apikey', 'api-key', 'x-api-key', 'x-auth-token',
})
class EcsFormatter(StdlibFormatter):
    @property
    @lru_cache
    def extractors(self):
        extractors = super().extractors
        extractors['http'] = self._extract_httpx_response
        return extractors
    def _extract_httpx_response(self, record: 'LogRecord') -> dict | None:
        if not record.exc_info or not isinstance(record.exc_info[1], httpx.HTTPStatusError):
            return None
        resp = record.exc_info[1].response
        redact = lambda x: dict(httpx._utils.obfuscate_sensitive_headers(x.multi_items()))
        if (url := resp.request.url).password:
            url = str(url).replace(f':{url.password}@', ':[secure]@')
        return {
            'version':          resp.http_version,
            'request':  {
                'method':       resp.request.method,
                'url':          str(url),
                'headers':      redact(resp.request.headers),
                'body.content': resp.request.content,
            },
            'response': {
                'status_code':  resp.status_code,
                'headers':      redact(resp.headers),
                'body.content': resp.content,
            }
        }
Additionally, this PR also:
- Removes unnecessary try/catch on Literalimport.Literalwas added in Python 3.8.
- Updates pre-commit's config avoiding two warnings.