Skip to content

Conversation

@kariy
Copy link
Member

@kariy kariy commented Sep 5, 2025

Summary

Implements a fluent builder pattern with compile-time guarantees for configuring tracing/logging features in the katana-tracing crate.

Motivation

The current tracing initialization uses a single init() function that takes all configuration at once. This PR introduces a type-state builder pattern that:

  • Enforces correct configuration at compile-time
  • Prevents mutually exclusive configurations (e.g., OTLP and Google Cloud telemetry)
  • Provides a more intuitive and flexible API
  • Maintains full backward compatibility

Changes

  • Added TracingBuilder with type-state pattern for compile-time safety
  • Implemented separate builder states for configuration flow
  • Added support for custom service names in telemetry backends
  • Created comprehensive examples demonstrating builder usage
  • Updated existing init() function to use builder internally (backward compatible)

API Usage

// Basic configuration
TracingBuilder::new()
    .with_log_format(LogFormat::Json)
    .with_default_filter()?
    .configure()
    .build()
    .await?;

// With OTLP telemetry
TracingBuilder::new()
    .with_service_name("my-katana-node")
    .configure()
    .with_telemetry()
    .otlp()
    .with_endpoint("http://localhost:4317")
    .build()
    .await?;

// With Google Cloud telemetry  
TracingBuilder::new()
    .configure()
    .with_telemetry()
    .gcloud()
    .with_project_id("my-project")
    .build()
    .await?;

Type-State Pattern Benefits

  • Compile-time validation: Invalid configurations are caught at compile time
  • Mutually exclusive telemetry: Once you choose OTLP or Google Cloud, you cannot configure the other
  • Single direction flow: Configuration progresses forward only, preventing inconsistent states
  • Required vs optional: Clear distinction between required and optional configurations

Testing

  • Added unit tests for builder pattern
  • Created comprehensive examples in examples/builder_usage.rs
  • All existing tests continue to pass

Backward Compatibility

The existing init() function is preserved and now uses the builder internally, ensuring no breaking changes for existing code.

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

@kariy kariy marked this pull request as draft September 5, 2025 16:27
@kariy kariy force-pushed the tracing-builder-pattern branch 3 times, most recently from 6c10672 to 4e67cc8 Compare September 9, 2025 18:36
kariy and others added 6 commits September 15, 2025 18:06
Implements a fluent builder pattern with compile-time guarantees for configuring tracing/logging features.

Key improvements:
- Type-state pattern ensures mutually exclusive telemetry backends (OTLP vs Google Cloud)
- Compile-time validation of configuration flow
- Support for custom service names in telemetry
- Flexible filter configuration (custom, env var, or defaults)
- Maintains backward compatibility with existing init() function

The builder enforces single-direction configuration flow and prevents invalid combinations at compile time, making the API more intuitive and error-proof.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove unnecessary configure() method from TracingBuilder
- Make telemetry methods (otlp() and gcloud()) directly available on TracingBuilder
- Simplify the API flow for better developer experience
- Remove unused type-state markers and PhantomData since they're no longer needed
- Update examples to reflect the cleaner API

The new API is more intuitive:
  TracingBuilder::new()
    .with_service_name("my-service")
    .otlp()
    .with_endpoint("http://localhost:4317")
    .build()

Instead of:
  TracingBuilder::new()
    .with_service_name("my-service")
    .configure()
    .with_telemetry()
    .otlp()
    .with_endpoint("http://localhost:4317")
    .build()

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implement compile-time enforcement for log format selection using type-state pattern:
- Log format must now be chosen first via .json() or .full() methods
- Filter and service configuration only available after format is selected
- Telemetry methods (otlp/gcloud) only accessible after format is set
- Maintains backward compatibility through internal with_format() helper

This ensures users cannot accidentally build without specifying a format,
catching configuration errors at compile time rather than runtime.

Example:
  TracingBuilder::new()
    .json()  // Must choose format first
    .with_service_name("my-service")
    .build()

The compiler will reject:
  TracingBuilder::new()
    .with_service_name("my-service")  // Error: method not found
    .build()

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@kariy kariy force-pushed the tracing-builder-pattern branch from 34dfd8b to 81752a6 Compare September 15, 2025 22:07
@kariy kariy force-pushed the main branch 2 times, most recently from e3bd68b to e897bbb Compare December 30, 2025 17:28
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.

2 participants