# frozen_string_literal: true # Copyright The OpenTelemetry Authors # # SPDX-License-Identifier: Apache-2.0 require 'opentelemetry/context/key' require 'opentelemetry/context/propagation' module OpenTelemetry # Manages context on a per-fiber basis class Context EMPTY_ENTRIES = {}.freeze STACK_KEY = :__opentelemetry_context_storage__ private_constant :EMPTY_ENTRIES, :STACK_KEY DetachError = Class.new(OpenTelemetry::Error) class << self # Returns a key used to index a value in a Context # # @param [String] name The key name # @return [Context::Key] def create_key(name) Key.new(name) end # Returns current context, which is never nil # # @return [Context] def current stack.last || ROOT end # Associates a Context with the caller's current Fiber. Every call to # this operation should be paired with a corresponding call to detach. # # Returns a token to be used with the matching call to detach # # @param [Context] context The new context # @return [Object] A token to be used when detaching def attach(context) s = stack s.push(context) s.size end # Restores the previous Context associated with the current Fiber. # The supplied token is used to check if the call to detach is balanced # with a corresponding attach call. A warning is logged if the # calls are unbalanced. # # @param [Object] token The token provided by the matching call to attach # @return [Boolean] True if the calls matched, false otherwise def detach(token) s = stack calls_matched = (token == s.size) OpenTelemetry.handle_error(exception: DetachError.new('calls to detach should match corresponding calls to attach.')) unless calls_matched s.pop calls_matched end # Executes a block with ctx as the current context. It restores # the previous context upon exiting. # # @param [Context] ctx The context to be made active # @yield [context] Yields context to the block def with_current(ctx) token = attach(ctx) yield ctx ensure detach(token) end # Execute a block in a new context with key set to value. Restores the # previous context after the block executes. # @param [String] key The lookup key # @param [Object] value The object stored under key # @param [Callable] Block to execute in a new context # @yield [context, value] Yields the newly created context and value to # the block def with_value(key, value) ctx = current.set_value(key, value) token = attach(ctx) yield ctx, value ensure detach(token) end # Execute a block in a new context where its values are merged with the # incoming values. Restores the previous context after the block executes. # @param [String] key The lookup key # @param [Hash] values Will be merged with values of the current context # and returned in a new context # @param [Callable] Block to execute in a new context # @yield [context, values] Yields the newly created context and values # to the block def with_values(values) ctx = current.set_values(values) token = attach(ctx) yield ctx, values ensure detach(token) end # Returns the value associated with key in the current context # # @param [String] key The lookup key def value(key) current.value(key) end def clear stack.clear end def empty new(EMPTY_ENTRIES) end private def stack Thread.current[STACK_KEY] ||= [] end end def initialize(entries) @entries = entries.freeze end # Returns the corresponding value (or nil) for key # # @param [Key] key The lookup key # @return [Object] def value(key) @entries[key] end alias [] value # Returns a new Context where entries contains the newly added key and value # # @param [Key] key The key to store this value under # @param [Object] value Object to be stored under key # @return [Context] def set_value(key, value) new_entries = @entries.dup new_entries[key] = value Context.new(new_entries) end # Returns a new Context with the current context's entries merged with the # new entries # # @param [Hash] values The values to be merged with the current context's # entries. # @param [Object] value Object to be stored under key # @return [Context] def set_values(values) # rubocop:disable Naming/AccessorMethodName: Context.new(@entries.merge(values)) end ROOT = empty.freeze end end