CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-opentelemetry--opentelemetry-context

OpenTelemetry Context propagation mechanism for carrying scoped values across API boundaries and between threads in Java applications

Pending
Overview
Eval results
Files

context-keys-scoping.mddocs/

Context Keys and Scoping

Context keys provide type-safe indexing for values stored in contexts, while scopes manage the lifecycle of context attachments with automatic cleanup.

Context Keys

Creating Context Keys

Creates a new context key with an optional debug name.

static <T> ContextKey<T> named(String name);

Parameters:

  • name - Debug name for the key (does not affect behavior)

Returns: A new ContextKey instance

Usage Example:

// Create typed context keys
private static final ContextKey<String> USER_ID_KEY = ContextKey.named("userId");
private static final ContextKey<Integer> REQUEST_COUNT_KEY = ContextKey.named("requestCount");
private static final ContextKey<List<String>> TAGS_KEY = ContextKey.named("tags");

// Use keys for type-safe storage and retrieval
Context context = Context.current()
    .with(USER_ID_KEY, "user123")
    .with(REQUEST_COUNT_KEY, 42)
    .with(TAGS_KEY, Arrays.asList("tag1", "tag2"));

// Type-safe retrieval
String userId = context.get(USER_ID_KEY); // String
Integer count = context.get(REQUEST_COUNT_KEY); // Integer  
List<String> tags = context.get(TAGS_KEY); // List<String>

Key Identity and Comparison

Context keys are compared by reference equality, not by name. Each call to named() creates a distinct key.

// These are different keys, even with same name
ContextKey<String> key1 = ContextKey.named("user");
ContextKey<String> key2 = ContextKey.named("user");

Context ctx = Context.current().with(key1, "alice");
String value1 = ctx.get(key1); // Returns "alice"
String value2 = ctx.get(key2); // Returns null - different key!

Best Practices:

  • Store keys as static final fields
  • Use descriptive names for debugging
  • Create keys once and reuse them
public class UserContext {
    // Good: single key instance shared across usage
    private static final ContextKey<String> USER_ID_KEY = ContextKey.named("userId");
    
    public static Context withUserId(String userId) {
        return Context.current().with(USER_ID_KEY, userId);
    }
    
    public static String getUserId() {
        return Context.current().get(USER_ID_KEY);
    }
}

Scope Management

Scope Interface

Represents a mounted context that must be closed to restore the previous context.

interface Scope extends AutoCloseable {
    static Scope noop();
    void close();
}

No-op Scope

Returns a scope that does nothing when closed.

static Scope noop();

Used internally when attaching a context that's already current.

Usage Example:

// Conditional scope creation
public Scope attachIfNeeded(Context context) {
    if (Context.current() != context) {
        return context.makeCurrent();
    } else {
        return Scope.noop(); // No change needed
    }
}

Closing Scopes

Closes the scope and restores the previous context.

void close();

Usage with try-with-resources:

Context newContext = Context.current().with(USER_KEY, user);

try (Scope scope = newContext.makeCurrent()) {
    // Context is active here
    performOperation();
} // Scope automatically closed, previous context restored

Manual closing (not recommended):

Context newContext = Context.current().with(USER_KEY, user);
Scope scope = newContext.makeCurrent();
try {
    performOperation();
} finally {
    scope.close(); // Must close manually
}

Scope Lifecycle Examples

Basic Scope Usage

private static final ContextKey<String> OPERATION_KEY = ContextKey.named("operation");

public void performUserOperation(String userId, String operation) {
    Context operationContext = Context.current()
        .with(USER_ID_KEY, userId)
        .with(OPERATION_KEY, operation);
    
    try (Scope scope = operationContext.makeCurrent()) {
        // Context is available to all called methods
        logOperationStart();
        executeOperation();
        logOperationEnd();
    } // Previous context automatically restored
}

private void logOperationStart() {
    String userId = Context.current().get(USER_ID_KEY);
    String operation = Context.current().get(OPERATION_KEY);
    logger.info("Starting {} for user {}", operation, userId);
}

Nested Scopes

public void processRequest(String requestId) {
    Context requestContext = Context.current().with(REQUEST_ID_KEY, requestId);
    
    try (Scope requestScope = requestContext.makeCurrent()) {
        logger.info("Processing request: {}", requestId);
        
        for (String userId : getUsers()) {
            Context userContext = Context.current().with(USER_ID_KEY, userId);
            
            try (Scope userScope = userContext.makeCurrent()) {
                // Both request ID and user ID available
                processUser();
            } // User context ends, request context continues
        }
        
    } // Request context ends, original context restored
}

Error Handling with Scopes

Scopes are automatically closed even when exceptions occur:

public void riskyOperation() {
    Context context = Context.current().with(OPERATION_KEY, "risky");
    
    try (Scope scope = context.makeCurrent()) {
        // Context is available
        performRiskyOperation();
        
        if (someCondition) {
            throw new RuntimeException("Operation failed");
        }
        
    } // Scope closed even if exception thrown
    catch (RuntimeException e) {
        // Previous context is already restored
        logger.error("Operation failed", e);
        throw e;
    }
}

Advanced Scope Patterns

Conditional Context Application

public void conditionalContext(String userId) {
    boolean needsUserContext = userId != null && !userId.isEmpty();
    
    Scope scope = needsUserContext 
        ? Context.current().with(USER_ID_KEY, userId).makeCurrent()
        : Scope.noop();
        
    try (scope) {
        performOperation();
    }
}

Scope Delegation

public class ContextualService {
    private final Service delegate;
    
    public void serviceMethod(Context context) {
        try (Scope scope = context.makeCurrent()) {
            // Service method runs with provided context
            delegate.performAction();
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-io-opentelemetry--opentelemetry-context

docs

context-keys-scoping.md

context-propagation.md

core-context.md

executor-integration.md

function-wrapping.md

implicit-context-values.md

index.md

storage-customization.md

tile.json