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
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ ENVIRONMENT=dev
LOG_LEVEL=INFO
OLTP_STD_LOGGING_ENABLED=True
OLTP_LOG_METHOD=none
OTLP_ENDPOINT=http://192.168.2.21:4317
PROJECT_NAME="FastAPI Template"
RELOAD=False
SECRET_KEY=CHANGE_ME_IN_PRODUCTION
Expand All @@ -22,3 +21,8 @@ ML_WORKER_COUNT=3
# OTEL_METRIC_EXPORT_INTERVAL=5000 # So we don't have to wait 60s for metrics

WATCHFILES_IGNORE_PERMISSION_DENIED=1

## LANGFUSE (Optional)
LANGFUSE_BASE_URL=FILL_IT
LANGFUSE_PUBLIC_KEY=FILL_IT
LANGFUSE_SECRET_KEY=FILL_IT
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ logfire projects use api-template

Set in `.env`: `OLTP_LOG_METHOD=logfire`

## Langfuse

Set in `.env`: `OLTP_LOG_METHOD=langfuse`

Also set the following in `.env`:

```bash
LANGFUSE_BASE_URL=FILL_IT
LANGFUSE_PUBLIC_KEY=FILL_IT
LANGFUSE_SECRET_KEY=FILL_IT
```

## Hatchet

Background workflows are powered by [Hatchet](https://hatchet.run).
Expand Down
9 changes: 8 additions & 1 deletion api-shared/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ requires-python = ">=3.11"
dependencies = [
"hatchet-sdk[otel]>=1.24",
"httpx>=0.28.1",
"logfire[httpx,system-metrics]>=3.12.0",
"loguru>=0.7.3",
"opentelemetry-distro[otlp]>=0.52b0",
"opentelemetry-instrumentation-logging>=0.52b0",
"pydantic-settings>=2.8.1",
]

[project.optional-dependencies]
logfire = [
"logfire[httpx,system-metrics]>=3.12.0",
]
langfuse = [
"langfuse>=3.2.4",
]

[dependency-groups]
test = [
"pytest>=8.3.3",
Expand Down
3 changes: 2 additions & 1 deletion api-shared/src/api_shared/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class OLTPLogMethod(StrEnum):
NONE = "none"
MANUAL = "manual"
LOGFIRE = "logfire"
LANGFUSE = "langfuse"


class SharedBaseSettings(BaseSettings):
Expand All @@ -36,7 +37,7 @@ class SharedBaseSettings(BaseSettings):
)
OLTP_LOG_METHOD: OLTPLogMethod = Field(
default=OLTPLogMethod.NONE,
description="OpenTelemetry logging method (none, manual, logfire).",
description="OpenTelemetry logging method (none, manual, logfire, langfuse).",
)
OTLP_ENDPOINT: CustomHttpUrlStr | None = Field(
default=None,
Expand Down
35 changes: 33 additions & 2 deletions api-shared/src/api_shared/core/telemetry.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logfire
from importlib import import_module

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.logging import LoggingInstrumentor
Expand All @@ -12,6 +13,27 @@
from opentelemetry.sdk.trace.export import BatchSpanProcessor

from api_shared.core.settings import OLTPLogMethod
from api_shared.utils.general import is_module_installed


def _get_logfire_or_raise():
if not is_module_installed("logfire"): # pragma: no cover
raise RuntimeError(
"OLTP_LOG_METHOD=logfire requires optional dependency 'logfire'. "
"Install dependency with `api-shared[logfire]`."
)

return import_module("logfire")


def _configure_langfuse_or_raise() -> None:
if not is_module_installed("langfuse"): # pragma: no cover
raise RuntimeError(
"OLTP_LOG_METHOD=langfuse requires optional dependency 'langfuse'. "
"Install extras with `uv sync --extra langfuse`."
)

import_module("langfuse").Langfuse()


def setup_opentelemetry_worker(settings):
Expand All @@ -20,6 +42,7 @@ def setup_opentelemetry_worker(settings):
return

if settings.OLTP_LOG_METHOD == OLTPLogMethod.LOGFIRE:
logfire = _get_logfire_or_raise()
logfire.configure(environment=settings.ENVIRONMENT.value)
logfire.instrument_system_metrics()
logfire.instrument_httpx()
Expand All @@ -30,6 +53,14 @@ def setup_opentelemetry_worker(settings):

return

if settings.OLTP_LOG_METHOD == OLTPLogMethod.LANGFUSE:
_configure_langfuse_or_raise()
if settings.OLTP_STD_LOGGING_ENABLED is True:
LoggingInstrumentor().instrument(
tracer_provider=trace.get_tracer_provider()
)
return

resource = Resource(
attributes={
SERVICE_NAME: getattr(settings, "PROJECT_NAME", "api-template-worker"),
Expand All @@ -40,7 +71,7 @@ def setup_opentelemetry_worker(settings):
trace_provider = TracerProvider(resource=resource)
otlp_exporter = OTLPSpanExporter(endpoint=settings.OTLP_ENDPOINT, insecure=True)
trace_provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
if getattr(settings, "OLTP_STD_LOGGING_ENABLED", False):
if settings.OLTP_STD_LOGGING_ENABLED is True:
LoggingInstrumentor().instrument(tracer_provider=trace_provider)
trace.set_tracer_provider(trace_provider)

Expand Down
35 changes: 35 additions & 0 deletions api-shared/src/api_shared/utils/general.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""General utility functions."""

import importlib


def is_module_installed(module_name: str, throw_error: bool = False) -> bool:
"""Check if the module is installed or not.

Examples:
>>> is_module_installed(module_name="yaml", throw_error=False)
True
>>> is_module_installed(module_name="numpy", throw_error=False)
False
>>> is_module_installed(module_name="numpy", throw_error=True)
Traceback (most recent call last):
ImportError: Module numpy is not installed.

Args:
module_name: Name of the module to be checked.
throw_error: If True, raises ImportError if module is not installed.

Returns:
Returns True if module is installed, False otherwise.

Raises:
ImportError: If throw_error is True and module is not installed.
"""
try:
importlib.import_module(module_name)
return True
except ImportError as e:
if throw_error:
message = f"Module {module_name} is not installed."
raise ImportError(message) from e
return False
2 changes: 1 addition & 1 deletion api-workers-general/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ classifiers = [
]

dependencies = [
"api-shared",
"api-shared[langfuse,logfire]",
"pandas>=3.0.0",
]

Expand Down
Loading