Skip to content

Isolate telemetry to garth's session using hooks#182

Merged
matin merged 1 commit intomainfrom
issue-181-isolated-telemetry
Jan 13, 2026
Merged

Isolate telemetry to garth's session using hooks#182
matin merged 1 commit intomainfrom
issue-181-isolated-telemetry

Conversation

@matin
Copy link
Copy Markdown
Owner

@matin matin commented Jan 13, 2026

Summary

  • Replace global logfire.instrument_requests() with session-level hooks
  • Only garth's requests are logged (no more global monkey-patching)
  • Add telemetry_callback parameter for custom telemetry destinations
  • Logfire cloud remains the default destination with same env vars

Changes

  • telemetry.py: Rewritten to use session.hooks['response'] instead of global instrumentation
  • http.py: Added telemetry_callback parameter, call telemetry.attach(session)
  • docs/configuration.md: Updated with custom callback examples and isolation notes

Why

The previous implementation had two issues:

  1. logfire.configure() set up the global OpenTelemetry provider, potentially overwriting user's config
  2. logfire.instrument_requests() patched ALL requests globally, not just garth's

Now telemetry is completely isolated to garth's session.

Fixes #181

Test plan

  • All 159 tests pass
  • New tests for callback functionality
  • New tests for session attachment
  • VCR test verifies telemetry captures real requests

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for a custom telemetry callback and automatic attachment of telemetry to HTTP sessions.
    • Child sessions now inherit response hooks for consistent post-response processing.
  • Documentation

    • Moved telemetry details into a dedicated Telemetry guide and added examples for callback and integration.
    • Clarified telemetry defaults and integration behavior.
  • Tests

    • Expanded tests covering telemetry callbacks, session attachment, and sanitization.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 13, 2026

Warning

Rate limit exceeded

@matin has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 15 minutes and 12 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 8993362 and 57a7a98.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • docs/configuration.md
  • docs/telemetry.md
  • mkdocs.yml
  • pyproject.toml
  • src/garth/http.py
  • src/garth/sso.py
  • src/garth/telemetry.py
  • tests/test_telemetry.py

Walkthrough

Replaces global logfire instrumentation with session-level telemetry hooks and a pluggable callback API; Telemetry now attaches a response hook to garth's requests.Session, routes sanitized telemetry data via a default logfire sender or a user-provided callback, and adds docs and tests for the new flow.

Changes

