Chaturmind
LearnDSASystem DesignDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight

Behavioral Design Patterns

  • Strategy Pattern
  • Observer Pattern
  • Command Pattern
  • Template Method
  • State Pattern
  • Chain of Responsibility
  • Iterator & Mediator
  • Visitor Pattern

Machine Coding Exercises

  • Parking Lot — Requirements & Class Design
  • Parking Lot — Implementation
  • Elevator System — Requirements & Class Design
  • Elevator System — Implementation & Scheduling
  • Library Management System
  • Tic-Tac-Toe / Board Game Design
  • Chess Engine Design
  • Vending Machine Design
  • ATM System Design
  • In-Memory Rate Limiter
  • Thread-Safe LRU Cache
  • Connection Pool Design
  • Producer-Consumer Class Design
  • Movie Ticket Booking System
  • Hotel Reservation System
  • Airline Booking / Seat Selection System
  • Splitwise / Expense Sharing System
  • Design a Logging Framework
  • Design a Notification/Observer-Based Pub-Sub
  • Design a Cache with Pluggable Eviction Policy
  • Design Snake and Ladder
  • Design a Card Game Framework
  • Design an Inventory Management System
  • Restaurant Management System
  • Food Delivery Order Matching
  • Design a Shopping Cart & Checkout Flow
  • Design a Recommendation Engine
  • Design a Job Scheduler
  • Design a Circuit Breaker
  • Design a Distributed ID Generator
  • Design a Pub-Sub Message Broker
  • Design Twitter/X
  • Multi-Pattern Problem: Design a Ride Booking Class Model
  • Multi-Pattern Problem: Design a Food Ordering Class Model
  • Design an Authentication/Authorization Module
  • Design a Form/Survey Builder
  • Design a Meeting Room / Calendar Booking System
  • Design a Search/Filter Engine for an E-Commerce Catalog
  • Design a Voting/Polling System
  • Design a Retry Mechanism with Backoff
  • Design a Health Check Aggregator
  • Design a Feature Flag System
  • Design a Config Management Client
  • Design a Leader Election Algorithm
  • Design a Distributed Counter
  • Design a Bloom Filter
  • Design a Consistent Hashing Ring
  • Design a Distributed Tracing Library
  • Design a Metrics Collection Library
  • Design an Audit Logging Framework
  • Design a Plugin/Extension System
  • Design a Workflow Engine
  • Multi-Pattern Capstone: Design a Food Delivery App
Chaturmind
← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight

Behavioral Design Patterns

  • Strategy Pattern
  • Observer Pattern
  • Command Pattern
  • Template Method
  • State Pattern
  • Chain of Responsibility
  • Iterator & Mediator
  • Visitor Pattern

Machine Coding Exercises

  • Parking Lot — Requirements & Class Design
  • Parking Lot — Implementation
  • Elevator System — Requirements & Class Design
  • Elevator System — Implementation & Scheduling
  • Library Management System
  • Tic-Tac-Toe / Board Game Design
  • Chess Engine Design
  • Vending Machine Design
  • ATM System Design
  • In-Memory Rate Limiter
  • Thread-Safe LRU Cache
  • Connection Pool Design
  • Producer-Consumer Class Design
  • Movie Ticket Booking System
  • Hotel Reservation System
  • Airline Booking / Seat Selection System
  • Splitwise / Expense Sharing System
  • Design a Logging Framework
  • Design a Notification/Observer-Based Pub-Sub
  • Design a Cache with Pluggable Eviction Policy
  • Design Snake and Ladder
  • Design a Card Game Framework
  • Design an Inventory Management System
  • Restaurant Management System
  • Food Delivery Order Matching
  • Design a Shopping Cart & Checkout Flow
  • Design a Recommendation Engine
  • Design a Job Scheduler
  • Design a Circuit Breaker
  • Design a Distributed ID Generator
  • Design a Pub-Sub Message Broker
  • Design Twitter/X
  • Multi-Pattern Problem: Design a Ride Booking Class Model
  • Multi-Pattern Problem: Design a Food Ordering Class Model
  • Design an Authentication/Authorization Module
  • Design a Form/Survey Builder
  • Design a Meeting Room / Calendar Booking System
  • Design a Search/Filter Engine for an E-Commerce Catalog
  • Design a Voting/Polling System
  • Design a Retry Mechanism with Backoff
  • Design a Health Check Aggregator
  • Design a Feature Flag System
  • Design a Config Management Client
  • Design a Leader Election Algorithm
  • Design a Distributed Counter
  • Design a Bloom Filter
  • Design a Consistent Hashing Ring
  • Design a Distributed Tracing Library
  • Design a Metrics Collection Library
  • Design an Audit Logging Framework
  • Design a Plugin/Extension System
  • Design a Workflow Engine
  • Multi-Pattern Capstone: Design a Food Delivery App
HomeLearnSystem DesignLow-Level Design & Design PatternsMachine Coding Exercises
✓ FreeIntermediate· 7 min read

Design a Distributed Tracing Library

