Skip to content

Conversation

monoxgas
Copy link
Contributor

@monoxgas monoxgas commented Oct 3, 2025

Support tracking and exporting spans with api key overrides to support multi-user services

Key Changes

  • Reworks our span exporter to check for an override attribute and create a dedicated export instance for those spans - routing them per api key/user.
  • Added an dn.run(..., api_key=) argument and with dn.using_api_key() to override this context on demand.
  • Ensured that get_run_context carries this override information so if we have a backend stack passing around user-local context, it's reflected in the exports.

Example

import dreadnode as dn
import threading

OTHER_API_KEY = "..."


with dn.run("testing", project="context"):
    dn.log_input("x", 5)
    dn.log_output("z", 15)


def continue_run_in_thread(context):
    with dn.continue_run(context):
        dn.log_input("a", 10)
        dn.log_output("b", 20)

with dn.using_api_key(OTHER_API_KEY):
    with dn.run("other", project="context") as run:
        dn.log_input("y", 10)
        dn.log_output("y", 10)

        context = dn.get_run_context()
        
        thread = threading.Thread(target=continue_run_in_thread, args=(context,))
        thread.start()
        thread.join()

Generated Summary:

PR Description

This pull request introduces significant modifications to the SDK's tracing mechanism, enhancing support for multi-user scenarios by allowing the use of API keys for span exports.

  • Refactored the get_run_context method to simplify the extraction of run context data.
  • Introduced a new context manager, using_api_key, to facilitate temporary overriding of the API key used for exporting spans during nested operations.
  • Added api_key as an optional parameter to run and initialize methods, enabling context-specific tracing.
  • Replaced the BatchSpanProcessor with a new RoutingSpanProcessor that allows spans to be routed based on user-specific tokens.
  • Updated the RunContext structure to include export_auth_token to maintain API key information across different contexts.
  • Cleaned up unused imports and comments related to OpenTelemetry integrations.

These changes enable more flexible tracing capabilities and improve the SDK's usability in multi-user applications, allowing for controlled export configurations based on individual API keys.

This summary was generated with ❤️ by rigging

@dreadnode-renovate-bot dreadnode-renovate-bot bot added area/docs Changes to documentation and guides type/docs Documentation updates and improvements labels Oct 3, 2025
@monoxgas monoxgas requested a review from Copilot October 3, 2025 04:53
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Introduces multi-user context support to enable tracking and exporting spans with API key overrides, allowing services to handle multiple users with dedicated export instances per API key.

  • Added RoutingSpanProcessor to route spans based on user-specific API keys instead of using a single BatchSpanProcessor
  • Introduced using_api_key() context manager and api_key parameter to run() for per-user span export control
  • Enhanced RunContext to include export authentication token for cross-thread/process context continuity

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
dreadnode/tracing/span.py Added export auth token context management and updated RunSpan to support API key overrides
dreadnode/tracing/processors.py New RoutingSpanProcessor for multi-user span routing based on API keys
dreadnode/tracing/constants.py Added constant for span resource attribute token
dreadnode/main.py Replaced BatchSpanProcessor with RoutingSpanProcessor and added using_api_key context manager
dreadnode/init.py Exported using_api_key function
docs/sdk/main.mdx Updated documentation to reflect new API key functionality

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +326 to 335
class RunContext(te.TypedDict, total=False):
"""Context for transferring and continuing runs in other places."""

run_id: str
run_name: str
project: str
trace_context: dict[str, str]
export_auth_token: str | None


Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

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

Changing RunContext from a required TypedDict to total=False makes all fields optional, which could break existing code that assumes required fields like 'run_id' are always present. Consider using separate required and optional field groups or create a new TypedDict for the optional fields.

Suggested change
class RunContext(te.TypedDict, total=False):
"""Context for transferring and continuing runs in other places."""
run_id: str
run_name: str
project: str
trace_context: dict[str, str]
export_auth_token: str | None
class RunContextBase(te.TypedDict):
"""Required fields for RunContext."""
run_id: str
run_name: str
project: str
trace_context: dict[str, str]
class RunContext(RunContextBase, total=False):
"""Context for transferring and continuing runs in other places, with optional fields."""
export_auth_token: str | None

Copilot uses AI. Check for mistakes.

server_url: str,
default_token: str,
*,
token_header_name: str = "X-Api-Key", # noqa: S107
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

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

The noqa comment suggests this is flagged as a security issue. Consider using a constant or configuration value instead of a hardcoded string literal for the API key header name.

Copilot uses AI. Check for mistakes.

Example

```python
with dreadnode.with_api_key("other_user_api_key"):
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

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

The example code uses dreadnode.with_api_key() but the actual function name is using_api_key(). This inconsistency will cause the example to fail.

Copilot uses AI. Check for mistakes.

Example:
~~~
with dreadnode.with_api_key("other_user_api_key"):
Copy link

Copilot AI Oct 3, 2025

Choose a reason for hiding this comment

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

The documentation example uses with_api_key() but the actual function name is using_api_key(). This inconsistency will cause the example to fail.

Copilot uses AI. Check for mistakes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Changes to documentation and guides type/docs Documentation updates and improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant