Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions app/exceptions/base.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from http import HTTPStatus

import logfire
from fastapi import Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse, Response
from starlette.exceptions import HTTPException

from app.logger import logger

_PROBLEM_JSON_MEDIA_TYPE = "application/problem+json"


Expand Down Expand Up @@ -69,10 +68,10 @@ def base_exception_handler(request: Request, exc: Exception) -> Response:
media_type=_PROBLEM_JSON_MEDIA_TYPE,
)
case _:
logger.exception(
"Unhandled exception while processing %s",
request.url,
exc=exc,
logfire.exception(
"Unhandled exception while processing {url}",
url=str(request.url),
_exc_info=exc,
)
return JSONResponse(
status_code=500,
Expand Down
4 changes: 2 additions & 2 deletions app/logger/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from app.logger.logger import debug, error, exception, info, setup_logging, warning
from app.logger.configuration import setup_logging

__all__ = ["debug", "error", "exception", "info", "setup_logging", "warning"]
__all__ = ["setup_logging"]
2 changes: 0 additions & 2 deletions app/logger/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
from logging import Handler, Logger

from app.logger.handlers import LOG_LEVEL
from app.settings import app_settings
from app.telemetry import get_otel_log_handler

LogHandlerFactory = Callable[[], Handler]

__all__ = ["LogHandlerFactory", "configure_logger", "setup_logging"]

_CONFIGURED_LOGGER_NAMES = (
app_settings.app_name,
"uvicorn",
"uvicorn.access",
"uvicorn.error",
Expand Down
117 changes: 0 additions & 117 deletions app/logger/formatters.py

This file was deleted.

63 changes: 7 additions & 56 deletions app/logger/handlers.py
Original file line number Diff line number Diff line change
@@ -1,69 +1,20 @@
import logging
import sys
from logging import Formatter, Handler
from typing import TextIO

import logfire

from app.logger.filters import HealthEndpointFilter
from app.logger.formatters import JsonFormatter

LOG_LEVEL = logging.INFO
JSON_LOG_ATTRIBUTE = "app.log.json"
_JSON_PAYLOAD_RECORD_ATTRIBUTE = "_app_json_log_payload"

__all__ = [
"JSON_LOG_ATTRIBUTE",
"LOG_LEVEL",
"JsonPayloadLogfireLoggingHandler",
"formatter",
"get_logfire_handler",
]

formatter: Formatter = JsonFormatter()


def _json_payload(record: logging.LogRecord) -> str:
payload = getattr(record, _JSON_PAYLOAD_RECORD_ATTRIBUTE, None)
if isinstance(payload, str):
return payload

payload = formatter.format(record)
setattr(record, _JSON_PAYLOAD_RECORD_ATTRIBUTE, payload)
return payload


class JsonPayloadLogfireLoggingHandler(logfire.LogfireLoggingHandler):
terminator = "\n"

def __init__(
self,
level: int | str = LOG_LEVEL,
*,
stream: TextIO | None = None,
) -> None:
super().__init__(level=level, fallback=logging.NullHandler())
self.stream = stream or sys.stdout
self.addFilter(HealthEndpointFilter())

def fill_attributes(self, record: logging.LogRecord) -> dict[str, object]:
attributes = super().fill_attributes(record)
attributes[JSON_LOG_ATTRIBUTE] = _json_payload(record)
return attributes

def emit(self, record: logging.LogRecord) -> None:
try:
payload = _json_payload(record)
super().emit(record)
self.stream.write(payload + self.terminator)
self.flush()
except Exception: # noqa: BLE001
self.handleError(record)

def flush(self) -> None:
if hasattr(self.stream, "flush"):
self.stream.flush()


def get_logfire_handler() -> Handler:
return JsonPayloadLogfireLoggingHandler(level=LOG_LEVEL)
def get_logfire_handler() -> logfire.LogfireLoggingHandler:
handler = logfire.LogfireLoggingHandler(
level=LOG_LEVEL,
fallback=logging.NullHandler(),
)
handler.addFilter(HealthEndpointFilter())
return handler
51 changes: 0 additions & 51 deletions app/logger/logger.py

This file was deleted.

9 changes: 7 additions & 2 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager

import logfire
import uvicorn
from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
Expand All @@ -26,12 +27,16 @@ async def lifespan(_app: FastAPI) -> AsyncGenerator[None]:
)
_app.openapi_schema = None
telemetry.instrument_sqlalchemy(get_engine())
logger.info(f"Starting up {app_settings.app_name} on port {app_settings.port}")
logfire.info(
"Starting up {service_name} on port {port}",
service_name=app_settings.app_name,
port=app_settings.port,
)
Comment on lines +30 to +34

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve logs when Logfire is not configured

In the default Compose/container path no LOGFIRE_TOKEN is supplied, and configure_otel() sets send_to_logfire="if-token-present" with console=False. After replacing the stdout-backed logger.info/logger.exception path with direct logfire.* calls, lifecycle and unhandled-exception diagnostics are silently dropped unless a Logfire token is configured, which makes default local/container failures much harder to diagnose. Please keep a stdout/stderr fallback or only route these messages exclusively through Logfire when an exporter is actually configured.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enabled Logfire console output without a token so local and container diagnostics remain visible.

try:
yield
finally:
await get_engine().dispose()
logger.info("Application shutdown")
logfire.info("Application shutdown")


app = OIDCOpenAPIFastAPI(
Expand Down
2 changes: 1 addition & 1 deletion app/telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def configure_otel() -> None:
logfire.configure(
service_name=app_settings.app_name,
send_to_logfire="if-token-present",
console=False,
console=logfire.ConsoleOptions(colors="never", show_project_link=False),
)
_configured = True

Expand Down
Loading
Loading