Span and Trace classes with parent-child relationships and timing capture, in-process context propagation simulating cross-service trace context, and the direct mapping to OpenTelemetry's own model.

Published September 23, 2026


Design a Distributed Tracing Library

Span and Trace classes

class Span {
    String spanId; String traceId; String parentSpanId;
    String operationName; Instant startTime; Instant endTime;

    void finish() { this.endTime = Instant.now(); }
    Duration duration() { return Duration.between(startTime, endTime); }
}

class Trace {
    String traceId;
    List<Span> spans = new CopyOnWriteArrayList<>();
    void addSpan(Span span) { spans.add(span); }
}

This is a from-scratch implementation of exactly the trace/span model covered conceptually in Distributed Tracing — traceId ties every span in one request's journey together; parentSpanId reconstructs the call HIERARCHY (which operation happened inside which other operation); startTime/endTime give per-span duration, the raw data a waterfall visualization (Zipkin/Jaeger) is built from.

Context propagation, simulated in-process

class TraceContext {
    private static final ThreadLocal<Span> currentSpan = new ThreadLocal<>();

    static Span startChildSpan(String operationName) {
        Span parent = currentSpan.get();
        Span child = new Span();
        child.traceId = (parent != null) ? parent.traceId : UUID.randomUUID().toString();
        child.parentSpanId = (parent != null) ? parent.spanId : null;
        child.spanId = UUID.randomUUID().toString();
        child.startTime = Instant.now();
        currentSpan.set(child);
        return child;
    }

    static void endSpan(Span span, Span previousSpan) {
        span.finish();
        currentSpan.set(previousSpan); // restore the PARENT as current once this span ends
    }
}

Using a ThreadLocal to track "the currently active span" is exactly the same MDC-style mechanism as Centralized Logging's correlation ID propagation — and it carries the SAME cleanup obligation: failing to restore the previous span (or clear it) on a pooled thread risks the exact thread-leak bug discussed there. This in-process simulation is a genuine, useful simplification of the REAL cross-service problem — in production, propagating trace context across an actual network call means serializing traceId/spanId into a header (the traceparent header from Distributed Tracing) rather than relying on a ThreadLocal that obviously can't survive crossing a network boundary; simulating it in-process here isolates and teaches the PARENT-CHILD bookkeeping logic without the added complexity of actual network serialization.

Method-call-chain example

void processOrder(Order order) {
    Span span = TraceContext.startChildSpan("processOrder");
    try {
        validateOrder(order);   // internally starts its own child span
        chargePayment(order);   // internally starts its own child span
    } finally {
        TraceContext.endSpan(span, /* previous */ null);
    }
}

Each nested method call that starts its own span automatically inherits the CURRENT span as its parent (via the ThreadLocal), building the hierarchy without any method needing to explicitly pass a parent reference down through every call — this mirrors exactly how a REAL tracing library's API (OpenTelemetry's) works from an instrumented application's point of view.

Mapping directly onto OpenTelemetry's actual design

This exercise's Span/Trace/TraceContext classes are a deliberately-simplified version of OpenTelemetry's actual API surface — OpenTelemetry's Span, SpanContext, and Context (its own ThreadLocal-like propagation mechanism, io.opentelemetry.context.Context) play essentially the same roles. Having built this from scratch is what makes OpenTelemetry's real API legible on sight, rather than a black box you configure without understanding.

Follow-up questions this topic invites — and their answers

Q: How would you extend this in-process simulation to actually propagate across a real network call? A: Serialize traceId + the current span's spanId into an outgoing HTTP header before making the call, and on the receiving side, read that header to seed a NEW span's traceId/parentSpanId rather than generating a fresh traceId — this is exactly the W3C Trace Context header format covered in Distributed Tracing.

Q: What happens if a span is never explicitly finish()'d, e.g. due to an uncaught exception? A: The span leaks with no endTime, and a real implementation needs a try/finally (as shown) or equivalent guaranteed-cleanup mechanism — an unfinished span either shows up as permanently 'in progress' in the tracing backend or gets silently dropped, depending on the collector's own timeout handling, either of which is a real observability gap.

Q: How would sampling (from Distributed Tracing) fit into this class design? A: A sampling decision would typically be made once at startChildSpan for the ROOT span (the first span in a trace) and propagated down — if the trace isn't sampled, span creation could still happen (for correctness of the in-process call structure) but the spans simply wouldn't be EXPORTED to the backend, keeping the sampling decision cheap to check without needing to prevent span objects from being created at all.

Q: Does this design support async/multi-threaded operations within one traced request? A: Not without extra work — a plain ThreadLocal doesn't automatically follow execution onto a DIFFERENT thread (e.g. a async task submitted to an executor); a real implementation needs to explicitly CAPTURE the current span before handing off to another thread and RESTORE it there, which is exactly the kind of context-propagation-across-thread-boundaries problem InheritableThreadLocal or explicit context-passing solves.

Previous

Design a Consistent Hashing Ring

Next

Design a Metrics Collection Library

AI Tutor

Lesson: Design a Distributed Tracing Library

Quick actions

AI responses can be inaccurate. Verify critical information.