Skip to content

Commit 02b42c6

Browse files
phernandezclaude
andcommitted
refactor: address PR review feedback
- Remove `# pragma: no cover` from init_cli/mcp/api_logging since they are fully tested (issue 1) - Move logfire to optional dependency `pip install basic-memory[telemetry]` while keeping it in dev deps for testing (issue 6) - Collapse `telemetry.operation` into an alias for `telemetry.scope` with a comment explaining the convention (issue 4) - Rename `routing.resolve_client` span to `routing.client_session` to accurately reflect that it covers the full tool execution lifetime, not just routing resolution (issue 5) - Update PR description to clarify two-step setup: logfire_enabled config flag + optional LOGFIRE_TOKEN (issue 2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
1 parent fe32ac5 commit 02b42c6

8 files changed

Lines changed: 28 additions & 20 deletions

File tree

docs/logfire-instrumentation-strategy.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ Child spans should represent real phases whose duration we care about.
187187

188188
Examples:
189189

190-
- `routing.resolve_client`
190+
- `routing.client_session`
191191
- `routing.resolve_project`
192192
- `routing.resolve_workspace`
193193
- `api.search.execute`

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ dependencies = [
1818
"mcp>=1.23.1",
1919
"pydantic-settings>=2.6.1",
2020
"loguru>=0.7.3",
21-
"logfire>=4.19.0",
2221
"pyright>=1.1.390",
2322
"markdown-it-py>=3.0.0",
2423
"python-frontmatter>=1.1.0",
@@ -59,6 +58,9 @@ Documentation = "https://github.com/basicmachines-co/basic-memory#readme"
5958
basic-memory = "basic_memory.cli.main:app"
6059
bm = "basic_memory.cli.main:app"
6160

61+
[project.optional-dependencies]
62+
telemetry = ["logfire>=4.19.0"]
63+
6264
[build-system]
6365
requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"]
6466
build-backend = "hatchling.build"
@@ -84,6 +86,7 @@ target-version = "py312"
8486

8587
[dependency-groups]
8688
dev = [
89+
"logfire>=4.19.0",
8790
"gevent>=24.11.1",
8891
"icecream>=2.1.3",
8992
"pytest>=8.3.4",

src/basic_memory/config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@ def _configure_logfire_for_entrypoint(entrypoint: str) -> None:
989989
)
990990

991991

992-
def init_cli_logging() -> None: # pragma: no cover
992+
def init_cli_logging() -> None:
993993
"""Initialize logging for CLI commands - file only.
994994
995995
CLI commands should not log to stdout to avoid interfering with
@@ -1000,7 +1000,7 @@ def init_cli_logging() -> None: # pragma: no cover
10001000
setup_logging(log_level=log_level, log_to_file=True)
10011001

10021002

1003-
def init_mcp_logging() -> None: # pragma: no cover
1003+
def init_mcp_logging() -> None:
10041004
"""Initialize logging for MCP server - file only.
10051005
10061006
MCP server must not log to stdout as it would corrupt the
@@ -1011,7 +1011,7 @@ def init_mcp_logging() -> None: # pragma: no cover
10111011
setup_logging(log_level=log_level, log_to_file=True)
10121012

10131013

1014-
def init_api_logging() -> None: # pragma: no cover
1014+
def init_api_logging() -> None:
10151015
"""Initialize logging for API server.
10161016
10171017
Cloud mode (BASIC_MEMORY_CLOUD_MODE=1): stdout with structured context

src/basic_memory/mcp/project_context.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ async def get_project_client(
550550
if is_factory_mode():
551551
route_mode = "factory"
552552
with telemetry.scope(
553-
"routing.resolve_client",
553+
"routing.client_session",
554554
project_name=resolved_project,
555555
route_mode=route_mode,
556556
workspace_id=workspace,
@@ -568,7 +568,7 @@ async def get_project_client(
568568
if _explicit_routing() and _force_local_mode():
569569
route_mode = "explicit_local"
570570
with telemetry.scope(
571-
"routing.resolve_client",
571+
"routing.client_session",
572572
project_name=resolved_project,
573573
route_mode=route_mode,
574574
):
@@ -611,7 +611,7 @@ async def get_project_client(
611611
if effective_workspace is not None:
612612
# Config-resolved workspace — pass directly to get_client, skip network lookup
613613
with telemetry.scope(
614-
"routing.resolve_client",
614+
"routing.client_session",
615615
project_name=resolved_project,
616616
route_mode=route_mode,
617617
workspace_id=effective_workspace,
@@ -627,7 +627,7 @@ async def get_project_client(
627627
# No config-based workspace — use resolve_workspace_parameter for discovery
628628
active_ws = await resolve_workspace_parameter(workspace=None, context=context)
629629
with telemetry.scope(
630-
"routing.resolve_client",
630+
"routing.client_session",
631631
project_name=resolved_project,
632632
route_mode=route_mode,
633633
workspace_id=active_ws.tenant_id,
@@ -644,7 +644,7 @@ async def get_project_client(
644644
# Step 4: Local routing (default)
645645
route_mode = "local_asgi"
646646
with telemetry.scope(
647-
"routing.resolve_client",
647+
"routing.client_session",
648648
project_name=resolved_project,
649649
route_mode=route_mode,
650650
):

src/basic_memory/telemetry.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,9 @@ def scope(name: str, **attrs: Any) -> Iterator[None]:
174174
yield
175175

176176

177-
@contextmanager
178-
def operation(name: str, **attrs: Any) -> Iterator[None]:
179-
"""Create a root operation span and mirror its metadata into Loguru context."""
180-
with scope(name, **attrs):
181-
yield
177+
# Alias: `operation` signals a root-level boundary (entrypoint, tool invocation),
178+
# while `scope` signals a nested phase. The distinction is convention only.
179+
operation = scope
182180

183181

184182
@contextmanager

tests/mcp/test_project_context_telemetry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,6 @@ async def test_get_project_client_contextualizes_route_mode(config_manager, monk
8989

9090
span_names = [name for name, _ in spans]
9191
assert "routing.resolve_project" in span_names
92-
assert "routing.resolve_client" in span_names
92+
assert "routing.client_session" in span_names
9393
assert "routing.validate_project" in span_names
9494
assert {"project_name": "main", "route_mode": "local_asgi"} in contexts

tests/test_telemetry.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def test_scope_creates_span_and_nested_log_context(monkeypatch) -> None:
204204
try:
205205
with telemetry.contextualize(project_name="main"):
206206
with telemetry.scope(
207-
"routing.resolve_client",
207+
"routing.client_session",
208208
route_mode="local_asgi",
209209
workspace_id=None,
210210
):
@@ -213,7 +213,7 @@ def test_scope_creates_span_and_nested_log_context(monkeypatch) -> None:
213213
logger.remove(sink_id)
214214

215215
assert fake_logfire.span_calls == [
216-
("routing.resolve_client", {"route_mode": "local_asgi"})
216+
("routing.client_session", {"route_mode": "local_asgi"})
217217
]
218218
assert records == [{"project_name": "main", "route_mode": "local_asgi"}]
219219

uv.lock

Lines changed: 9 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)