-
Notifications
You must be signed in to change notification settings - Fork 375
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[OpenTracing] Scope and ScopeManager implementation (#473)
* Changed: Scope to expect Span and ScopeManager. * Added: Datadog::OpenTracer::ThreadLocalScope and Manager.
- Loading branch information
Showing
7 changed files
with
205 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,15 @@ | ||
module Datadog | ||
module OpenTracer | ||
# OpenTracing adapter for scope | ||
class Scope < ::OpenTracing::Scope | ||
attr_reader \ | ||
:manager, | ||
:span | ||
|
||
def initialize(manager:, span:) | ||
@manager = manager | ||
@span = span | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
module Datadog | ||
module OpenTracer | ||
# OpenTracing adapter for thread local scopes | ||
class ThreadLocalScope < Scope | ||
attr_reader \ | ||
:finish_on_close | ||
|
||
def initialize( | ||
manager:, | ||
span:, | ||
finish_on_close: true | ||
) | ||
super(manager: manager, span: span) | ||
@finish_on_close = finish_on_close | ||
@previous_scope = manager.active | ||
end | ||
|
||
# Mark the end of the active period for the current thread and Scope, | ||
# updating the ScopeManager#active in the process. | ||
# | ||
# NOTE: Calling close more than once on a single Scope instance leads to | ||
# undefined behavior. | ||
def close | ||
return unless equal?(manager.active) | ||
span.finish if finish_on_close | ||
manager.send(:set_scope, @previous_scope) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
module Datadog | ||
module OpenTracer | ||
# OpenTracing adapter for thread local scope management | ||
class ThreadLocalScopeManager < ScopeManager | ||
# Make a span instance active. | ||
# | ||
# @param span [Span] the Span that should become active | ||
# @param finish_on_close [Boolean] whether the Span should automatically be | ||
# finished when Scope#close is called | ||
# @return [Scope] instance to control the end of the active period for the | ||
# Span. It is a programming error to neglect to call Scope#close on the | ||
# returned instance. | ||
def activate(span, finish_on_close: true) | ||
ThreadLocalScope.new(manager: self, span: span).tap do |scope| | ||
set_scope(scope) | ||
end | ||
end | ||
|
||
# @return [Scope] the currently active Scope which can be used to access the | ||
# currently active Span. | ||
# | ||
# If there is a non-null Scope, its wrapped Span becomes an implicit parent | ||
# (as Reference#CHILD_OF) of any newly-created Span at Tracer#start_active_span | ||
# or Tracer#start_span time. | ||
def active | ||
Thread.current[object_id.to_s] | ||
end | ||
|
||
private | ||
|
||
def set_scope(scope) | ||
Thread.current[object_id.to_s] = scope | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
spec/ddtrace/opentracer/thread_local_scope_manager_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
require 'spec_helper' | ||
|
||
require 'ddtrace/opentracer' | ||
require 'ddtrace/opentracer/helper' | ||
|
||
if Datadog::OpenTracer.supported? | ||
RSpec.describe Datadog::OpenTracer::ThreadLocalScopeManager do | ||
include_context 'OpenTracing helpers' | ||
|
||
subject(:scope_manager) { described_class.new } | ||
|
||
describe '#activate' do | ||
subject(:activate) { scope_manager.activate(span, finish_on_close: finish_on_close) } | ||
let(:scope) { activate } | ||
let(:span) { instance_double(Datadog::OpenTracer::Span) } | ||
let(:finish_on_close) { true } | ||
it { is_expected.to be_a_kind_of(Datadog::OpenTracer::ThreadLocalScope) } | ||
it { expect(scope.manager).to be(scope_manager) } | ||
it { expect(scope.span).to be(span) } | ||
end | ||
|
||
describe '#activate' do | ||
subject(:active) { scope_manager.active } | ||
|
||
context 'when no scope has been activated' do | ||
it { is_expected.to be nil } | ||
end | ||
|
||
context 'when a scope has been activated' do | ||
let(:scope) { scope_manager.activate(span, finish_on_close: finish_on_close) } | ||
let(:span) { instance_double(Datadog::OpenTracer::Span) } | ||
let(:finish_on_close) { true } | ||
|
||
before(:each) { scope } # Activate a scope | ||
|
||
it { is_expected.to be(scope) } | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
require 'spec_helper' | ||
|
||
require 'ddtrace/opentracer' | ||
require 'ddtrace/opentracer/helper' | ||
|
||
if Datadog::OpenTracer.supported? | ||
RSpec.describe Datadog::OpenTracer::ThreadLocalScope do | ||
include_context 'OpenTracing helpers' | ||
|
||
subject(:scope) do | ||
described_class.new( | ||
manager: manager, | ||
span: span, | ||
finish_on_close: finish_on_close | ||
) | ||
end | ||
let(:manager) { Datadog::OpenTracer::ThreadLocalScopeManager.new } | ||
let(:span) { instance_double(Datadog::OpenTracer::Span) } | ||
let(:finish_on_close) { true } | ||
let(:previous_scope) { nil } | ||
|
||
before(:each) do | ||
allow(manager).to receive(:active) do | ||
# Unstub after first call | ||
allow(manager).to receive(:active).and_call_original | ||
previous_scope | ||
end | ||
end | ||
|
||
it { is_expected.to be_a_kind_of(Datadog::OpenTracer::Scope) } | ||
it { is_expected.to have_attributes(finish_on_close: finish_on_close) } | ||
|
||
describe '#close' do | ||
subject(:close) { scope.close } | ||
|
||
context 'when the scope is' do | ||
before(:each) do | ||
scope # Initialize the scope, to prevent overstubbing the previous stub | ||
allow(manager).to receive(:active).and_return(active_scope) | ||
end | ||
|
||
context 'active' do | ||
let(:active_scope) { scope } | ||
|
||
context 'and #finish_on_close' do | ||
context 'is true' do | ||
let(:finish_on_close) { true } | ||
|
||
it 'finishes the span and restores the previous scope' do | ||
expect(span).to receive(:finish) | ||
expect(manager).to receive(:set_scope).with(previous_scope) | ||
scope.close | ||
end | ||
end | ||
|
||
context 'is false' do | ||
let(:finish_on_close) { false } | ||
|
||
it 'does not finish the span but restores the previous scope' do | ||
expect(span).to_not receive(:finish) | ||
expect(manager).to receive(:set_scope).with(previous_scope) | ||
scope.close | ||
end | ||
end | ||
end | ||
end | ||
|
||
context 'not active' do | ||
let(:active_scope) { instance_double(described_class) } | ||
|
||
it 'should do nothing' do | ||
expect(span).to_not receive(:finish) | ||
expect(manager).to_not receive(:set_scope) | ||
scope.close | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |