-
Notifications
You must be signed in to change notification settings - Fork 248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Context prop combined #147
Changes from all commits
864564a
49cff24
7c0949e
ddc8cc5
d6e107b
4a03744
ceca1d9
b37ac9d
6802ee1
837010d
539c428
814cf80
e69ae76
0f53c8d
bc7dae9
d72f14e
dc95d11
081d6d5
44e10a3
29913b2
bb699db
9d3f047
51f247f
8e8bbb2
20002d2
282caa9
7bc6872
c7143fe
cb34670
d59d1c2
2f99708
f0d5024
5c85983
897297d
e774350
0fde9f8
2cfc20a
1e7c764
169f6b9
9cd0eb8
ef32bc4
6dc3ea7
d60acc8
e0305ff
814e325
83176bb
09c2f31
6f0a04a
2a21291
6d0c005
6b5411b
e3c195a
eaf04b5
15c75a1
26df483
f43da9e
0cfc36c
5402193
63e5688
7826be7
e926e59
1dfb66e
a7fe406
fd2c55a
d4036a4
9f7e580
49b6356
0544f73
5e38da0
df8ea06
b3775c1
bb26c99
2b62da5
8eb736a
fcc337b
4329947
108f9d1
5104720
805dfc7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking some more about this, the Context API from the OTEP differs in an important way from this implementation: it recommends a I think the addition of this abstraction might allow a more efficient underlying implementation. The OTEP describes two cross-cutting concerns (Observability and Correlations), while the Context API is generic enough to support additional concerns outside of the two specified. I think there is value in leveraging knowledge of the two specified concerns to improve lookup efficiency. For example, giving @observability = nil
@correlations = nil
@extensions = {}.freeze and copying the fields in My assumption here is that we can define a private class that contains all the relevant context for Observability and likewise for Correlations. For Correlations, that may well be just a frozen Hash. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The i went ahead and reverted back to my original frozen hash design to alleviate concerns over the linked list approach. I also implemented |
||
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. | ||
|
||
mwear marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# @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. | ||
|
||
mwear marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# @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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't read too far into this PR yet, so I may be missing some context (ha!) here. The design of this class makes me uncomfortable.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are all valid concerns and I share most of them. I don't think that I borrowed the single valued contexts idea from go. Take a look at line 480 until the end of the file: https://golang.org/src/context/context.go. You can also take a look at the hash based context I had earlier in this PR: a226469. Let me know if you'd be more comfortable with one over the other. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went ahead and removed |
||
|
||
ROOT = empty.freeze | ||
end | ||
end |
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 |
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' | ||
mwear marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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 |
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 |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still trying to wrap my head around this whole thing but I'm a little surprised to see this dep because the otep author seems to indicate
https://github.com/open-telemetry/oteps/pull/66/files#r359638018
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
Context
API is actually separate. That require is for namespacing reasons, so that all propagation code nests underOpenTelemetry::Context::Propagation
. SinceOpenTelemetry::Context
doesn't actually depend on anything inOpenTelemetry::Context::Propagation
, we could consider moving propagation up to the top level,OpenTelemetry::Propagation
. However, propagation does depend on context, so there is a relationship there.