Compare commits

...

2 Commits

Author SHA1 Message Date
maxid
f5761bc661 run the container as the user configured if not starting as root 2026-02-04 18:50:52 +01:00
Maximilian Dorninger
c45c9e5873 add correlation id to logging (#398)
This PR adds Correlation IDs to logs and request responses.

```
2026-02-04 12:40:32,793 - [afd825081d874d6e835b5c59a6ddb371] DEBUG - media_manager.movies - get_importable_movies(): Found 5 importable movies.
2026-02-04 12:40:32,794 - [afd825081d874d6e835b5c59a6ddb371] INFO - uvicorn.access - send(): 172.19.0.1:64094 - "GET /api/v1/movies/importable HTTP/1.1" 200
2026-02-04 12:40:47,322 - [41d30b7003fd45288c6a4bb1cfba5e7a] INFO - uvicorn.access - send(): 127.0.0.1:52964 - "GET /api/v1/health HTTP/1.1" 200
2026-02-04 12:41:17,408 - [157027ea5dde472a9e620f53739ccd53] INFO - uvicorn.access - send(): 127.0.0.1:39850 - "GET /api/v1/health HTTP/1.1" 200
```
2026-02-04 13:55:05 +01:00
5 changed files with 57 additions and 14 deletions

View File

@@ -21,13 +21,20 @@ LOG_FILE = Path(os.getenv("LOG_FILE", "/app/config/media_manager.log"))
LOGGING_CONFIG = { LOGGING_CONFIG = {
"version": 1, "version": 1,
"disable_existing_loggers": False, "disable_existing_loggers": False,
"filters": {
"correlation_id": {
"()": "asgi_correlation_id.CorrelationIdFilter",
"uuid_length": 32,
"default_value": "-",
},
},
"formatters": { "formatters": {
"default": { "default": {
"format": "%(asctime)s - %(levelname)s - %(name)s - %(funcName)s(): %(message)s" "format": "%(asctime)s - [%(correlation_id)s] %(levelname)s - %(name)s - %(funcName)s(): %(message)s"
}, },
"json": { "json": {
"()": ISOJsonFormatter, "()": ISOJsonFormatter,
"format": "%(asctime)s %(levelname)s %(name)s %(message)s", "format": "%(asctime)s %(correlation_id)s %(levelname)s %(name)s %(message)s",
"rename_fields": { "rename_fields": {
"levelname": "level", "levelname": "level",
"asctime": "timestamp", "asctime": "timestamp",
@@ -39,11 +46,13 @@ LOGGING_CONFIG = {
"console": { "console": {
"class": "logging.StreamHandler", "class": "logging.StreamHandler",
"formatter": "default", "formatter": "default",
"filters": ["correlation_id"],
"stream": sys.stdout, "stream": sys.stdout,
}, },
"file": { "file": {
"class": "logging.handlers.RotatingFileHandler", "class": "logging.handlers.RotatingFileHandler",
"formatter": "json", "formatter": "json",
"filters": ["correlation_id"],
"filename": str(LOG_FILE), "filename": str(LOG_FILE),
"maxBytes": 10485760, "maxBytes": 10485760,
"backupCount": 5, "backupCount": 5,

View File

@@ -2,6 +2,7 @@ import logging
import os import os
import uvicorn import uvicorn
from asgi_correlation_id import CorrelationIdMiddleware
from fastapi import APIRouter, FastAPI, Request, Response from fastapi import APIRouter, FastAPI, Request, Response
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
@@ -71,6 +72,7 @@ app.add_middleware(
allow_credentials=True, allow_credentials=True,
allow_methods=["GET", "PUT", "POST", "DELETE", "PATCH", "HEAD", "OPTIONS"], allow_methods=["GET", "PUT", "POST", "DELETE", "PATCH", "HEAD", "OPTIONS"],
) )
app.add_middleware(CorrelationIdMiddleware, header_name="X-Correlation-ID")
api_app = APIRouter(prefix="/api/v1") api_app = APIRouter(prefix="/api/v1")

View File

@@ -145,21 +145,30 @@ else
echo "Config file found at: $CONFIG_FILE" echo "Config file found at: $CONFIG_FILE"
fi fi
# permission fix # check if running as root, if yes, fix permissions
echo "Ensuring file permissions for mediamanager user..." if [ "$(id -u)" = '0' ]; then
echo "Running as root. Ensuring file permissions for mediamanager user..."
chown -R mediamanager:mediamanager "$CONFIG_DIR"
chown -R mediamanager:mediamanager "$CONFIG_DIR" if [ -d "/data" ]; then
if [ "$(stat -c '%U' /data)" != "mediamanager" ]; then
if [ -d "/data" ]; then echo "Fixing ownership of /data (this may take a while for large media libraries)..."
if [ "$(stat -c '%U' /data)" != "mediamanager" ]; then chown -R mediamanager:mediamanager /data
echo "Fixing ownership of /data (this may take a while for large libraries)..." else
chown -R mediamanager:mediamanager /data echo "/data ownership is already correct."
fi
fi fi
else
echo "Running as non-root user ($(id -u)). Skipping permission fixes."
echo "Note: Ensure your host volumes are manually set to the correct permissions."
fi fi
echo "Running DB migrations..." echo "Running DB migrations..."
gosu mediamanager uv run alembic upgrade head if [ "$(id -u)" = '0' ]; then
gosu mediamanager uv run alembic upgrade head
else
uv run alembic upgrade head
fi
echo "Starting MediaManager backend service..." echo "Starting MediaManager backend service..."
echo "" echo ""
@@ -172,9 +181,16 @@ echo ""
DEVELOPMENT_MODE=${MEDIAMANAGER_MISC__DEVELOPMENT:-FALSE} DEVELOPMENT_MODE=${MEDIAMANAGER_MISC__DEVELOPMENT:-FALSE}
PORT=${PORT:-8000} PORT=${PORT:-8000}
if [ "$DEVELOPMENT_MODE" == "TRUE" ]; then if [ "$DEVELOPMENT_MODE" == "TRUE" ]; then
echo "Development mode is enabled, enabling auto-reload..." echo "Development mode is enabled, enabling auto-reload..."
exec gosu mediamanager uv run fastapi run /app/media_manager/main.py --port "$PORT" --proxy-headers --reload DEV_OPTIONS="--reload"
else else
exec gosu mediamanager uv run fastapi run /app/media_manager/main.py --port "$PORT" --proxy-headers DEV_OPTIONS=""
fi fi
if [ "$(id -u)" = '0' ]; then
exec gosu mediamanager uv run fastapi run /app/media_manager/main.py --port "$PORT" --proxy-headers $DEV_OPTIONS
else
exec uv run fastapi run /app/media_manager/main.py --port "$PORT" --proxy-headers $DEV_OPTIONS
fi

View File

@@ -34,6 +34,7 @@ dependencies = [
"transmission-rpc>=7.0.11", "transmission-rpc>=7.0.11",
"libtorrent>=2.0.11", "libtorrent>=2.0.11",
"pathvalidate>=3.3.1", "pathvalidate>=3.3.1",
"asgi-correlation-id>=4.3.4",
] ]
[dependency-groups] [dependency-groups]

15
uv.lock generated
View File

@@ -105,6 +105,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" },
] ]
[[package]]
name = "asgi-correlation-id"
version = "4.3.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
{ name = "starlette" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f4/ff/a6538245ac1eaa7733ec6740774e9d5add019e2c63caa29e758c16c0afdd/asgi_correlation_id-4.3.4.tar.gz", hash = "sha256:ea6bc310380373cb9f731dc2e8b2b6fb978a76afe33f7a2384f697b8d6cd811d", size = 20075, upload-time = "2024-10-17T11:44:30.324Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d9/ab/6936e2663c47a926e0659437b9333ad87d1ff49b1375d239026e0a268eba/asgi_correlation_id-4.3.4-py3-none-any.whl", hash = "sha256:36ce69b06c7d96b4acb89c7556a4c4f01a972463d3d49c675026cbbd08e9a0a2", size = 15262, upload-time = "2024-10-17T11:44:28.739Z" },
]
[[package]] [[package]]
name = "attrs" name = "attrs"
version = "25.4.0" version = "25.4.0"
@@ -854,6 +867,7 @@ source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "alembic" }, { name = "alembic" },
{ name = "apscheduler" }, { name = "apscheduler" },
{ name = "asgi-correlation-id" },
{ name = "bencoder" }, { name = "bencoder" },
{ name = "cachetools" }, { name = "cachetools" },
{ name = "fastapi", extra = ["standard"] }, { name = "fastapi", extra = ["standard"] },
@@ -894,6 +908,7 @@ dev = [
requires-dist = [ requires-dist = [
{ name = "alembic", specifier = ">=1.16.1" }, { name = "alembic", specifier = ">=1.16.1" },
{ name = "apscheduler", specifier = ">=3.11.0" }, { name = "apscheduler", specifier = ">=3.11.0" },
{ name = "asgi-correlation-id", specifier = ">=4.3.4" },
{ name = "bencoder", specifier = ">=0.2.0" }, { name = "bencoder", specifier = ">=0.2.0" },
{ name = "cachetools", specifier = ">=6.0.0" }, { name = "cachetools", specifier = ">=6.0.0" },
{ name = "fastapi", extras = ["standard"], specifier = ">=0.115.12" }, { name = "fastapi", extras = ["standard"], specifier = ">=0.115.12" },