Skip to content

Commit

Permalink
Context prop combined (#147)
Browse files Browse the repository at this point in the history
* Add Context.with_current to handle context nesting

* Update active span tracking to use Context

* Rename Context#update -> Context.set

* Add Context#clear method

* Introduce baggage context

* Introduce basic Context object

* Implement Context#attach and Context#detach

* Rename set->update; freeze entries

* Handle detach when parent is nil

* Context propagation prototype

* Comment out failing tests after rebase

* Appease the cop with todos for missing docs

* Move baggage implementation to SDK

* Use method names from API keep implicit (block) form, for now

* Update baggage to use explicit context

* Use context keys for baggage and current span

* Align Context methods more closely with Java prototype

* Add baggage_manager accessor to OpenTelemetry Module

This seems like the right thing to do so that different implementations
can be specified. We might also want to consider making Baggage::Manager
a class rather than a module.

* Convert tests for distributed_context_manager to correlation_context_manager

* Add minimal CorrelationContextManager implementation

* Add constructor and test for Label

* Cleanup context in an after block rather than before

* Initial implementation of SDK correlation context

* Simplify label

The spec specifies that there should be Label::Key and Label::Value classes.
Each wraps a string, which is a little heavyweight and cumbersome to use. This
commit changes Key and Value to simple strings instead.

* Initial implementation of SDK::CorrelationContextManager

* Add initial Propagation API

* Move SpanContext related propagation to Trace namespace

* Add HttpTraceContextInjector/Extractor

For simplicity, these are based off of the TextFormat class. Ultimately,
we'll probably end up with HttpInjector/Extractor classes extracted from
the TextFormat class.

* Add global injectors and extractors

* Make Baggage::Manager a class instead of a module

* Add Tracer#current_span_context

* Add Context.with_values

* Use Context.with_values in Tracer#with_span

* Implement HttpTraceContextExtractor / Injector directly

* Remove TextFormat and references

* Remove reference to binary format in TracerFactory

* Remove references to CorrelationContext (temporarily)

CorrelationContext has been left out of the current OTEP. It's likely to be back
and we have an initial implementation. This commit removes references from the
API and SDK, but leaves the files intact until we know what should be done with
them in the long term.

* Add tests for context on multiple threads

* Use alternative context implementation

We should still do some benchmarking, but I think this is going to be a
better implementation, so I'm preemptively using it for the purpose of
the initial PR.

* Provide default key names for HttpTraceContext / Injector extractor

* Standardize naming for Baggage and DistributedContext injector / extractors

* Add readers for injectors / extractors to TracerFactory

* Remove distributed context

* Move Baggage -> CorrelationContext

* Prefer current span over extracted span context as implicit parent

* Propagate remote span context if there is not a current span

* Rename remote_span_context_key -> extracted_span_context_key

* Move propagator references from TracerFactory to global Propagation

* Happy path correlation context extractor

* Ignore correlation context properties

OTel may use properties in the future for metadata. For now, we'll
ignore them when extracting.

* Initial HttpCorrelationContextInjector implmentation

* Manage CorrelationContext values as Strings

* Accept explicit context object in Tracer#start_span

* Use Correlation-Context as the default key

This is subject to change, but we'll use the key as it's spec'd today.

* Add HttpCorrelationContextExtractor/Injectors to Propagation

* Extract default getter / setters into modules

* Log warning for non matching Context attach / detach

* Fix @todo docs

* Remove explicit block parameters in Context

* Provide defaults for Propagation#inject and Propagation#extract

This commit improves the ergonomics of Propagation#inject and
Propagation#extract by providing defaults arguments where possible.
For both methods, carrier is the only required argument. Context
defaults to Context.current and the http_injectors / extractors
default to those registered globally.

* Add implicit context to CorrelationContext::Manager methods

* Introduce Builder to facilitate multiple modifications to CorrelationContext

This commit adds a builder to faciliate making multiple modifications to
CorrelationContext without creating multiple, intermediary contexts.
Manager#build_context should be used when making multiple modifications
to the correlation context. When making a single modification, all other
methods should be used. Some may object to the builder and might recommend
a single method with kwargs, but I think this makes for a more fluent API.

* Reorganize accessors for injector / extractors

* Rename HttpCorrelationContextInjector/Extractor -> HttpInjector/Extractor

* Cleanup ContextKeys

* Remove Context#attach and Context#detach

* Cleanup correlations

* Remove with_context from Tracer#start_span, redefine with_parent_context

This commit replaces with_context with with_parent_context and removes
with_context.

* Return original context if parsing TraceContext fails

* Revert "Remove Context#attach and Context#detach"

This reverts commit 88138ea66627f7d3153c4d668ed228a481f24e05.

* Document Context#attach and Context#detach to be private

* Introduce Context::Key for indexing and retrieving values from a Context

* Return to Hash based context

There are perf concerns about the linked list approach.

* Don't test implementation details

* Update docs

* Fix unnecessary cop after rebase

* Return original context on failed Correlation Context extraction

* Update Faraday and Sinatra adapters to use OpenTelemetry.propagation

* Rename OpenTelemetry.correlation_context_manager -> OpenTelemetry.correlations

Co-authored-by: Francis Bogsanyi <francis.bogsanyi@shopify.com>
  • Loading branch information
mwear and fbogsany committed Feb 6, 2020
1 parent b795aa6 commit c95f7cd
Show file tree
Hide file tree
Showing 53 changed files with 1,972 additions and 457 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,8 @@ def disable_span_reporting?(_env)

attr_reader :app

# Outbound requests should only need to inject the current span.
def propagate_context(span, env)
propagator.inject(span.context, env.request_headers)
end

def propagator
OpenTelemetry.tracer_factory.http_text_format
OpenTelemetry.propagation.inject(env.request_headers)
end

def tracer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def call(env)
attr_reader :app

def parent_context(env)
OpenTelemetry.tracer_factory.http_text_format.extract(env)
OpenTelemetry.propagation.extract(env)
end

def tracer
Expand Down
22 changes: 14 additions & 8 deletions api/lib/opentelemetry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

require 'opentelemetry/error'
require 'opentelemetry/context'
require 'opentelemetry/distributed_context'
require 'opentelemetry/correlation_context'
require 'opentelemetry/internal'
require 'opentelemetry/instrumentation'
require 'opentelemetry/metrics'
Expand All @@ -23,7 +23,7 @@
module OpenTelemetry
extend self

attr_writer :tracer_factory, :meter_factory, :distributed_context_manager
attr_writer :tracer_factory, :meter_factory, :correlations

attr_accessor :logger

Expand All @@ -39,17 +39,23 @@ def meter_factory
@meter_factory ||= Metrics::MeterFactory.new
end

# @return [Object, DistributedContext::Manager] registered distributed
# context manager or a default no-op implementation of the manager
def distributed_context_manager
@distributed_context_manager ||= DistributedContext::Manager.new
end

# @return [Instrumentation::Registry] registry containing all known
# instrumentation
def instrumentation_registry
@instrumentation_registry ||= Instrumentation::Registry.new
end

# @return [Object, CorrelationContext::Manager] registered
# correlation context manager or a default no-op implementation of the
# manager.
def correlations
@correlations ||= CorrelationContext::Manager.new
end

# @return [Context::Propagation::Propagation] an instance of the propagation API
def propagation
@propagation ||= Context::Propagation::Propagation.new
end

self.logger = Logger.new(STDOUT)
end
148 changes: 133 additions & 15 deletions api/lib/opentelemetry/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,146 @@
#
# SPDX-License-Identifier: Apache-2.0

require 'opentelemetry/context/key'
require 'opentelemetry/context/propagation'

module OpenTelemetry
# The Context module provides per-thread storage.
module Context
extend self
# Manages context on a per-fiber basis
class Context
KEY = :__opentelemetry_context__
EMPTY_ENTRIES = {}.freeze

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
Thread.current[KEY] ||= ROOT
end

# Sets the current context
#
# @param [Context] ctx The context to be made active
def current=(ctx)
Thread.current[KEY] = ctx
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
def with_current(ctx)
prev = ctx.attach
yield
ensure
ctx.detach(prev)
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
def with_value(key, value)
ctx = current.set_value(key, value)
prev = ctx.attach
yield value
ensure
ctx.detach(prev)
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
def with_values(values)
ctx = current.set_values(values)
prev = ctx.attach
yield values
ensure
ctx.detach(prev)
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
self.current = ROOT
end

def get(key)
storage[key]
def empty
new(nil, EMPTY_ENTRIES)
end
end

def with(key, value)
store = storage
previous = store[key]
store[key] = value
yield value
ensure
store[key] = previous
def initialize(parent, entries)
@parent = parent
@entries = entries.freeze
end

private
# 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(self, 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(self, @entries.merge(values))
end

def storage
Thread.current[:__opentelemetry__] ||= {}
# @api private
def attach
prev = self.class.current
self.class.current = self
prev
end

# @api private
def detach(ctx_to_attach = nil)
OpenTelemetry.logger.warn 'Calls to detach should match corresponding calls to attach' if self.class.current != self

ctx_to_attach ||= @parent || ROOT
ctx_to_attach.attach
end

ROOT = empty.freeze
end
end
29 changes: 29 additions & 0 deletions api/lib/opentelemetry/context/key.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

# Copyright 2019 OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

module OpenTelemetry
class Context
# The Key class provides mechanisms to index and access values from a
# Context
class Key
attr_reader :name

# @api private
# Use Context.create_key to obtain a Key instance.
def initialize(name)
@name = name
end

# Returns the value indexed by this Key in the specified context
#
# @param [optional Context] context The Context to lookup the key from.
# Defaults to +Context.current+.
def get(context = Context.current)
context[self]
end
end
end
end
18 changes: 18 additions & 0 deletions api/lib/opentelemetry/context/propagation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

# Copyright 2019 OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

require 'opentelemetry/context/propagation/default_getter'
require 'opentelemetry/context/propagation/default_setter'
require 'opentelemetry/context/propagation/propagation'

module OpenTelemetry
class Context
# The propagation module contains APIs and utilities to interact with context
# and propagate across process boundaries.
module Propagation
end
end
end
26 changes: 26 additions & 0 deletions api/lib/opentelemetry/context/propagation/default_getter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

# Copyright 2019 OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

module OpenTelemetry
class Context
module Propagation
# The default getter module provides a common method for reading
# a key from a carrier that implements +[]+
module DefaultGetter
DEFAULT_GETTER = ->(carrier, key) { carrier[key] }
private_constant :DEFAULT_GETTER

# Returns a callable that can read a key from a carrier that implements
# +[]+. Useful for extract operations.
#
# @return [Callable]
def default_getter
DEFAULT_GETTER
end
end
end
end
end
26 changes: 26 additions & 0 deletions api/lib/opentelemetry/context/propagation/default_setter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

# Copyright 2019 OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

module OpenTelemetry
class Context
module Propagation
# The default setter module provides a common method for writing
# a key into a carrier that implements +[]=+
module DefaultSetter
DEFAULT_SETTER = ->(carrier, key, value) { carrier[key] = value }
private_constant :DEFAULT_SETTER

# Returns a callable that can write a key into a carrier that implements
# +[]=+. Useful for inject operations.
#
# @return [Callable]
def default_setter
DEFAULT_SETTER
end
end
end
end
end
Loading

0 comments on commit c95f7cd

Please sign in to comment.