Skip to content

Commit e46eac4

Browse files
committed
add first version
1 parent dd44202 commit e46eac4

File tree

8 files changed

+511
-1
lines changed

8 files changed

+511
-1
lines changed

.github/dependabot.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
version: 2
3+
updates:
4+
- package-ecosystem: "github-actions"
5+
directory: "/"
6+
schedule:
7+
interval: "weekly"
8+
assignees:
9+
- "chrisK824"
10+
reviewers:
11+
- "chrisK824"
12+
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: CI/CD
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
release:
7+
types:
8+
- published
9+
10+
jobs:
11+
linting:
12+
name: CI against Python versions
13+
runs-on: ubuntu-latest
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
18+
19+
steps:
20+
- uses: actions/checkout@v4
21+
- name: Set up Python ${{ matrix.python-version }}
22+
uses: actions/setup-python@v5
23+
with:
24+
python-version: ${{ matrix.python-version }}
25+
- name: Install dependencies
26+
run: |
27+
python -m pip install --upgrade pip
28+
python -m pip install flake8 pytest
29+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
30+
- name: Lint with flake8
31+
run: |
32+
# stop the build if there are Python syntax errors or undefined names
33+
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
34+
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
35+
flake8 . --count --exit-zero --max-complexity=20 --max-line-length=127 --statistics
36+
37+
build:
38+
name: Build Python Package
39+
needs:
40+
- linting
41+
runs-on: ubuntu-latest
42+
steps:
43+
- uses: actions/checkout@v4
44+
- name: Install pypa/build
45+
run: >-
46+
python3 -m
47+
pip install
48+
build
49+
--user
50+
- name: Build a binary wheel and a source tarball
51+
run: python3 -m build
52+
- name: Store the distribution packages
53+
uses: actions/upload-artifact@v4
54+
with:
55+
name: fastapi-gae-logging
56+
path: dist/
57+
58+
publish-to-pypi:
59+
name: Publish package to PyPI
60+
if: github.event_name == 'release' && github.event.action == 'published'
61+
needs:
62+
- build
63+
runs-on: ubuntu-latest
64+
environment:
65+
name: pypi
66+
url: https://pypi.org/p/fastapi-gae-logging
67+
permissions:
68+
id-token: write
69+
70+
steps:
71+
- name: Download all the dists
72+
uses: actions/download-artifact@v4
73+
with:
74+
name: fastapi-gae-logging
75+
path: dist/
76+
- name: Publish distribution 📦 to PyPI
77+
uses: pypa/gh-action-pypi-publish@release/v1

.vscode/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"flake8.args": [
3+
"--max-line-length=127"
4+
]
5+
}

