Skip to content

Commit 87bedf5

Browse files
committed
feat: color logging
1 parent b387e1a commit 87bedf5

File tree

9 files changed

+191
-6
lines changed

9 files changed

+191
-6
lines changed

.github/workflows/pytest-all.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: pytest-all
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
inputs:
10+
debug_enabled:
11+
type: boolean
12+
description: "Run with tmate debugging enabled"
13+
required: false
14+
default: false
15+
16+
concurrency:
17+
group: ${{ github.workflow }}-${{ github.ref }}
18+
cancel-in-progress: true
19+
20+
jobs:
21+
pytest-all:
22+
strategy:
23+
matrix:
24+
python-version: ["3.9", "3.10", "3.11", "3.12"]
25+
uses: Komorebi-AI/github-actions/.github/workflows/pytest-uv.yml@main
26+
with:
27+
uv-version: 0.8.0
28+
python-version: ${{ matrix.python-version }}
29+
debug-enabled: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ ENV PATH="/app/.venv/bin:$PATH"
3636
WORKDIR /app
3737

3838
# Run the FastAPI application by default
39-
CMD ["uvicorn", "template.api:app", "--host", "0.0.0.0", "--port", "80"]
39+
CMD ["uvicorn", "template.api:app", "--host", "0.0.0.0", "--port", "80", "--log-config", "log_conf.yaml"]

Makefile

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.ONESHELL:
2-
.PHONY: install hooks hooks-update ruff test test-api docker build run debug attach push
2+
.PHONY: install hooks hooks-update ruff test test-all test-api docker build run debug attach push
33

44
SHELL=/bin/bash
55
DOCKER_IMG_NAME=ghcr.io/komorebi-ai/python-template
@@ -35,8 +35,15 @@ ruff:
3535
test:
3636
$(RUN) pytest
3737

38+
# For libraries we may want to test in all supported Python versions
39+
test-all:
40+
$(RUN) --python 3.9 --all-extras --with . pytest
41+
$(RUN) --python 3.10 --all-extras --with . pytest
42+
$(RUN) --python 3.11 --all-extras --with . pytest
43+
$(RUN) --python 3.12 --all-extras --with . pytest
44+
3845
test-api:
39-
uvx --from httpie http GET localhost:6000
46+
uvx --from httpie http localhost:7000
4047

4148
docker: build run
4249

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,23 @@ make ruff
140140
```{bash}
141141
make test
142142
```
143+
144+
#### Test API
145+
146+
1. Install `httpie` globally:
147+
148+
```{bash}
149+
uv tool install httpie
150+
```
151+
152+
2. Send GET request:
153+
154+
```{bash}
155+
http localhost:7000
156+
```
157+
158+
3. Send POST request:
159+
160+
```{bash}
161+
http localhost:7000/predict input=5
162+
```

log_conf.yaml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
version: 1
2+
disable_existing_loggers: False
3+
4+
formatters:
5+
default:
6+
(): colorlog.ColoredFormatter
7+
format: "%(asctime_log_color)s%(asctime)s%(reset)s - %(name_log_color)s%(name)s%(reset)s - %(log_color)s%(levelname)s%(reset)s - %(message_log_color)s%(message)s%(reset)s"
8+
log_colors:
9+
DEBUG: white
10+
INFO: green
11+
WARNING: yellow
12+
ERROR: red
13+
CRITICAL: bold_red
14+
secondary_log_colors:
15+
asctime:
16+
DEBUG: cyan
17+
INFO: cyan
18+
WARNING: cyan
19+
ERROR: cyan
20+
CRITICAL: cyan
21+
name:
22+
DEBUG: blue
23+
INFO: blue
24+
WARNING: blue
25+
ERROR: blue
26+
CRITICAL: blue
27+
message:
28+
DEBUG: white
29+
INFO: white
30+
WARNING: white
31+
ERROR: white
32+
CRITICAL: white
33+
34+
access:
35+
(): colorlog.ColoredFormatter
36+
format: "%(asctime_log_color)s%(asctime)s%(reset)s - %(name_log_color)s%(name)s%(reset)s - %(log_color)s%(levelname)s%(reset)s - %(message_log_color)s%(message)s%(reset)s"
37+
log_colors:
38+
INFO: green
39+
ERROR: red
40+
secondary_log_colors:
41+
asctime:
42+
INFO: cyan
43+
ERROR: cyan
44+
name:
45+
INFO: blue
46+
ERROR: blue
47+
message:
48+
INFO: white
49+
ERROR: white
50+
51+
handlers:
52+
default:
53+
class: logging.StreamHandler
54+
formatter: default
55+
stream: ext://sys.stderr
56+
57+
access:
58+
class: logging.StreamHandler
59+
formatter: access
60+
stream: ext://sys.stdout
61+
62+
loggers:
63+
uvicorn.error:
64+
level: INFO
65+
handlers:
66+
- default
67+
propagate: no
68+
69+
uvicorn.access:
70+
level: INFO
71+
handlers:
72+
- access
73+
propagate: no
74+
75+
root:
76+
level: WARNING
77+
handlers:
78+
- default

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ dependencies = [
1616
"fastapi",
1717
"uvicorn",
1818
"typer-slim",
19+
"colorlog>=6.9.0",
1920
]
2021

2122
[project.urls]

template/api.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,58 @@
1+
# Use A | B syntax for Union types in Python 3.9
12
from __future__ import annotations
23

4+
import logging
5+
36
from fastapi import FastAPI
7+
from pydantic import BaseModel
48

59
from template.main import __version__
610

11+
logger = logging.getLogger(__name__)
12+
13+
# Overwrite root log level from `log_conf.yaml`
14+
logger.setLevel(logging.DEBUG)
15+
716
app = FastAPI()
817

918

19+
class Request(BaseModel):
20+
"""Data model for the request body."""
21+
22+
input: int
23+
24+
25+
class Response(BaseModel):
26+
"""Data model for the response body."""
27+
28+
output: int
29+
30+
1031
@app.get("/")
1132
def read_root() -> dict[str, str]:
1233
"""Check API version."""
34+
logger.debug("DEBUG")
35+
logger.warning("WARNING")
36+
logger.info("INFO")
37+
logger.error("ERROR")
38+
logger.critical("CRITICAL")
39+
1340
return {"template-api": f"version {__version__}"}
1441

1542

43+
@app.post("/predict")
44+
def predict(request: Request) -> Response:
45+
"""Mock prediction endpoint."""
46+
return {"output": request.input}
47+
48+
1649
if __name__ == "__main__":
1750
import sys
1851

1952
import uvicorn
2053

21-
port = 7654 if len(sys.argv) < 2 else int(sys.argv[1])
54+
port = 7000 if len(sys.argv) < 2 else int(sys.argv[1])
2255
host = "127.0.0.1" if len(sys.argv) < 3 else sys.argv[2]
23-
uvicorn.run("api:app", host=host, port=port, reload=True)
56+
uvicorn.run(
57+
"api:app", host=host, port=port, reload=True, log_config="log_conf.yaml"
58+
)

template/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python
2-
from __future__ import annotations # noqa: I001
2+
# Use A | B syntax for Union types in Python 3.9
3+
from __future__ import annotations
34

45
from importlib.metadata import PackageNotFoundError, version
56

uv.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)