Cohort / File(s) Summary
Telemetry core
src/garth/telemetry.py
Reworked Telemetry to store an optional callback, add _default_callback, _configure_logfire, _response_hook, _attached_sessions, and attach(session). configure(...) now accepts callback and conditionally initializes logfire. Scrubbing and dispatch route moved to callback path.
HTTP client wiring
src/garth/http.py
Client.configure gains `telemetry_callback: Callable[[dict], None]
SSO session hooks propagation
src/garth/sso.py
New line copying parent session response hooks into the created GarminOAuth1Session so child session inherits response hooks.
Tests
tests/test_telemetry.py
Updated/added tests to exercise callback-based telemetry, session attach() idempotency, captured/sanitized payloads, conditional logfire configuration, and telemetry enable/disable paths.
Documentation
docs/telemetry.md, mkdocs.yml, docs/configuration.md
Added telemetry.md and nav entry; removed the Telemetry section from docs/configuration.md; telemetry.md documents enabling, env vars, default logfire behavior, and custom callback examples.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Garth Client
    participant Telemetry
    participant Session as requests.Session
    participant Hook as Response Hook
    participant Callback as Callback (default or custom)
    participant Logfire

    Client->>Telemetry: configure(telemetry_enabled, callback?)
    Telemetry->>Telemetry: store callback or set default
    alt no custom callback
        Telemetry->>Telemetry: _configure_logfire() once (if needed)
    end
    Client->>Telemetry: attach(session)
    Telemetry->>Session: append _response_hook to hooks['response']

    Note over Session: Later — an HTTP request/response occurs
    Session->>Hook: invoke response hook
    Hook->>Telemetry: _response_hook(response)
    Telemetry->>Telemetry: build & sanitize payload
    alt custom callback provided
        Telemetry->>Callback: callback(sanitized_payload)
    else default callback
        Telemetry->>Callback: _default_callback(sanitized_payload)
        Callback->>Logfire: POST payload to Logfire ingest
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: isolating telemetry to garth's session by using hooks instead of global instrumentation, which is the core objective of the PR.
Linked Issues check ✅ Passed The PR successfully implements all key requirements: removes global logfire configuration and instrumentation, attaches telemetry via session hooks, maintains environment variable defaults, and exposes telemetry_callback API for custom destinations.
Out of Scope Changes check ✅ Passed All changes are within scope: documentation updates (configuration.md, telemetry.md, mkdocs.yml), core implementation (http.py, telemetry.py), session hook propagation (sso.py), and comprehensive test coverage align with the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-181-isolated-telemetry

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Jan 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (77f3cbc) to head (57a7a98).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main      #182    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files           68        68            
  Lines         3381      3486   +105     
==========================================
+ Hits          3381      3486   +105     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/garth/telemetry.py (1)

97-100: Consider using WeakSet to avoid potential memory leaks.

_attached_sessions stores id(session) values, which are integers. If a session is garbage collected and a new session is created with the same id(), the check session_id in self._attached_sessions could incorrectly return True, skipping hook attachment.

Consider using weakref.WeakSet to store weak references to sessions, which automatically removes entries when sessions are garbage collected.

♻️ Suggested implementation using WeakSet
 import json
 import os
 import re
+import weakref
 from collections.abc import Callable
 from dataclasses import dataclass, field
 
 from requests import Response, Session
     _configured: bool = field(default=False, repr=False)
     _logfire_configured: bool = field(default=False, repr=False)
-    _attached_sessions: set = field(default_factory=set, repr=False)
+    _attached_sessions: weakref.WeakSet = field(
+        default_factory=weakref.WeakSet, repr=False
+    )

Then update attach:

     def attach(self, session: Session):
-        session_id = id(session)
-        if session_id in self._attached_sessions:
+        if session in self._attached_sessions:
             return
 
         session.hooks["response"].append(self._response_hook)
-        self._attached_sessions.add(session_id)
+        self._attached_sessions.add(session)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 77f3cbc and 09a88d3.

📒 Files selected for processing (4)
  • docs/configuration.md
  • src/garth/http.py
  • src/garth/telemetry.py
  • tests/test_telemetry.py
🧰 Additional context used
📓 Path-based instructions (2)
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use pytest with VCR cassettes for HTTP recording/playback in tests
Mirror test directory structure to match source code structure

Files:

  • tests/test_telemetry.py
tests/**

⚙️ CodeRabbit configuration file

tests/**: - test functions shouldn't have a return type hint

  • it's ok to use assert instead of pytest.assume()

Files:

  • tests/test_telemetry.py
🧠 Learnings (1)
📚 Learning: 2026-01-09T22:41:07.977Z
Learnt from: CR
Repo: matin/garth PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T22:41:07.977Z
Learning: Applies to tests/**/*.py : Use pytest with VCR cassettes for HTTP recording/playback in tests

Applied to files:

  • tests/test_telemetry.py
🧬 Code graph analysis (2)
src/garth/http.py (1)
src/garth/telemetry.py (1)
  • attach (212-223)
src/garth/telemetry.py (2)
src/garth/http.py (2)
  • request (149-188)
  • configure (50-112)
tests/conftest.py (1)
  • session (15-16)
🪛 LanguageTool
docs/configuration.md

[grammar] ~131-~131: Ensure spelling is correct
Context: ...allback instead. ### Use with existing logfire setup If your application already conf...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~131-~131: Ensure spelling is correct
Context: ...ad. ### Use with existing logfire setup If your application already configures l...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 Ruff (0.14.10)
src/garth/telemetry.py

108-108: Unused method argument: args

(ARG002)


108-108: Unused method argument: kwargs

(ARG002)


137-138: try-except-pass detected, consider logging the exception

(S110)


137-137: Do not catch blind exception: Exception

(BLE001)

tests/test_telemetry.py

127-127: Unused lambda argument: data

(ARG005)


132-132: Possible hardcoded password assigned to argument: "token"

(S106)


138-138: Possible hardcoded password assigned to: "token"

(S105)


273-273: Unused lambda argument: data

(ARG005)


385-385: Unused lambda argument: data

(ARG005)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test 3.14
🔇 Additional comments (14)
docs/configuration.md (3)

80-82: LGTM! Clear documentation of telemetry isolation.

The updated description accurately reflects the new session-level hook approach, making it clear to users that enabling telemetry won't affect other HTTP clients in their application.


112-129: Good example for custom callback usage.

The example clearly demonstrates how to route telemetry data to a custom destination. The documented fields (method, url, status_code, request_headers, request_body, response_headers, response_body) match the implementation in telemetry.py _response_hook.


131-142: Useful guidance for existing logfire users.

This section addresses a common use case where users already have logfire configured in their application. The example correctly shows that no token is needed when relying on existing configuration.

src/garth/telemetry.py (3)

108-138: LGTM! Response hook implementation is solid.

The *args, **kwargs parameters (flagged by static analysis) are necessary for compatibility with the requests.Session hook signature. The silent exception handling aligns with the PR objective that telemetry errors should never break the application.

One minor observation: response.text on line 133 could theoretically raise an exception for certain encoding issues, but it's safely wrapped in the try/except block.


193-210: LGTM! Logfire configuration logic is correct.

The early return when no token is available (lines 198-200) correctly handles the case where users have their own logfire configuration. When _logfire_configured remains False, the default callback will still work if the user has already configured logfire externally.


212-223: LGTM! Idempotent session attachment.

The attach method correctly prevents duplicate hook registration. This is important since Client.configure() can be called multiple times.

src/garth/http.py (2)

68-68: LGTM! New callback parameter integrated correctly.

The telemetry_callback parameter is properly typed and follows the existing pattern for telemetry configuration options.


105-112: LGTM! Telemetry configuration and attachment flow is correct.

The flow correctly:

  1. Configures telemetry settings (including the optional callback)
  2. Attaches the telemetry hooks to the session

Since Telemetry.attach() is idempotent, calling it on every configure() invocation is safe and ensures the hooks are always attached when telemetry is enabled.

tests/test_telemetry.py (6)

69-95: LGTM! Well-structured VCR test with callback capture.

The test correctly uses @pytest.mark.vcr decorator as per coding guidelines, and the callback capture pattern cleanly verifies telemetry data without requiring logfire mocking.


181-207: LGTM! Comprehensive response hook test.

The test properly verifies:

  • Data capture via callback
  • Request/response metadata extraction
  • Sanitization of sensitive data (password in request, token in response)

269-286: LGTM! Exception handling test verifies resilience.

This test confirms that telemetry errors (simulated via ZeroDivisionError) don't propagate and break the application.


345-371: LGTM! Idempotency tests are thorough.

Good coverage for both single attach and multiple attach scenarios. This validates the _attached_sessions tracking mechanism works correctly.


374-409: LGTM! Tests properly verify callback/logfire interaction.

These tests confirm:

  • Custom callback skips logfire configuration (lines 374-390)
  • Missing token skips logfire configuration (lines 393-409)

Both are important edge cases for the PR's isolation objectives.


412-426: LGTM! Client integration test validates end-to-end flow.

This test confirms that Client.configure() properly passes the telemetry_callback through to the underlying Telemetry instance.

@matin matin force-pushed the issue-181-isolated-telemetry branch 4 times, most recently from 2afebdd to 8993362 Compare January 13, 2026 10:39
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/garth/telemetry.py:
- Around line 174-181: Add a unit test that sets Telemetry.enabled=True with no
LOGFIRE_TOKEN and no custom callback, ensures after init that
telemetry._configured is True and telemetry._logfire_configured is False, then
patch the global "logfire" module with a mock and call
telemetry._response_hook(dummy_request, dummy_response); assert the call does
not raise and that mock_logfire.info was not called (verifying the default
callback behaves safely when logfire remains unconfigured). Use unique symbols
Telemetry, _configure_logfire, _response_hook, _default_callback,
_logfire_configured and _configured to locate code under test.
🧹 Nitpick comments (3)
src/garth/telemetry.py (2)

87-90: Minor: Consider memory leak potential with _attached_sessions.

The _attached_sessions set stores session IDs indefinitely. If sessions are created and destroyed frequently, this set will grow unbounded. Additionally, if a session is garbage collected and a new session happens to get the same id(), the hook won't be attached to the new session.

For typical usage (single long-lived session per Client), this is unlikely to be an issue. If high-frequency session creation becomes a use case, consider using weakref.WeakSet to track sessions or clearing the set when sessions are replaced.


98-128: Consider logging telemetry exceptions for debugging.

The broad exception handler at lines 127-128 silently swallows all errors. While this is intentional to prevent telemetry from breaking the application, completely silent failures can make debugging difficult.

Consider logging at DEBUG level or to stderr when exceptions occur:

♻️ Suggested improvement
         callback = self.callback or self._default_callback
         callback(data)
-    except Exception:
-        pass  # Don't let telemetry errors break the app
+    except Exception as e:
+        import logging
+        logging.getLogger(__name__).debug(
+            "Telemetry hook error: %s", e, exc_info=True
+        )

Note: The unused *args, **kwargs parameters are required by the requests hook signature and can be safely ignored.

docs/telemetry.md (1)

55-66: Documentation is accurate; consider adding clarity about why garth skips logfire configuration.

The example correctly demonstrates using garth with a pre-configured logfire setup. When garth.configure(telemetry_enabled=True) is called without a token, _configure_logfire() returns early (line 190 of telemetry.py), allowing garth to use the existing logfire configuration that was already initialized by the user's logfire.configure() call. Recent logfire releases require explicit configure() before calling logfire.info(), and the documentation example shows the correct ordering, so telemetry will work as documented.

For improved clarity, consider adding a note explaining that garth deliberately skips its own logfire initialization in this scenario and relies on the application's pre-existing logfire configuration.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09a88d3 and 8993362.

📒 Files selected for processing (7)
  • docs/configuration.md
  • docs/telemetry.md
  • mkdocs.yml
  • src/garth/http.py
  • src/garth/sso.py
  • src/garth/telemetry.py
  • tests/test_telemetry.py
💤 Files with no reviewable changes (1)
  • docs/configuration.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/garth/http.py
🧰 Additional context used
📓 Path-based instructions (2)
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Use pytest with VCR cassettes for HTTP recording/playback in tests
Mirror test directory structure to match source code structure

Files:

  • tests/test_telemetry.py
tests/**

⚙️ CodeRabbit configuration file

tests/**: - test functions shouldn't have a return type hint

  • it's ok to use assert instead of pytest.assume()

Files:

  • tests/test_telemetry.py
🧠 Learnings (1)
📚 Learning: 2026-01-09T22:41:07.977Z
Learnt from: CR
Repo: matin/garth PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T22:41:07.977Z
Learning: Applies to tests/**/*.py : Use pytest with VCR cassettes for HTTP recording/playback in tests

Applied to files:

  • tests/test_telemetry.py
🧬 Code graph analysis (2)
tests/test_telemetry.py (2)
src/garth/http.py (1)
  • configure (50-112)
src/garth/telemetry.py (4)
  • configure (130-181)
  • _response_hook (98-128)
  • _scrubbing_callback (76-78)
  • attach (202-213)
src/garth/telemetry.py (1)
src/garth/http.py (1)
  • configure (50-112)
🪛 LanguageTool
docs/telemetry.md

[grammar] ~55-~55: Ensure spelling is correct
Context: ...callback instead. ## Use with existing logfire setup If your application already conf...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~55-~55: Ensure spelling is correct
Context: ...ead. ## Use with existing logfire setup If your application already configures l...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 Ruff (0.14.10)
tests/test_telemetry.py

127-127: Unused lambda argument: data

(ARG005)


132-132: Possible hardcoded password assigned to argument: "token"

(S106)


138-138: Possible hardcoded password assigned to: "token"

(S105)


273-273: Unused lambda argument: data

(ARG005)


376-376: Unused lambda argument: data

(ARG005)

src/garth/telemetry.py

98-98: Unused method argument: args

(ARG002)


98-98: Unused method argument: kwargs

(ARG002)


127-128: try-except-pass detected, consider logging the exception

(S110)


127-127: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (13)
src/garth/sso.py (1)

48-52: LGTM! Hook propagation ensures telemetry coverage for OAuth flows.

Correctly extends the child session's response hooks with the parent's, ensuring telemetry hooks attached to the main Client.sess are inherited by GarminOAuth1Session instances used in get_oauth1_token and exchange.

src/garth/telemetry.py (4)

4-7: LGTM!

Imports are appropriate for the new session-based telemetry implementation.


76-78: LGTM!

Correctly bypasses logfire's scrubbing since data is already sanitized in _response_hook before reaching the callback.


183-200: LGTM!

Logfire configuration is properly guarded and only runs when using the default callback with a valid token.


202-213: LGTM!

The attach method correctly implements idempotent hook attachment using session identity tracking.

docs/telemetry.md (1)

1-96: LGTM! Comprehensive telemetry documentation.

Documentation clearly covers all configuration options, use cases, and security considerations (redaction). The static analysis hints about "logfire" are false positives—it's a product name.

mkdocs.yml (1)

44-44: LGTM!

Navigation entry correctly added for the new telemetry documentation.

tests/test_telemetry.py (6)

1-14: LGTM!

Imports are appropriate for testing the new telemetry functionality.


47-54: LGTM!

Test correctly verifies the new callback field defaults to None.


69-96: LGTM! Good VCR-based integration test.

Correctly uses @pytest.mark.vcr for HTTP recording/playback and verifies telemetry data capture through the callback mechanism.


181-287: LGTM! Comprehensive response hook test coverage.

Tests cover all key scenarios: string body, bytes body, missing body, disabled state, and exception handling. The static analysis warnings about unused lambda arguments are false positives—the lambdas are intentionally minimal for testing purposes.


336-417: LGTM! Good test coverage for attach and callback functionality.

Tests properly verify:

  • Hook attachment to sessions
  • Idempotent attachment behavior
  • Custom callback skipping logfire configuration
  • Missing token behavior
  • Client integration with telemetry callback

125-139: LGTM!

Test correctly verifies all configure parameters including the new callback. The static analysis warnings about hardcoded tokens are false positives—these are intentional test values.

Comment on lines +174 to +181
if not self.enabled:
return

# Set token if provided
# Configure logfire only if using default callback and not yet done
if self.callback is None and not self._logfire_configured:
self._configure_logfire()

self._configured = True
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the telemetry.py file around the mentioned lines
cd /root && find . -name "telemetry.py" -type f

Repository: matin/garth

Length of output: 110


🏁 Script executed:

# Let's look at the configure method and related code
head -200 src/garth/telemetry.py | tail -100

Repository: matin/garth

Length of output: 3545


🏁 Script executed:

# Check the _default_callback implementation
rg -n "_default_callback" src/garth/telemetry.py -A 5

Repository: matin/garth

Length of output: 486


🏁 Script executed:

# Check the _configure_logfire implementation
rg -n "_configure_logfire" src/garth/telemetry.py -A 10

Repository: matin/garth

Length of output: 527


🏁 Script executed:

# Check _response_hook to see how callbacks are used
rg -n "_response_hook" src/garth/telemetry.py -A 10

Repository: matin/garth

Length of output: 601


🏁 Script executed:

# Check test coverage as mentioned in the review
rg -n "enabled.*True" tests/test_telemetry.py | head -30

Repository: matin/garth

Length of output: 570


🏁 Script executed:

# Let's examine the test file more thoroughly to see if this edge case is tested
cat tests/test_telemetry.py

Repository: matin/garth

Length of output: 12551


🏁 Script executed:

# Let's check if there are any tests that specifically test enabled=True without token
rg -n "enabled.*True" tests/test_telemetry.py -B 3 -A 5 | head -50

Repository: matin/garth

Length of output: 1710


🏁 Script executed:

# Let's look for tests that don't set a token
rg -n "configure\(" tests/test_telemetry.py -A 3 | head -40

Repository: matin/garth

Length of output: 1025


🏁 Script executed:

# Check how logfire behaves when not configured - search for any error handling
rg -n "logfire" src/garth/telemetry.py -B 2 -A 2

Repository: matin/garth

Length of output: 3066


🏁 Script executed:

# Let's verify what logfire's default behavior is when info() is called unconfigured
rg -n "def _default_callback" src/garth/telemetry.py -A 5

Repository: matin/garth

Length of output: 262


🏁 Script executed:

# Check if there's a test that actually invokes _response_hook when logfire is unconfigured
rg -n "test_telemetry_no_token_skips_logfire_config" tests/test_telemetry.py -A 20

Repository: matin/garth

Length of output: 805


🏁 Script executed:

# Search for any explicit logfire initialization or error handling in default callback
rg -n "logfire" src/garth/telemetry.py

Repository: matin/garth

Length of output: 1316


Add test coverage for enabled telemetry without token or callback.

The edge case exists: when enabled=True without a LOGFIRE_TOKEN or custom callback, _configure_logfire() returns early without setting _logfire_configured=True (line 190). However, _configured is still set to True at line 181.

When _response_hook subsequently fires, it calls _default_callback, which imports logfire and calls logfire.info() on an unconfigured instance. This produces warnings or fails silently depending on logfire's behavior. The exception handler in _response_hook (lines 127-128) only catches exceptions, not warnings.

Existing test test_telemetry_no_token_skips_logfire_config verifies this state can exist but does not test actual hook invocation in this scenario. Add a test that mocks logfire and invokes _response_hook to verify behavior when logfire is unconfigured.

🤖 Prompt for AI Agents
In @src/garth/telemetry.py around lines 174 - 181, Add a unit test that sets
Telemetry.enabled=True with no LOGFIRE_TOKEN and no custom callback, ensures
after init that telemetry._configured is True and telemetry._logfire_configured
is False, then patch the global "logfire" module with a mock and call
telemetry._response_hook(dummy_request, dummy_response); assert the call does
not raise and that mock_logfire.info was not called (verifying the default
callback behaves safely when logfire remains unconfigured). Use unique symbols
Telemetry, _configure_logfire, _response_hook, _default_callback,
_logfire_configured and _configured to locate code under test.

@matin matin force-pushed the issue-181-isolated-telemetry branch from 8993362 to 8ab9506 Compare January 13, 2026 10:46
Previously, telemetry used logfire.instrument_requests() which
globally patches all requests.Session usage. This could conflict
with applications that have their own logfire/OpenTelemetry setup.

Now telemetry uses session-level hooks that only capture garth's
requests:

- Attach response hooks to garth's specific Session instance
- Only garth's HTTP requests are logged
- No global monkey-patching of the requests library
- Custom callback support for alternative telemetry destinations
- Logfire cloud remains the default when no callback is provided

Fixes #181

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@matin matin force-pushed the issue-181-isolated-telemetry branch from 8ab9506 to 57a7a98 Compare January 13, 2026 10:47
@matin matin merged commit f3bce32 into main Jan 13, 2026
26 checks passed
@matin matin deleted the issue-181-isolated-telemetry branch January 13, 2026 10:58
@matin matin mentioned this pull request Jan 13, 2026
19 tasks
@coderabbitai coderabbitai bot mentioned this pull request Jan 15, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Isolate telemetry to garth's session using hooks instead of global instrumentation

1 participant