README.md

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,96 @@
1-
# fastapi-gae-logging
1+
# fastapi-gae-logging
2+
Custom Cloud Logging handler for FastAPI applications deployed in Google App Engine.
3+
Groups logs coming from the same request lifecycle and propagates the maximum log level throughout the request lifecycle using middleware and context management.
4+
5+
## Install
6+
`pip install fastapi-gae-logging`
7+
8+
## Features:
9+
10+
- **Request Logs Grouping**: Groups logs coming from the same request lifecycle to ease out log analysis using the Google Cloud Log Explorer. Grouping logger name can be customised and it defaults to the Google Cloud Project ID with '-request-logger' as a suffix.
11+
- **Request Maximum Log Level propagation**: Propagates the maximum log level throughout the request lifecycle to ease out log searching based on severity of an issue.
12+
13+
## API
14+
- Custom Cloud Logging Handler to use with official library `google-cloud-logging`: `FastAPIGAELoggingHandler`
15+
16+
17+
## Example
18+
19+
```python
20+
from fastapi import FastAPI
21+
from fastapi.responses import JSONResponse
22+
from fastapi.exceptions import HTTPException
23+
import logging
24+
import os
25+
26+
27+
app = FastAPI()
28+
29+
if os.getenv('GAE_ENV', '').startswith('standard'):
30+
import google.cloud.logging
31+
from google.cloud.logging_v2.handlers import setup_logging
32+
from fastapi_gae_logging import FastAPIGAELoggingHandler
33+
34+
client = google.cloud.logging.Client()
35+
gae_log_handler = FastAPIGAELoggingHandler(app=app, client=client)
36+
setup_logging(handler=gae_log_handler)
37+
38+
39+
logging.getLogger().setLevel(logging.DEBUG)
40+
41+
42+
@app.get("/info")
43+
def info():
44+
logging.debug("this is a debug")
45+
logging.info("this is an info")
46+
return JSONResponse(
47+
content={
48+
"message": "info"
49+
}
50+
)
51+
52+
53+
@app.get("/warning")
54+
async def warning():
55+
logging.debug("this is a debug")
56+
logging.info("this is an info")
57+
logging.warning("this is a warning")
58+
return JSONResponse(
59+
content={
60+
"message": "warning"
61+
}
62+
)
63+
64+
65+
@app.get("/error")
66+
def error():
67+
logging.debug("this is a debug")
68+
logging.info("this is an info")
69+
logging.warning("this is a warning")
70+
logging.error("this is an error")
71+
return JSONResponse(
72+
content={
73+
"message": "error"
74+
}
75+
)
76+
77+
78+
@app.get("/exception")
79+
def exception():
80+
logging.debug("this is a debug")
81+
logging.info("this is an info")
82+
logging.error("this is an error")
83+
raise ValueError("This is a value error")
84+
85+
86+
@app.get("/http_exception")
87+
def http_exception():
88+
logging.debug("this is an debug")
89+
logging.info("this is an info")
90+
raise HTTPException(
91+
status_code=404,
92+
detail={
93+
"error": "Resource not found"
94+
}
95+
)
96+
```

examples.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from fastapi import FastAPI
2+
from fastapi.responses import JSONResponse
3+
from fastapi.exceptions import HTTPException
4+
import logging
5+
import os
6+
7+
8+
app = FastAPI()
9+
10+
11+
# Init logging
12+
if os.getenv('GAE_ENV', '').startswith('standard'):
13+
import google.cloud.logging
14+
from google.cloud.logging_v2.handlers import setup_logging
15+
from fastapi_gae_logging.fastapi_gae_logging import FastAPIGAELoggingHandler
16+
17+
client = google.cloud.logging.Client()
18+
gae_log_handler = FastAPIGAELoggingHandler(app=app, client=client)
19+
setup_logging(handler=gae_log_handler)
20+
21+
22+
logging.getLogger().setLevel(logging.DEBUG)
23+
24+
25+
@app.get("/info")
26+
def info():
27+
logging.debug("this is a debug")
28+
logging.info("this is an info")
29+
return JSONResponse(
30+
content={
31+
"message": "info"
32+
}
33+
)
34+
35+
36+
@app.get("/warning")
37+
async def warning():
38+
logging.debug("this is a debug")
39+
logging.info("this is an info")
40+
logging.warning("this is a warning")
41+
return JSONResponse(
42+
content={
43+
"message": "warning"
44+
}
45+
)
46+
47+
48+
@app.get("/error")
49+
def error():
50+
logging.debug("this is a debug")
51+
logging.info("this is an info")
52+
logging.warning("this is a warning")
53+
logging.error("this is an error")
54+
return JSONResponse(
55+
content={
56+
"message": "error"
57+
}
58+
)
59+
60+
61+
@app.get("/exception")
62+
def exception():
63+
logging.debug("this is a debug")
64+
logging.info("this is an info")
65+
logging.error("this is an error")
66+
raise ValueError("This is a value error")
67+
68+
69+
@app.get("/http_exception")
70+
def http_exception():
71+
logging.debug("this is an debug")
72+
logging.info("this is an info")
73+
raise HTTPException(
74+
status_code=404,
75+
detail={
76+
"error": "Resource not found"
77+
}
78+
)

fastapi_gae_logging/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .fastapi_gae_logging import FastAPIGAELoggingHandler
2+
3+
__all__ = [
4+
"FastAPIGAELoggingHandler",
5+
]

0 commit comments

Comments
 (0)