Skip to content

Commit

Permalink
Merge pull request #289 from lsst/tickets/DM-47796
Browse files Browse the repository at this point in the history
DM-47796: Add Slack error reporting to fastapi_safir_app
  • Loading branch information
rra authored Nov 25, 2024
2 parents b67f88a + 5a627af commit 23706a1
Show file tree
Hide file tree
Showing 20 changed files with 88 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from pydantic import Field
from pydantic import Field, SecretStr
from pydantic_settings import SettingsConfigDict
from safir.logging import LogLevel, Profile
from safir.uws import UWSApplication, UWSAppSettings, UWSConfig, UWSRoute
Expand All @@ -16,8 +16,16 @@
class Config(UWSAppSettings):
"""Configuration for example-uws."""

model_config = SettingsConfigDict(
env_prefix="EXAMPLE_UWS_", case_sensitive=False
)

name: str = Field("example-uws", title="Name of application")

log_level: LogLevel = Field(
LogLevel.INFO, title="Log level of the application's logger"
)

path_prefix: str = Field(
"/example-uws", title="URL prefix for application"
)
Expand All @@ -26,12 +34,10 @@ class Config(UWSAppSettings):
Profile.development, title="Application logging profile"
)

log_level: LogLevel = Field(
LogLevel.INFO, title="Log level of the application's logger"
)

model_config = SettingsConfigDict(
env_prefix="EXAMPLE_UWS_", case_sensitive=False
slack_webhook: SecretStr | None = Field(
None,
title="Slack webhook for alerts",
description="If set, alerts will be posted to this Slack webhook",
)

@property
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@

"""Job parameter dependencies."""

from typing import Annotated

from fastapi import Depends, Request
from fastapi import Depends
from safir.uws import UWSJobParameter, uws_post_params_dependency

__all__ = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

"""Domain models for example-uws."""

from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
from fastapi import APIRouter, Depends
from safir.dependencies.logger import logger_dependency
from safir.metadata import get_metadata
from safir.slack.webhook import SlackRouteErrorHandler
from structlog.stdlib import BoundLogger

from ..config import config
from ..models import Index

__all__ = ["external_router"]

external_router = APIRouter()
external_router = APIRouter(route_class=SlackRouteErrorHandler)
"""FastAPI router for all external handlers."""


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

from fastapi import APIRouter
from safir.metadata import Metadata, get_metadata
from safir.slack.webhook import SlackRouteErrorHandler

from ..config import config

__all__ = ["internal_router"]

internal_router = APIRouter()
internal_router = APIRouter(route_class=SlackRouteErrorHandler)
"""FastAPI router for all internal handlers."""


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
from contextlib import asynccontextmanager
from importlib.metadata import metadata, version

import structlog
from fastapi import FastAPI
from safir.dependencies.http_client import http_client_dependency
from safir.logging import configure_logging, configure_uvicorn_logging
from safir.middleware.x_forwarded import XForwardedMiddleware
from safir.slack.webhook import SlackRouteErrorHandler

from .config import config, uws
from .handlers.external import external_router
Expand Down Expand Up @@ -65,3 +67,11 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:

# Install error handlers.
uws.install_error_handlers(app)

# Configure Slack alerts.
if config.slack_webhook:
logger = structlog.get_logger("exampleuws")
SlackRouteErrorHandler.initialize(
config.slack_webhook, "example-uws", logger
)
logger.debug("Initialized Slack webhook")
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

"""Worker for UWS database updates."""

from __future__ import annotations
Expand Down
20 changes: 13 additions & 7 deletions project_templates/fastapi_safir_app/example/src/example/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from pydantic import Field
from pydantic import Field, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
from safir.logging import LogLevel, Profile

Expand All @@ -12,8 +12,16 @@
class Config(BaseSettings):
"""Configuration for example."""

model_config = SettingsConfigDict(
env_prefix="EXAMPLE_", case_sensitive=False
)

name: str = Field("example", title="Name of application")

log_level: LogLevel = Field(
LogLevel.INFO, title="Log level of the application's logger"
)

path_prefix: str = Field(
"/example", title="URL prefix for application"
)
Expand All @@ -22,12 +30,10 @@ class Config(BaseSettings):
Profile.development, title="Application logging profile"
)

log_level: LogLevel = Field(
LogLevel.INFO, title="Log level of the application's logger"
)

