litewave-logger 0.1.0__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.
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: litewave_logger
3
+ Version: 0.1.0
4
+ Summary: A lightweight logging library with context carry forward
5
+ Author: Litewave
6
+ Author-email: Sonu Sudhakaran <sonu@litewave.ai>
7
+ Requires-Python: >=3.6
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: requests
10
+ Requires-Dist: fastapi
11
+ Dynamic: author
12
+ Dynamic: requires-python
13
+
14
+ # Litewave Logger
15
+
16
+ This module provides a centralized and consistent logging solution for Litewave services. It ensures that a `request-id` is maintained across all services, including HTTP requests and Celery tasks, allowing for easy tracing of requests as they propagate through the system.
17
+
18
+ ## Features
19
+
20
+ - **Centralized Logging**: A single module to configure and manage logging across all services.
21
+ - **Request ID Propagation**: Automatically injects a `request-id` into all log messages.
22
+ - **FastAPI Integration**: Middleware for FastAPI to handle `request-id` for incoming HTTP requests.
23
+ - **Celery Integration**: Signal handlers to propagate the `request-id` to Celery tasks.
24
+ - **Requests Library Patching**: Automatically injects the `request-id` into outgoing HTTP requests made with the `requests` library.
25
+
26
+ ## Installation
27
+
28
+ 1. Add the `litewave_logger` directory to your Python project.
29
+ 2. Ensure that the dependencies listed in the main `requirements.txt` (`fastapi`, `celery`, `requests`) are installed.
30
+
31
+ ## Usage
32
+
33
+ To use the `litewave_logger` in your service, follow these steps:
34
+
35
+ 1. **Initialize the logger**: In your main application file (e.g., `api.py`), import and call the `setup_logging` function. This should be done as early as possible.
36
+
37
+ ```python
38
+ from litewave_logger import setup_logging
39
+
40
+ setup_logging()
41
+ ```
42
+
43
+ 2. **Add the FastAPI middleware**: If your service is a FastAPI application, add the `RequestIdMiddleware` to your FastAPI app.
44
+
45
+ ```python
46
+ from fastapi import FastAPI
47
+ from litewave_logger.middleware import RequestIdMiddleware
48
+
49
+ app = FastAPI()
50
+ app.add_middleware(RequestIdMiddleware)
51
+ ```
52
+
53
+ 3. **Patch the `requests` library**: To ensure the `request-id` is propagated to other services, patch the `requests` library.
54
+
55
+ ```python
56
+ from litewave_logger.requests import patch_requests
57
+
58
+ patch_requests()
59
+ ```
60
+
61
+ 4. **Connect Celery signals**: If your service uses Celery, you need to import the Celery signal handlers to ensure they are registered. You don't need to call them directly.
62
+
63
+ ```python
64
+ from litewave_logger.celery import propagate_request_id_to_celery, clear_request_id_after_celery
65
+ ```
66
+
67
+ ### Example
68
+
69
+ Here's a complete example of how to integrate the `litewave_logger` into a FastAPI application:
70
+
71
+ ```python
72
+ from fastapi import FastAPI
73
+ from litewave_logger import setup_logging
74
+ from litewave_logger.middleware import RequestIdMiddleware
75
+ from litewave_logger.requests import patch_requests
76
+
77
+ # These imports are needed to register the signal handlers
78
+ from litewave_logger.celery import propagate_request_id_to_celery, clear_request_id_after_celery
79
+
80
+ # 1. Initialize logging
81
+ setup_logging()
82
+
83
+ # 2. Patch requests library
84
+ patch_requests()
85
+
86
+ app = FastAPI()
87
+
88
+ # 3. Add RequestIdMiddleware
89
+ app.add_middleware(RequestIdMiddleware)
90
+
91
+ # Your application code here...
92
+ ```
@@ -0,0 +1,79 @@
1
+ # Litewave Logger
2
+
3
+ This module provides a centralized and consistent logging solution for Litewave services. It ensures that a `request-id` is maintained across all services, including HTTP requests and Celery tasks, allowing for easy tracing of requests as they propagate through the system.
4
+
5
+ ## Features
6
+
7
+ - **Centralized Logging**: A single module to configure and manage logging across all services.
8
+ - **Request ID Propagation**: Automatically injects a `request-id` into all log messages.
9
+ - **FastAPI Integration**: Middleware for FastAPI to handle `request-id` for incoming HTTP requests.
10
+ - **Celery Integration**: Signal handlers to propagate the `request-id` to Celery tasks.
11
+ - **Requests Library Patching**: Automatically injects the `request-id` into outgoing HTTP requests made with the `requests` library.
12
+
13
+ ## Installation
14
+
15
+ 1. Add the `litewave_logger` directory to your Python project.
16
+ 2. Ensure that the dependencies listed in the main `requirements.txt` (`fastapi`, `celery`, `requests`) are installed.
17
+
18
+ ## Usage
19
+
20
+ To use the `litewave_logger` in your service, follow these steps:
21
+
22
+ 1. **Initialize the logger**: In your main application file (e.g., `api.py`), import and call the `setup_logging` function. This should be done as early as possible.
23
+
24
+ ```python
25
+ from litewave_logger import setup_logging
26
+
27
+ setup_logging()
28
+ ```
29
+
30
+ 2. **Add the FastAPI middleware**: If your service is a FastAPI application, add the `RequestIdMiddleware` to your FastAPI app.
31
+
32
+ ```python
33
+ from fastapi import FastAPI
34
+ from litewave_logger.middleware import RequestIdMiddleware
35
+
36
+ app = FastAPI()
37
+ app.add_middleware(RequestIdMiddleware)
38
+ ```
39
+
40
+ 3. **Patch the `requests` library**: To ensure the `request-id` is propagated to other services, patch the `requests` library.
41
+
42
+ ```python
43
+ from litewave_logger.requests import patch_requests
44
+
45
+ patch_requests()
46
+ ```
47
+
48
+ 4. **Connect Celery signals**: If your service uses Celery, you need to import the Celery signal handlers to ensure they are registered. You don't need to call them directly.
49
+
50
+ ```python
51
+ from litewave_logger.celery import propagate_request_id_to_celery, clear_request_id_after_celery
52
+ ```
53
+
54
+ ### Example
55
+
56
+ Here's a complete example of how to integrate the `litewave_logger` into a FastAPI application:
57
+
58
+ ```python
59
+ from fastapi import FastAPI
60
+ from litewave_logger import setup_logging
61
+ from litewave_logger.middleware import RequestIdMiddleware
62
+ from litewave_logger.requests import patch_requests
63
+
64
+ # These imports are needed to register the signal handlers
65
+ from litewave_logger.celery import propagate_request_id_to_celery, clear_request_id_after_celery
66
+
67
+ # 1. Initialize logging
68
+ setup_logging()
69
+
70
+ # 2. Patch requests library
71
+ patch_requests()
72
+
73
+ app = FastAPI()
74
+
75
+ # 3. Add RequestIdMiddleware
76
+ app.add_middleware(RequestIdMiddleware)
77
+
78
+ # Your application code here...
79
+ ```
@@ -0,0 +1,45 @@
1
+ import logging
2
+ import uuid
3
+ from contextvars import ContextVar
4
+
5
+ # Context variable to hold the request ID
6
+ # This is used to store the request ID in the context of the request
7
+ # The value of the request ID is set in the RequestIdMiddleware
8
+ # Magically, this is maintained across multiple requests without it getting mixed up.
9
+ request_id_var = ContextVar("request_id", default=None)
10
+
11
+
12
+ class RequestIdFilter(logging.Filter):
13
+ """
14
+ A logging filter that injects the request_id from the context variable into the log record.
15
+ """
16
+ def filter(self, record):
17
+ record.request_id = request_id_var.get()
18
+ return True
19
+
20
+
21
+ def setup_logging():
22
+ """
23
+ Configures the logging for the application.
24
+ It sets up a handler, a formatter, and adds the RequestIdFilter.
25
+ """
26
+ logger = logging.getLogger()
27
+ logger.setLevel(logging.INFO)
28
+
29
+ # Prevent adding duplicate handlers
30
+ if not logger.handlers:
31
+ handler = logging.StreamHandler()
32
+ formatter = logging.Formatter(
33
+ "%(asctime)s - %(name)s - %(levelname)s - [%(request_id)s] - %(message)s"
34
+ )
35
+ handler.setFormatter(formatter)
36
+ logger.addHandler(handler)
37
+
38
+ # Add the filter to all handlers
39
+ for h in logger.handlers:
40
+ if not any(isinstance(f, RequestIdFilter) for f in h.filters):
41
+ h.addFilter(RequestIdFilter())
42
+
43
+ return logger
44
+
45
+ __all__ = ["setup_logging", "request_id_var", "RequestIdFilter"]
@@ -0,0 +1,86 @@
1
+ import uuid
2
+ from celery.signals import (
3
+ task_prerun,
4
+ task_postrun,
5
+ before_task_publish,
6
+ after_setup_task_logger
7
+ )
8
+ from . import request_id_var, RequestIdFilter
9
+ import logging
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ # This signal runs in the CLIENT process (e.g., FastAPI app)
14
+ # This is where we tap into the request ID.
15
+ # Set the header for tasks to use the request ID.
16
+ @before_task_publish.connect
17
+ def propagate_request_id_to_celery_header(sender=None, headers=None, body=None, **kwargs):
18
+ """
19
+ Celery signal handler executed before a task is published.
20
+ It retrieves the request ID from the current context and injects it
21
+ into the task's headers. This propagates the ID from the client to the worker.
22
+ """
23
+ request_id = request_id_var.get()
24
+ if request_id:
25
+ if headers is None:
26
+ headers = {}
27
+ headers['request_id'] = request_id
28
+ logger.info(f"Injecting request_id {request_id} into Celery task headers.")
29
+
30
+ # This signal runs in the WORKER process
31
+ # check if header has request_id
32
+ # this works magically because, we are importing the request_id_var from the same module in the worker process.
33
+ @task_prerun.connect
34
+ def set_request_id_from_celery_header(sender, task_id, task, args, kwargs, **extras):
35
+ """
36
+ Celery signal handler executed before a task runs in the worker.
37
+ It retrieves the request ID from the task's headers (if present)
38
+ and sets it in the worker's context variable.
39
+ """
40
+ request_id = task.request.get('request_id')
41
+ if request_id:
42
+ request_id_var.set(request_id)
43
+ logger.info(f"Set request_id {request_id} from Celery task {task.name} headers")
44
+ else:
45
+ # Generate a new one if it's a standalone task initiated not from a request
46
+ request_id_var.set(str(uuid.uuid4()))
47
+
48
+ @after_setup_task_logger.connect
49
+ def setup_celery_logging(logger, **kwargs):
50
+ """
51
+ This function is connected to the `after_setup_task_logger` signal.
52
+ It modifies the ROOT logger's handlers to ensure that all propagated
53
+ logs (including those from Celery tasks) are formatted correctly
54
+ with our custom request_id.
55
+ """
56
+ # Celery task logs propagate to the root logger. We need to modify
57
+ # the handlers on the root logger.
58
+ root_logger = logging.getLogger()
59
+
60
+ for handler in root_logger.handlers:
61
+ # Check if we've already configured this handler to avoid duplicates
62
+ if not any(isinstance(f, RequestIdFilter) for f in handler.filters):
63
+ # 1. Add the filter to inject the request_id
64
+ handler.addFilter(RequestIdFilter())
65
+
66
+ # 2. Set our custom formatter to display the request_id
67
+ formatter = logging.Formatter(
68
+ "%(asctime)s - %(name)s - %(levelname)s - [%(request_id)s] - %(message)s"
69
+ )
70
+ handler.setFormatter(formatter)
71
+
72
+
73
+ @task_postrun.connect
74
+ def clear_request_id_after_celery(sender, task_id, task, args, kwargs, retval, state, **extras):
75
+ """
76
+ Celery signal handler executed after a task completes.
77
+ It clears the request ID from the context variable in the worker.
78
+ """
79
+ request_id_var.set(None)
80
+
81
+ __all__ = [
82
+ "propagate_request_id_to_celery_header",
83
+ "set_request_id_from_celery_header",
84
+ "clear_request_id_after_celery",
85
+ "setup_celery_logging" # Expose the new function
86
+ ]
@@ -0,0 +1,31 @@
1
+ import uuid
2
+ from starlette.middleware.base import BaseHTTPMiddleware
3
+ from starlette.requests import Request
4
+
5
+ from . import request_id_var
6
+
7
+ # This middleware is used to handle the request ID.
8
+ # It checks for a 'X-Request-ID' header in the incoming request.
9
+ # If the header is present, its value is used as the request ID.
10
+ # If not, a new UUID is generated.
11
+ # The request ID is then stored in a context variable.
12
+ class RequestIdMiddleware(BaseHTTPMiddleware):
13
+ """
14
+ FastAPI middleware to handle the request ID.
15
+ It checks for a 'X-Request-ID' header in the incoming request.
16
+ If the header is present, its value is used as the request ID.
17
+ If not, a new UUID is generated.
18
+ The request ID is then stored in a context variable.
19
+ """
20
+ async def dispatch(self, request: Request, call_next):
21
+ request_id = request.headers.get("X-Request-ID")
22
+ if not request_id:
23
+ request_id = str(uuid.uuid4())
24
+
25
+ request_id_var.set(request_id)
26
+
27
+ response = await call_next(request)
28
+ response.headers["X-Request-ID"] = request_id_var.get()
29
+ return response
30
+
31
+ __all__ = ["RequestIdMiddleware"]
@@ -0,0 +1,32 @@
1
+ import requests
2
+ from . import request_id_var
3
+ import logging
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+ # This is used to patch the requests library to automatically inject the 'X-Request-ID' header
8
+ # into all outgoing requests.
9
+ # It is used to ensure that the request ID is propagated to the external services.
10
+ def patch_requests():
11
+ """
12
+ Patches the requests library to automatically inject the 'X-Request-ID' header
13
+ into all outgoing requests.
14
+ """
15
+ # Get the original Session class
16
+ original_session_class = requests.Session
17
+
18
+ # Create a new Session class that inherits from the original
19
+ class PatchedSession(original_session_class):
20
+ def request(self, method, url, *args, **kwargs):
21
+ request_id = request_id_var.get()
22
+ if request_id:
23
+ headers = kwargs.get("headers", {})
24
+ headers["X-Request-ID"] = request_id
25
+ kwargs["headers"] = headers
26
+ logger.debug(f"Injecting request_id {request_id} into outgoing request to {url}")
27
+ return super().request(method, url, *args, **kwargs)
28
+
29
+ # Replace the original Session class with our patched version
30
+ requests.Session = PatchedSession
31
+
32
+ __all__ = ["patch_requests"]
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: litewave_logger
3
+ Version: 0.1.0
4
+ Summary: A lightweight logging library with context carry forward
5
+ Author: Litewave
6
+ Author-email: Sonu Sudhakaran <sonu@litewave.ai>
7
+ Requires-Python: >=3.6
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: requests
10
+ Requires-Dist: fastapi
11
+ Dynamic: author
12
+ Dynamic: requires-python
13
+
14
+ # Litewave Logger
15
+
16
+ This module provides a centralized and consistent logging solution for Litewave services. It ensures that a `request-id` is maintained across all services, including HTTP requests and Celery tasks, allowing for easy tracing of requests as they propagate through the system.
17
+
18
+ ## Features
19
+
20
+ - **Centralized Logging**: A single module to configure and manage logging across all services.
21
+ - **Request ID Propagation**: Automatically injects a `request-id` into all log messages.
22
+ - **FastAPI Integration**: Middleware for FastAPI to handle `request-id` for incoming HTTP requests.
23
+ - **Celery Integration**: Signal handlers to propagate the `request-id` to Celery tasks.
24
+ - **Requests Library Patching**: Automatically injects the `request-id` into outgoing HTTP requests made with the `requests` library.
25
+
26
+ ## Installation
27
+
28
+ 1. Add the `litewave_logger` directory to your Python project.
29
+ 2. Ensure that the dependencies listed in the main `requirements.txt` (`fastapi`, `celery`, `requests`) are installed.
30
+
31
+ ## Usage
32
+
33
+ To use the `litewave_logger` in your service, follow these steps:
34
+
35
+ 1. **Initialize the logger**: In your main application file (e.g., `api.py`), import and call the `setup_logging` function. This should be done as early as possible.
36
+
37
+ ```python
38
+ from litewave_logger import setup_logging
39
+
40
+ setup_logging()
41
+ ```
42
+
43
+ 2. **Add the FastAPI middleware**: If your service is a FastAPI application, add the `RequestIdMiddleware` to your FastAPI app.
44
+
45
+ ```python
46
+ from fastapi import FastAPI
47
+ from litewave_logger.middleware import RequestIdMiddleware
48
+
49
+ app = FastAPI()
50
+ app.add_middleware(RequestIdMiddleware)
51
+ ```
52
+
53
+ 3. **Patch the `requests` library**: To ensure the `request-id` is propagated to other services, patch the `requests` library.
54
+
55
+ ```python
56
+ from litewave_logger.requests import patch_requests
57
+
58
+ patch_requests()
59
+ ```
60
+
61
+ 4. **Connect Celery signals**: If your service uses Celery, you need to import the Celery signal handlers to ensure they are registered. You don't need to call them directly.
62
+
63
+ ```python
64
+ from litewave_logger.celery import propagate_request_id_to_celery, clear_request_id_after_celery
65
+ ```
66
+
67
+ ### Example
68
+
69
+ Here's a complete example of how to integrate the `litewave_logger` into a FastAPI application:
70
+
71
+ ```python
72
+ from fastapi import FastAPI
73
+ from litewave_logger import setup_logging
74
+ from litewave_logger.middleware import RequestIdMiddleware
75
+ from litewave_logger.requests import patch_requests
76
+
77
+ # These imports are needed to register the signal handlers
78
+ from litewave_logger.celery import propagate_request_id_to_celery, clear_request_id_after_celery
79
+
80
+ # 1. Initialize logging
81
+ setup_logging()
82
+
83
+ # 2. Patch requests library
84
+ patch_requests()
85
+
86
+ app = FastAPI()
87
+
88
+ # 3. Add RequestIdMiddleware
89
+ app.add_middleware(RequestIdMiddleware)
90
+
91
+ # Your application code here...
92
+ ```
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ litewave_logger/__init__.py
5
+ litewave_logger/celery.py
6
+ litewave_logger/middleware.py
7
+ litewave_logger/requests.py
8
+ litewave_logger.egg-info/PKG-INFO
9
+ litewave_logger.egg-info/SOURCES.txt
10
+ litewave_logger.egg-info/dependency_links.txt
11
+ litewave_logger.egg-info/requires.txt
12
+ litewave_logger.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ requests
2
+ fastapi
@@ -0,0 +1 @@
1
+ litewave_logger
@@ -0,0 +1,14 @@
1
+ [project]
2
+ name = "litewave_logger"
3
+ version = "0.1.0"
4
+ description = "A lightweight logging library with context carry forward"
5
+ authors = [{name = "Sonu Sudhakaran", email = "sonu@litewave.ai"}]
6
+ dependencies = [
7
+ "requests", "fastapi"
8
+ ]
9
+ requires-python = ">=3.7"
10
+ readme = "README.md"
11
+
12
+ [build-system]
13
+ requires = ["setuptools", "wheel"]
14
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,21 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="litewave_logger",
5
+ version="0.1.0",
6
+ description="A centralized logging module for Litewave services.",
7
+ author="Litewave",
8
+ packages=find_packages(),
9
+ install_requires=[
10
+ "fastapi",
11
+ "starlette",
12
+ "celery",
13
+ "requests",
14
+ ],
15
+ classifiers=[
16
+ "Programming Language :: Python :: 3",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ ],
20
+ python_requires='>=3.6',
21
+ )