refactor(core): rip telemetry wrappers, use logfire directly (#754)

Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Hernandez
2026-04-17 13:58:19 -05:00
committed by GitHub
parent c4cf0aff1e
commit 1b39062ecd
47 changed files with 1302 additions and 1930 deletions
+3 -3
View File
@@ -25,7 +25,7 @@ from basic_memory.api.v2.routers.project_router import (
list_projects,
synchronize_projects,
)
from basic_memory import telemetry
import logfire
from basic_memory.config import init_api_logging
from basic_memory.services.exceptions import EntityAlreadyExistsError
from basic_memory.services.initialization import initialize_app
@@ -44,7 +44,7 @@ async def lifespan(app: FastAPI): # pragma: no cover
set_container(container)
app.state.container = container
with telemetry.operation(
with logfire.span(
"api.lifecycle.startup",
entrypoint="api",
mode=container.mode.name.lower(),
@@ -69,7 +69,7 @@ async def lifespan(app: FastAPI): # pragma: no cover
yield
# Shutdown - coordinator handles clean task cancellation
with telemetry.operation(
with logfire.span(
"api.lifecycle.shutdown",
entrypoint="api",
mode=container.mode.name.lower(),
@@ -13,7 +13,7 @@ Key improvements:
from fastapi import APIRouter, HTTPException, Response, Path
from loguru import logger
from basic_memory import telemetry
import logfire
from basic_memory.deps import (
EntityServiceV2ExternalDep,
SearchServiceV2ExternalDep,
@@ -74,7 +74,7 @@ async def get_graph(
Returns a flat node/edge structure optimized for rendering with graph libraries.
Only includes resolved relations (where to_id is not null).
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.get_graph",
entrypoint="api",
domain="knowledge",
@@ -148,7 +148,7 @@ async def resolve_identifier(
"resolution_method": "permalink"
}
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.resolve_entity",
entrypoint="api",
domain="knowledge",
@@ -215,7 +215,7 @@ async def get_entity_by_id(
Raises:
HTTPException: 404 if entity not found
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.get_entity",
entrypoint="api",
domain="knowledge",
@@ -255,7 +255,7 @@ async def create_entity(
Returns:
Created entity with generated external_id (UUID) and file content
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.create_entity",
entrypoint="api",
domain="knowledge",
@@ -313,7 +313,7 @@ async def update_entity_by_id(
Returns:
Updated entity with file content
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.update_entity",
entrypoint="api",
domain="knowledge",
@@ -385,7 +385,7 @@ async def edit_entity_by_id(
Raises:
HTTPException: 404 if entity not found, 400 if edit fails
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.edit_entity",
entrypoint="api",
domain="knowledge",
@@ -454,7 +454,7 @@ async def delete_entity_by_id(
Note: Returns deleted=False if entity doesn't exist (idempotent)
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.delete_entity",
entrypoint="api",
domain="knowledge",
@@ -503,7 +503,7 @@ async def move_entity(
Returns:
Updated entity with new file path
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.move_entity",
entrypoint="api",
domain="knowledge",
@@ -583,7 +583,7 @@ async def move_directory(
Returns:
DirectoryMoveResult with counts and details of moved files
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.move_directory",
entrypoint="api",
domain="knowledge",
@@ -647,7 +647,7 @@ async def delete_directory(
Returns:
DirectoryDeleteResult with counts and details of deleted files
"""
with telemetry.operation(
with logfire.span(
"api.request.knowledge.delete_directory",
entrypoint="api",
domain="knowledge",
@@ -9,7 +9,7 @@ from typing import Annotated, Optional
from fastapi import APIRouter, Query, Path
from loguru import logger
from basic_memory import telemetry
import logfire
from basic_memory.deps import ContextServiceV2ExternalDep, EntityRepositoryV2ExternalDep
from basic_memory.schemas.base import TimeFrame, parse_timeframe
from basic_memory.schemas.memory import (
@@ -51,7 +51,7 @@ async def recent(
Returns:
GraphContext with recent activity and related entities
"""
with telemetry.operation(
with logfire.span(
"api.request.memory.recent_activity",
entrypoint="api",
domain="memory",
@@ -72,7 +72,7 @@ async def recent(
limit = page_size
offset = (page - 1) * page_size
with telemetry.scope(
with logfire.span(
"api.memory.recent_activity.build_context",
domain="memory",
action="recent_activity",
@@ -88,7 +88,7 @@ async def recent(
offset=offset,
max_related=max_related,
)
with telemetry.scope(
with logfire.span(
"api.memory.recent_activity.shape_response",
domain="memory",
action="recent_activity",
@@ -137,7 +137,7 @@ async def get_memory_context(
Returns:
GraphContext with the entity and its related context
"""
with telemetry.operation(
with logfire.span(
"api.request.memory.build_context",
entrypoint="api",
domain="memory",
@@ -154,7 +154,7 @@ async def get_memory_context(
limit = page_size
offset = (page - 1) * page_size
with telemetry.scope(
with logfire.span(
"api.memory.build_context.build_context",
domain="memory",
action="build_context",
@@ -170,7 +170,7 @@ async def get_memory_context(
offset=offset,
max_related=max_related,
)
with telemetry.scope(
with logfire.span(
"api.memory.build_context.shape_response",
domain="memory",
action="build_context",
@@ -15,7 +15,7 @@ from pathlib import Path as PathLib
from fastapi import APIRouter, HTTPException, Response, Path
from loguru import logger
from basic_memory import telemetry
import logfire
from basic_memory.deps import (
ProjectConfigV2ExternalDep,
FileServiceV2ExternalDep,
@@ -56,7 +56,7 @@ async def get_resource_content(
Raises:
HTTPException: 404 if entity or file not found
"""
with telemetry.operation(
with logfire.span(
"api.request.resource.get_content",
entrypoint="api",
domain="resource",
@@ -64,7 +64,7 @@ async def get_resource_content(
):
logger.debug(f"V2 Getting content for project {project_id}, entity_id: {entity_id}")
with telemetry.scope(
with logfire.span(
"api.resource.get_content.load_entity",
domain="resource",
action="get_content",
@@ -74,7 +74,7 @@ async def get_resource_content(
if not entity:
raise HTTPException(status_code=404, detail=f"Entity {entity_id} not found")
with telemetry.scope(
with logfire.span(
"api.resource.get_content.validate_path",
domain="resource",
action="get_content",
@@ -90,7 +90,7 @@ async def get_resource_content(
detail="Entity contains invalid file path",
)
with telemetry.scope(
with logfire.span(
"api.resource.get_content.ensure_exists",
domain="resource",
action="get_content",
@@ -102,7 +102,7 @@ async def get_resource_content(
detail=f"File not found: {entity.file_path}",
)
with telemetry.scope(
with logfire.span(
"api.resource.get_content.read_content",
domain="resource",
action="get_content",
@@ -139,7 +139,7 @@ async def create_resource(
Raises:
HTTPException: 400 for invalid file paths, 409 if file already exists
"""
with telemetry.operation(
with logfire.span(
"api.request.resource.create",
entrypoint="api",
domain="resource",
@@ -166,7 +166,7 @@ async def create_resource(
f"Use PUT /resource/{existing_entity.external_id} to update it.",
)
with telemetry.scope(
with logfire.span(
"api.resource.create.write_file",
domain="resource",
action="create",
@@ -175,7 +175,7 @@ async def create_resource(
await file_service.ensure_directory(PathLib(data.file_path).parent)
checksum = await file_service.write_file(data.file_path, data.content)
with telemetry.scope(
with logfire.span(
"api.resource.create.read_metadata",
domain="resource",
action="create",
@@ -197,7 +197,7 @@ async def create_resource(
created_at=file_metadata.created_at,
updated_at=file_metadata.modified_at,
)
with telemetry.scope(
with logfire.span(
"api.resource.create.upsert_entity",
domain="resource",
action="create",
@@ -205,7 +205,7 @@ async def create_resource(
):
entity = await entity_repository.add(entity)
with telemetry.scope(
with logfire.span(
"api.resource.create.search_index",
domain="resource",
action="create",
@@ -258,7 +258,7 @@ async def update_resource(
Raises:
HTTPException: 404 if entity not found, 400 for invalid paths
"""
with telemetry.operation(
with logfire.span(
"api.request.resource.update",
entrypoint="api",
domain="resource",
@@ -282,7 +282,7 @@ async def update_resource(
"Path must be relative and stay within project boundaries.",
)
with telemetry.scope(
with logfire.span(
"api.resource.update.write_file",
domain="resource",
action="update",
@@ -297,7 +297,7 @@ async def update_resource(
checksum = await file_service.write_file(target_file_path, data.content)
with telemetry.scope(
with logfire.span(
"api.resource.update.read_metadata",
domain="resource",
action="update",
@@ -309,7 +309,7 @@ async def update_resource(
content_type = file_service.content_type(target_file_path)
note_type = "canvas" if target_file_path.endswith(".canvas") else "file"
with telemetry.scope(
with logfire.span(
"api.resource.update.update_entity",
domain="resource",
action="update",
@@ -329,7 +329,7 @@ async def update_resource(
if updated_entity is None:
raise HTTPException(status_code=404, detail=f"Entity {entity_id} not found")
with telemetry.scope(
with logfire.span(
"api.resource.update.search_index",
domain="resource",
action="update",
@@ -6,7 +6,7 @@ V1 uses string-based project names which are less efficient and less stable.
from fastapi import APIRouter, HTTPException, Path
from basic_memory import telemetry
import logfire
from basic_memory.api.v2.utils import to_search_results
from basic_memory.repository.semantic_errors import (
SemanticDependenciesMissingError,
@@ -48,7 +48,7 @@ async def search(
Returns:
SearchResponse with paginated search results
"""
with telemetry.operation(
with logfire.span(
"api.request.search",
entrypoint="api",
domain="search",
@@ -67,7 +67,7 @@ async def search(
offset = (page - 1) * page_size
fetch_limit = page_size + 1
try:
with telemetry.scope(
with logfire.span(
"api.search.search.execute_query",
domain="search",
action="search",
@@ -83,7 +83,7 @@ async def search(
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
with telemetry.scope(
with logfire.span(
"api.search.search.paginate_results",
domain="search",
action="search",
@@ -94,7 +94,7 @@ async def search(
if has_more:
results = results[:page_size]
with telemetry.scope(
with logfire.span(
"api.search.search.hydrate_results",
domain="search",
action="search",
@@ -102,7 +102,7 @@ async def search(
result_count=len(results),
):
search_results = await to_search_results(entity_service, results)
with telemetry.scope(
with logfire.span(
"api.search.search.build_response",
domain="search",
action="search",
+7 -7
View File
@@ -1,6 +1,6 @@
from typing import Any, Protocol, Optional, List, Sequence
from basic_memory import telemetry
import logfire
from basic_memory.repository.search_repository import SearchIndexRow
from basic_memory.schemas.memory import (
EntitySummary,
@@ -43,7 +43,7 @@ async def to_graph_context(
page: Optional[int] = None,
page_size: Optional[int] = None,
) -> GraphContext:
with telemetry.scope(
with logfire.span(
"memory.hydrate_context",
domain="memory",
action="build_context",
@@ -80,7 +80,7 @@ async def to_graph_context(
entity_title_lookup: dict[int, str] = {}
entity_external_id_lookup: dict[int, str] = {}
if entity_ids_needed:
with telemetry.scope(
with logfire.span(
"memory.hydrate_context.lookup_entities",
domain="memory",
action="build_context",
@@ -148,7 +148,7 @@ async def to_graph_context(
created_at=item.created_at,
)
with telemetry.scope(
with logfire.span(
"memory.hydrate_context.shape_results",
domain="memory",
action="build_context",
@@ -198,7 +198,7 @@ async def to_graph_context(
async def to_search_results(
entity_service: EntityServiceBatchLookup, results: List[SearchIndexRow]
) -> list[SearchResult]:
with telemetry.scope(
with logfire.span(
"search.hydrate_results",
domain="search",
action="search",
@@ -215,7 +215,7 @@ async def to_search_results(
# Single batch fetch for all entities
entities_by_id: dict[int, Any] = {}
with telemetry.scope(
with logfire.span(
"search.hydrate_results.fetch_entities",
domain="search",
action="search",
@@ -227,7 +227,7 @@ async def to_search_results(
entities_by_id = {e.id: e for e in entities}
search_results = []
with telemetry.scope(
with logfire.span(
"search.hydrate_results.shape_results",
domain="search",
action="search",