model_config = SettingsConfigDict(
env_prefix="EXAMPLE_", case_sensitive=False
slack_webhook: SecretStr | None = Field(
None,
title="Slack webhook for alerts",
description="If set, alerts will be posted to this Slack webhook",
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
from fastapi import APIRouter, Depends
from safir.dependencies.logger import logger_dependency
from safir.metadata import get_metadata
from safir.slack.webhook import SlackRouteErrorHandler
from structlog.stdlib import BoundLogger

from ..config import config
from ..models import Index

__all__ = ["external_router"]

external_router = APIRouter()
external_router = APIRouter(route_class=SlackRouteErrorHandler)
"""FastAPI router for all external handlers."""


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

from fastapi import APIRouter
from safir.metadata import Metadata, get_metadata
from safir.slack.webhook import SlackRouteErrorHandler

from ..config import config

__all__ = ["internal_router"]

internal_router = APIRouter()
internal_router = APIRouter(route_class=SlackRouteErrorHandler)
"""FastAPI router for all internal handlers."""


Expand Down
10 changes: 10 additions & 0 deletions project_templates/fastapi_safir_app/example/src/example/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
from contextlib import asynccontextmanager
from importlib.metadata import metadata, version

import structlog
from fastapi import FastAPI
from safir.dependencies.http_client import http_client_dependency
from safir.logging import configure_logging, configure_uvicorn_logging
from safir.middleware.x_forwarded import XForwardedMiddleware
from safir.slack.webhook import SlackRouteErrorHandler

from .config import config
from .handlers.external import external_router
Expand Down Expand Up @@ -58,3 +60,11 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:

# Add middleware.
app.add_middleware(XForwardedMiddleware)

# Configure Slack alerts.
if config.slack_webhook:
logger = structlog.get_logger("example")
SlackRouteErrorHandler.initialize(
config.slack_webhook, "example", logger
)
logger.debug("Initialized Slack webhook")
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from pydantic import Field
from pydantic import Field, SecretStr
from pydantic_settings import {% if cookiecutter.flavor != "UWS" %}BaseSettings, {% endif %}SettingsConfigDict
from safir.logging import LogLevel, Profile
{%- if cookiecutter.flavor == "UWS" %}
Expand All @@ -18,8 +18,16 @@
class Config({% if cookiecutter.flavor == "UWS" %}UWSAppSettings{% else %}BaseSettings{% endif %}):
"""Configuration for {{ cookiecutter.name }}."""

model_config = SettingsConfigDict(
env_prefix="{{ cookiecutter.name | upper | replace('-', '_') }}_", case_sensitive=False
)

name: str = Field("{{ cookiecutter.name }}", title="Name of application")

log_level: LogLevel = Field(
LogLevel.INFO, title="Log level of the application's logger"
)

path_prefix: str = Field(
"/{{ cookiecutter.name | lower }}", title="URL prefix for application"
)
Expand All @@ -28,12 +36,10 @@ class Config({% if cookiecutter.flavor == "UWS" %}UWSAppSettings{% else %}BaseSe
Profile.development, title="Application logging profile"
)

log_level: LogLevel = Field(
LogLevel.INFO, title="Log level of the application's logger"
)

model_config = SettingsConfigDict(
env_prefix="{{ cookiecutter.name | upper | replace('-', '_') }}_", case_sensitive=False
slack_webhook: SecretStr | None = Field(
None,
title="Slack webhook for alerts",
description="If set, alerts will be posted to this Slack webhook",
)
{%- if cookiecutter.flavor == "UWS" %}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{%- if cookiecutter.flavor == "UWS" %}
{%- if cookiecutter.flavor == "UWS" -%}
"""Job parameter dependencies."""

from typing import Annotated

from fastapi import Depends, Request
from fastapi import Depends
from safir.uws import UWSJobParameter, uws_post_params_dependency

__all__ = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{%- if cookiecutter.flavor == "UWS" %}
{%- if cookiecutter.flavor == "UWS" -%}
"""Domain models for {{ cookiecutter.name }}."""

from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
from fastapi import APIRouter, Depends
from safir.dependencies.logger import logger_dependency
from safir.metadata import get_metadata
from safir.slack.webhook import SlackRouteErrorHandler
from structlog.stdlib import BoundLogger

from ..config import config
from ..models import Index

__all__ = ["external_router"]

external_router = APIRouter()
external_router = APIRouter(route_class=SlackRouteErrorHandler)
"""FastAPI router for all external handlers."""


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

from fastapi import APIRouter
from safir.metadata import Metadata, get_metadata
from safir.slack.webhook import SlackRouteErrorHandler

from ..config import config

__all__ = ["internal_router"]

internal_router = APIRouter()
internal_router = APIRouter(route_class=SlackRouteErrorHandler)
"""FastAPI router for all internal handlers."""


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
from contextlib import asynccontextmanager
from importlib.metadata import metadata, version

import structlog
from fastapi import FastAPI
from safir.dependencies.http_client import http_client_dependency
from safir.logging import configure_logging, configure_uvicorn_logging
from safir.middleware.x_forwarded import XForwardedMiddleware
from safir.slack.webhook import SlackRouteErrorHandler

from .config import config{% if cookiecutter.flavor == "UWS" %}, uws{% endif %}
from .handlers.external import external_router
Expand Down Expand Up @@ -73,3 +75,11 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
# Install error handlers.
uws.install_error_handlers(app)
{%- endif %}

# Configure Slack alerts.
if config.slack_webhook:
logger = structlog.get_logger("{{ cookiecutter.module_name }}")
SlackRouteErrorHandler.initialize(
config.slack_webhook, "{{ cookiecutter.name }}", logger
)
logger.debug("Initialized Slack webhook")
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{%- if cookiecutter.flavor == "UWS" %}
{%- if cookiecutter.flavor == "UWS" -%}
"""Worker for UWS database updates."""

from __future__ import annotations
Expand Down
2 changes: 1 addition & 1 deletion project_templates/technote_md/testn-000/technote.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ series_id = "TESTN"
canonical_url = "https://testn-000.lsst.io"
github_url = "https://github.com/lsst/testn-000"
github_default_branch = "main"
date_created = 2024-11-25T21:31:10Z
date_created = 2024-11-25T22:09:21Z
organization.name = "Vera C. Rubin Observatory"
organization.ror = "https://ror.org/048g3cy84"
license.id = "CC-BY-4.0"
Expand Down
2 changes: 1 addition & 1 deletion project_templates/technote_rst/testn-000/technote.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ series_id = "TESTN"
canonical_url = "https://testn-000.lsst.io"
github_url = "https://github.com/lsst/testn-000"
github_default_branch = "main"
date_created = 2024-11-25T21:31:10Z
date_created = 2024-11-25T22:09:21Z
organization.name = "Vera C. Rubin Observatory"
organization.ror = "https://ror.org/048g3cy84"
license.id = "CC-BY-4.0"
Expand Down

0 comments on commit 23706a1

Please sign in to comment.