Skip to content

Commit

Permalink
Merge pull request #1491 from fractal-analytics-platform/uvicorn-logging
Browse files Browse the repository at this point in the history
Improve uvicorn/gunicorn logging
  • Loading branch information
tcompa committed May 15, 2024
2 parents bc5b48e + 4802fb9 commit b3c1a80
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 11 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@

# Unreleased

> NOTE: This version changes log formats.
> For `uvicorn` logs, this change requires no action.
> For `gunicorn`, logs formats are only changed by adding the following
> command-line option:
> `gunicorn ... --logger-class fractal_server.logger.gunicorn_logger.FractalGunicornLogger`.
* API:
* Add `FRACTAL_API_V1_MODE` environment variable to include/exclude V1 API (\#1480).
* Change format of uvicorn loggers (\#1491).
* Introduce `FractalGunicornLogger` class (\#1491).
* Runner:
* Fix missing `.log` files in server folder for SLURM jobs (\#1479).
* Database:
* Remove `UserOAuth.project_list` and `UserOAuth.project_list_v2` relationships (\#1482).
* Dev depdendencies:
* Dev dependencies:
* Bump `pytest` to `8.1.*` (#1486).
* Bump `coverage` to `7.5.*` (#1486).
* Bump `pytest-docker` to `3.1.*` (#1486).
Expand Down
30 changes: 28 additions & 2 deletions fractal_server/logger.py → fractal_server/logger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
from typing import Optional
from typing import Union

from .config import get_settings
from .syringe import Inject
from ..config import get_settings
from ..syringe import Inject


LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
Expand Down Expand Up @@ -136,3 +136,29 @@ def reset_logger_handlers(logger: logging.Logger) -> None:
"""
close_logger(logger)
logger.handlers.clear()


def config_uvicorn_loggers():
"""
Change the formatter for the uvicorn access/error loggers.
This is similar to https://stackoverflow.com/a/68864979/19085332. See also
https://github.com/tiangolo/fastapi/issues/1508.
This function is meant to work in two scenarios:
1. The most relevant case is for a `gunicorn` startup command, with
`--access-logfile` and `--error-logfile` options set.
2. The case of `fractalctl start` (directly calling `uvicorn`).
Because of the second use case, we need to check whether uvicorn loggers
already have a handler. If not, we skip the formatting.
"""

access_logger = logging.getLogger("uvicorn.access")
if len(access_logger.handlers) > 0:
access_logger.handlers[0].setFormatter(LOG_FORMATTER)

error_logger = logging.getLogger("uvicorn.error")
if len(error_logger.handlers) > 0:
error_logger.handlers[0].setFormatter(LOG_FORMATTER)
19 changes: 19 additions & 0 deletions fractal_server/logger/gunicorn_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
This module (which is only executed if `gunicorn` can be imported) subclasses
the gunicorn `Logger` class in order to slightly change its log formats.
This class can be used by including this `gunicorn` command-line option:
```
--logger-class fractal_server.logger.gunicorn_logger.FractalGunicornLogger
```
"""

try:
from gunicorn.glogging import Logger as GunicornLogger

class FractalGunicornLogger(GunicornLogger):
error_fmt = r"%(asctime)s - gunicorn.error - %(levelname)s - [pid %(process)d] - %(message)s" # noqa: E501
datefmt = r"%Y-%m-%d %H:%M:%S,%u"

except (ModuleNotFoundError, ImportError):
pass
2 changes: 2 additions & 0 deletions fractal_server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from .app.security import _create_first_user
from .config import get_settings
from .logger import config_uvicorn_loggers
from .logger import reset_logger_handlers
from .logger import set_logger
from .syringe import Inject
Expand Down Expand Up @@ -84,6 +85,7 @@ async def __on_startup() -> None:
callable.
"""
check_settings()
config_uvicorn_loggers()


def start_application() -> FastAPI:
Expand Down
1 change: 1 addition & 0 deletions tests/data/example_server_startup/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ fractal-server.err
artifacts
LOG*
logs
logs*
*.db
FRACTAL_TASKS_DIR
.fractal_server.env
Expand Down
1 change: 1 addition & 0 deletions tests/data/example_server_startup/cleanup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
rm test.db
rm -r Tasks
rm -r Artifacts
rm logs*
8 changes: 0 additions & 8 deletions tests/data/example_server_startup/run_server.sh

This file was deleted.

13 changes: 13 additions & 0 deletions tests/data/example_server_startup/run_server_gunicorn.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

# Create an empty db
poetry run fractalctl set-db

# Start the server
poetry run gunicorn fractal_server.main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
--access-logfile logs-fractal-server.access \
--error-logfile logs-fractal-server.error \
--logger-class fractal_server.logger.gunicorn_logger.FractalGunicornLogger \
7 changes: 7 additions & 0 deletions tests/data/example_server_startup/run_server_uvicorn.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

# Create an empty db
poetry run fractalctl set-db

# Start the server
poetry run fractalctl start --port 8000
9 changes: 9 additions & 0 deletions tests/no_version/test_unit_config_uvicorn_loggers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from fractal_server.main import config_uvicorn_loggers


def test_config_uvicorn_loggers():
"""
This test simply runs `config_uvicorn_loggers`, but it does not assert
anything. It is only meant to catch some trivial errors.
"""
config_uvicorn_loggers()

0 comments on commit b3c1a80

Please sign in to comment.