signalfx_test_tracer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1c7975556c65bcae13576a0042f49d1f8e626f57c739a14e6a4e3cd8296cf5c5
4
+ data.tar.gz: 4b45314f7606a4bcdd88d790f7cd0405f79fb27a8fc250aa739439d957ab8588
5
+ SHA512:
6
+ metadata.gz: 174023e555261b6ace6c7685bad9ab7f9d3fd5033cef1ace14dfe662b56224fb7fd4f82ea61c7bfadc475543bd2d76990e8c303424e23a97d153b3843a5a162a
7
+ data.tar.gz: 867fe20b48d8b766b5f292fc525f82b2f1b43c2cf928331da0f8306d349d9409fb75d84edd0d0c195b54fb1cf2d2ee8312d7a32547efc78beb7b682f270cf61a
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /vendor/
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,46 @@
1
+ require: rubocop-rspec
2
+
3
+ Style/Documentation:
4
+ Enabled: no
5
+
6
+ Style/IfUnlessModifier:
7
+ Enabled: no
8
+
9
+ RSpec/NestedGroups:
10
+ Max: 4
11
+
12
+ RSpec/ExampleLength:
13
+ Enabled: no
14
+
15
+ RSpec/MultipleExpectations:
16
+ Enabled: no
17
+
18
+ RSpec/FilePath:
19
+ Enabled: no
20
+
21
+ Metrics/BlockLength:
22
+ Enabled: no
23
+
24
+ Metrics/MethodLength:
25
+ Enabled: no
26
+
27
+ Metrics/AbcSize:
28
+ Enabled: no
29
+
30
+ Metrics/ClassLength:
31
+ Enabled: no
32
+
33
+ Metrics/ParameterLists:
34
+ Enabled: no
35
+
36
+ Lint/UnusedMethodArgument:
37
+ Enabled: no
38
+
39
+ Style/FrozenStringLiteralComment:
40
+ Enabled: yes
41
+ EnforcedStyle: always
42
+ Include:
43
+ - 'lib/**/*'
44
+
45
+ Metrics/LineLength:
46
+ Max: 120
@@ -0,0 +1,43 @@
1
+ require: rubocop-rspec
2
+
3
+ Style/Documentation:
4
+ Enabled: no
5
+
6
+ Style/IfUnlessModifier:
7
+ Enabled: no
8
+
9
+ RSpec/NestedGroups:
10
+ Max: 4
11
+
12
+ RSpec/ExampleLength:
13
+ Enabled: no
14
+
15
+ RSpec/MultipleExpectations:
16
+ Enabled: no
17
+
18
+ Metrics/BlockLength:
19
+ Enabled: no
20
+
21
+ Metrics/MethodLength:
22
+ Enabled: no
23
+
24
+ Metrics/AbcSize:
25
+ Enabled: no
26
+
27
+ Metrics/ClassLength:
28
+ Enabled: no
29
+
30
+ Metrics/ParameterLists:
31
+ Enabled: no
32
+
33
+ Lint/UnusedMethodArgument:
34
+ Enabled: no
35
+
36
+ Style/FrozenStringLiteralComment:
37
+ Enabled: yes
38
+ EnforcedStyle: always
39
+ Include:
40
+ - 'lib/**/*'
41
+
42
+ Metrics/LineLength:
43
+ Max: 120
@@ -0,0 +1,7 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3
5
+ - 2.4
6
+ - 2.5
7
+ before_install: gem install bundler -v 1.15.3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in opentracing_ruby_tracer.gemspec
6
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "{}"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright {yyyy} {name of copyright owner}
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,66 @@
1
+ # OpenTracingTestTracer
2
+
3
+ OpenTracing compatible in-memory Tracer implementation. It exposes information about the recorded spans which are useful for testing.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'signalfx_test_tracer'
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ruby
16
+ require 'signalfx_test_tracer'
17
+ OpenTracing.global_tracer = OpenTracingTestTracer.build
18
+
19
+ OpenTracing.start_active_span('span name') do
20
+ # do something
21
+
22
+ OpenTracing.start_active_span('inner span name') do
23
+ # do something else
24
+ end
25
+ end
26
+ ```
27
+
28
+ See [opentracing-ruby](https://github.com/opentracing/opentracing-ruby) for more examples.
29
+
30
+ In addition to OpenTracing compatible methods this tracer provides following methods:
31
+
32
+ ### OpenTracingTestTracer
33
+
34
+ 1. `#spans` returns all spans, including those in progress.
35
+
36
+ ### OpenTracingTestTracer::Span
37
+
38
+ 1. `finished?` informs whether the span is finished.
39
+ 2. `start_time` returns when the span was started.
40
+ 3. `end_time` returns when the span was finished, or nil if still in progress.
41
+ 4. `tags` returns the span tags.
42
+ 5. `logs` returns the span logs.
43
+
44
+ ### OpenTracingTestTracer::SpanContext
45
+
46
+ 1. `trace_id` returns the trace ID.
47
+ 1. `span_id` returns the current span ID.
48
+ 2. `parent_id` returns the parent span ID.
49
+
50
+ ## Development
51
+
52
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
53
+
54
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
55
+
56
+ ## Contributing
57
+
58
+ Bug reports and pull requests are welcome on GitHub at https://github.com/salemove/test-ruby-opentracing
59
+
60
+ ## Credits
61
+
62
+ This gem is heavily inspired by @iaintshine [test-tracer](https://github.com/iaintshine/ruby-test-tracer).
63
+
64
+ **signalfx_test_tracer** is maintained and funded by [SaleMove, Inc].
65
+
66
+ [SaleMove, Inc]: http://salemove.com/ "SaleMove Website"
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ RuboCop::RakeTask.new(:rubocop)
10
+
11
+ task default: %i[rubocop spec]
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'signalfx_test_tracer'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'opentracing'
4
+ require 'logger'
5
+
6
+ require_relative 'signalfx_test_tracer/span'
7
+ require_relative 'signalfx_test_tracer/span_context'
8
+ require_relative 'signalfx_test_tracer/trace_id'
9
+ require_relative 'signalfx_test_tracer/scope_manager'
10
+ require_relative 'signalfx_test_tracer/scope'
11
+
12
+ class OpenTracingTestTracer
13
+ def self.build(logger: Logger.new(STDOUT))
14
+ new(logger: logger)
15
+ end
16
+
17
+ def initialize(logger:)
18
+ @logger = logger
19
+ @scope_manager = ScopeManager.new
20
+ @spans = []
21
+ @finished_spans = []
22
+ end
23
+
24
+ # NOTE: This is not OT compliant. This is meant only for testing.
25
+ attr_reader :spans
26
+
27
+ # @return [ScopeManager] the current ScopeManager, which may be a no-op but
28
+ # may not be nil.
29
+ attr_reader :scope_manager
30
+
31
+ # @return [Span, nil] the active span. This is a shorthand for
32
+ # `scope_manager.active.span`, and nil will be returned if
33
+ # Scope#active is nil.
34
+ def active_span
35
+ scope = scope_manager.active
36
+ scope&.span
37
+ end
38
+
39
+ # Starts a new span
40
+ #
41
+ # This is similar to #start_active_span, but the returned Span will not be
42
+ # registered via the ScopeManager.
43
+ #
44
+ # @param operation_name [String] The operation name for the Span
45
+ # @param child_of [SpanContext, Span] SpanContext that acts as a parent to
46
+ # the newly-started Span. If a Span instance is provided, its
47
+ # context is automatically substituted. See [Reference] for more
48
+ # information.
49
+ #
50
+ # If specified, the `references` parameter must be omitted.
51
+ # @param references [Array<Reference>] An array of reference
52
+ # objects that identify one or more parent SpanContexts.<Paste>
53
+ # @param start_time [Time] When the Span started, if not now
54
+ # @param tags [Hash] Tags to assign to the Span at start time
55
+ # @param ignore_active_scope [Boolean] whether to create an implicit
56
+ # References#CHILD_OF reference to the ScopeManager#active.
57
+ #
58
+ # @return [Span] The newly-started Span
59
+ def start_span(operation_name,
60
+ child_of: nil,
61
+ start_time: Time.now,
62
+ tags: nil,
63
+ references: nil,
64
+ ignore_active_scope: false,
65
+ **)
66
+ context = prepare_span_context(
67
+ child_of: child_of,
68
+ references: references,
69
+ ignore_active_scope: ignore_active_scope
70
+ )
71
+ span = Span.new(
72
+ context: context,
73
+ operation_name: operation_name,
74
+ start_time: start_time,
75
+ references: references,
76
+ tags: tags
77
+ )
78
+ @spans << span
79
+ span
80
+ end
81
+
82
+ # Creates a newly started and activated Scope
83
+ #
84
+ # If the Tracer's ScopeManager#active is not nil, no explicit references
85
+ # are provided, and `ignore_active_scope` is false, then an inferred
86
+ # References#CHILD_OF reference is created to the ScopeManager#active's
87
+ # SpanContext when start_active is invoked.
88
+ #
89
+ # @param operation_name [String] The operation name for the Span
90
+ # @param child_of [SpanContext, Span] SpanContext that acts as a parent to
91
+ # the newly-started Span. If a Span instance is provided, its
92
+ # context is automatically substituted. See [Reference] for more
93
+ # information.
94
+ #
95
+ # If specified, the `references` parameter must be omitted.
96
+ # @param references [Array<Reference>] An array of reference
97
+ # objects that identify one or more parent SpanContexts.
98
+ # @param start_time [Time] When the Span started, if not now
99
+ # @param tags [Hash] Tags to assign to the Span at start time
100
+ # @param ignore_active_scope [Boolean] whether to create an implicit
101
+ # References#CHILD_OF reference to the ScopeManager#active.
102
+ # @param finish_on_close [Boolean] whether span should automatically be
103
+ # finished when Scope#close is called
104
+ # @yield [Scope] If an optional block is passed to start_active it will
105
+ # yield the newly-started Scope. If `finish_on_close` is true then the
106
+ # Span will be finished automatically after the block is executed.
107
+ # @return [Scope] The newly-started and activated Scope
108
+ def start_active_span(operation_name,
109
+ child_of: nil,
110
+ references: nil,
111
+ start_time: Time.now,
112
+ tags: nil,
113
+ ignore_active_scope: false,
114
+ finish_on_close: true,
115
+ **)
116
+ span = start_span(
117
+ operation_name,
118
+ child_of: child_of,
119
+ references: references,
120
+ start_time: start_time,
121
+ tags: tags,
122
+ ignore_active_scope: ignore_active_scope
123
+ )
124
+ scope = @scope_manager.activate(span, finish_on_close: finish_on_close)
125
+
126
+ if block_given?
127
+ begin
128
+ yield scope
129
+ ensure
130
+ scope.close
131
+ end
132
+ end
133
+
134
+ scope
135
+ end
136
+
137
+ # Inject a SpanContext into the given carrier
138
+ #
139
+ # @param span_context [SpanContext]
140
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
141
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
142
+ def inject(span_context, format, carrier)
143
+ case format
144
+ when OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_RACK
145
+ carrier['test-traceid'] = span_context.trace_id
146
+ carrier['test-parentspanid'] = span_context.parent_id
147
+ carrier['test-spanid'] = span_context.span_id
148
+ else
149
+ @logger.error "#{format} format is not supported yet"
150
+ end
151
+ end
152
+
153
+ # Extract a SpanContext in the given format from the given carrier.
154
+ #
155
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
156
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
157
+ # @return [SpanContext] the extracted SpanContext or nil if none could be found
158
+ def extract(format, carrier)
159
+ case format
160
+ when OpenTracing::FORMAT_TEXT_MAP
161
+ trace_id = carrier['test-traceid']
162
+ parent_id = carrier['test-parentspanid']
163
+ span_id = carrier['test-spanid']
164
+
165
+ create_span_context(trace_id, span_id, parent_id)
166
+ when OpenTracing::FORMAT_RACK
167
+ trace_id = carrier['HTTP_TEST_TRACEID']
168
+ parent_id = carrier['HTTP_TEST_PARENTSPANID']
169
+ span_id = carrier['HTTP_TEST_SPANID']
170
+
171
+ create_span_context(trace_id, span_id, parent_id)
172
+ else
173
+ @logger.error "#{format} format is not supported yet"
174
+ nil
175
+ end
176
+ end
177
+
178
+ private
179
+
180
+ def create_span_context(trace_id, span_id, parent_id)
181
+ return nil if !trace_id || !span_id
182
+
183
+ SpanContext.new(
184
+ trace_id: trace_id,
185
+ parent_id: parent_id,
186
+ span_id: span_id
187
+ )
188
+ end
189
+
190
+ def prepare_span_context(child_of:, references:, ignore_active_scope:)
191
+ parent_context =
192
+ context_from_child_of(child_of) ||
193
+ context_from_references(references) ||
194
+ context_from_active_scope(ignore_active_scope)
195
+
196
+ if parent_context
197
+ SpanContext.create_from_parent_context(parent_context)
198
+ else
199
+ SpanContext.create_parent_context
200
+ end
201
+ end
202
+
203
+ def context_from_child_of(child_of)
204
+ return nil unless child_of
205
+ child_of.respond_to?(:context) ? child_of.context : child_of
206
+ end
207
+
208
+ def context_from_references(references)
209
+ return nil if !references || references.none?
210
+
211
+ # Prefer CHILD_OF reference if present
212
+ ref = references.detect do |reference|
213
+ reference.type == OpenTracing::Reference::CHILD_OF
214
+ end
215
+ (ref || references[0]).context
216
+ end
217
+
218
+ def context_from_active_scope(ignore_active_scope)
219
+ return if ignore_active_scope
220
+
221
+ active_scope = @scope_manager.active
222
+ active_scope&.span&.context
223
+ end
224
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OpenTracingTestTracer
4
+ # Scope represents an OpenTracing Scope
5
+ #
6
+ # See http://www.opentracing.io for more information.
7
+ class Scope
8
+ def initialize(span, scope_stack, finish_on_close:)
9
+ @span = span
10
+ @scope_stack = scope_stack
11
+ @finish_on_close = finish_on_close
12
+ @closed = false
13
+ end
14
+
15
+ # Return the Span scoped by this Scope
16
+ #
17
+ # @return [Span]
18
+ attr_reader :span
19
+
20
+ # Close scope
21
+ #
22
+ # Mark the end of the active period for the current thread and Scope,
23
+ # updating the ScopeManager#active in the process.
24
+ def close
25
+ raise "Tried to close already closed span: #{inspect}" if @closed
26
+ @closed = true
27
+
28
+ @span.finish if @finish_on_close
29
+ removed_scope = @scope_stack.pop
30
+
31
+ if removed_scope != self # rubocop:disable Style/GuardClause
32
+ raise 'Removed non-active scope, ' \
33
+ "removed: #{removed_scope.inspect}, "\
34
+ "expected: #{inspect}"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'scope_manager/scope_stack'
4
+ require_relative 'scope_manager/scope_identifier'
5
+
6
+ class OpenTracingTestTracer
7
+ # ScopeManager represents an OpenTracing ScopeManager
8
+ #
9
+ # See http://www.opentracing.io for more information.
10
+ #
11
+ # The ScopeManager interface abstracts both the activation of Span instances
12
+ # via ScopeManager#activate and access to an active Span/Scope via
13
+ # ScopeManager#active
14
+ #
15
+ class ScopeManager
16
+ def initialize
17
+ @scope_stack = ScopeStack.new
18
+ end
19
+
20
+ # Make a span instance active
21
+ #
22
+ # @param span [Span] the Span that should become active
23
+ # @param finish_on_close [Boolean] whether the Span should automatically be
24
+ # finished when Scope#close is called
25
+ # @return [Scope] instance to control the end of the active period for the
26
+ # Span. It is a programming error to neglect to call Scope#close on the
27
+ # returned instance.
28
+ def activate(span, finish_on_close: true)
29
+ return active if active && active.span == span
30
+ scope = Scope.new(span, @scope_stack, finish_on_close: finish_on_close)
31
+ @scope_stack.push(scope)
32
+ scope
33
+ end
34
+
35
+ # Return active scope
36
+ #
37
+ # If there is a non-null Scope, its wrapped Span becomes an implicit parent
38
+ # (as Reference#CHILD_OF) of any newly-created Span at
39
+ # Tracer#start_active_span or Tracer#start_span time.
40
+ #
41
+ # @return [Scope] the currently active Scope which can be used to access the
42
+ # currently active Span.
43
+ def active
44
+ @scope_stack.peek
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OpenTracingTestTracer
4
+ class ScopeManager
5
+ # @api private
6
+ class ScopeIdentifier
7
+ def self.generate
8
+ # 65..90.chr are characters between A and Z
9
+ "opentracing_#{(0...8).map { rand(65..90).chr }.join}".to_sym
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OpenTracingTestTracer
4
+ class ScopeManager
5
+ # @api private
6
+ class ScopeStack
7
+ def initialize
8
+ # Generate a random identifier to use as the Thread.current key. This is
9
+ # needed so that it would be possible to create multiple tracers in one
10
+ # thread (mostly useful for testing purposes)
11
+ @scope_identifier = ScopeIdentifier.generate
12
+ end
13
+
14
+ def push(scope)
15
+ store << scope
16
+ end
17
+
18
+ def pop
19
+ store.pop
20
+ end
21
+
22
+ def peek
23
+ store.last
24
+ end
25
+
26
+ private
27
+
28
+ def store
29
+ Thread.current[@scope_identifier] ||= []
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OpenTracingTestTracer
4
+ class Span
5
+ attr_accessor :operation_name
6
+
7
+ # NOTE: These are not OT compliant. These are meant only for testing.
8
+ attr_reader :context, :start_time, :tags, :logs, :references, :end_time
9
+
10
+ def initialize(
11
+ context:,
12
+ operation_name:,
13
+ start_time: Time.now,
14
+ tags: {},
15
+ references: nil
16
+ )
17
+ @context = context
18
+ @operation_name = operation_name
19
+ @start_time = start_time
20
+ @tags = tags
21
+ @logs = []
22
+ @references = references
23
+ end
24
+
25
+ # Set a tag value on this span
26
+ #
27
+ # @param key [String] the key of the tag
28
+ # @param value [String, Numeric, Boolean] the value of the tag. If it's not
29
+ # a String, Numeric, or Boolean it will be encoded with to_s
30
+ def set_tag(key, value)
31
+ sanitized_value = valid_tag_value?(value) ? value : value.to_s
32
+ @tags = @tags.merge(key.to_s => sanitized_value)
33
+ end
34
+
35
+ # Set a baggage item on the span
36
+ #
37
+ # @param key [String] the key of the baggage item
38
+ # @param value [String] the value of the baggage item
39
+ def set_baggage_item(_key, _value)
40
+ self
41
+ end
42
+
43
+ # Get a baggage item
44
+ #
45
+ # @param key [String] the key of the baggage item
46
+ #
47
+ # @return Value of the baggage item
48
+ def get_baggage_item(_key)
49
+ nil
50
+ end
51
+
52
+ # Add a log entry to this span
53
+ #
54
+ # @deprecated Use {#log_kv} instead.
55
+ def log(*args)
56
+ warn 'Span#log is deprecated. Please use Span#log_kv instead.'
57
+ log_kv(*args)
58
+ end
59
+
60
+ # Add a log entry to this span
61
+ #
62
+ # @param timestamp [Time] time of the log
63
+ # @param fields [Hash] Additional information to log
64
+ def log_kv(timestamp: Time.now, **fields)
65
+ @logs << fields.merge(timestamp: timestamp)
66
+ nil
67
+ end
68
+
69
+ # Finish the {Span}
70
+ #
71
+ # @param end_time [Time] custom end time, if not now
72
+ def finish(end_time: Time.now)
73
+ @end_time = end_time
74
+ end
75
+
76
+ def to_s
77
+ "Span(operation_name=#{@operation_name}, " \
78
+ "tags=#{@tags}, " \
79
+ "logs=#{@logs}, " \
80
+ "start_time=#{@start_time}, " \
81
+ "end_time=#{@end_time}, " \
82
+ "context=#{@context})"
83
+ end
84
+
85
+ # NOTE: This is not OT compliant. This is meant only for testing.
86
+ def finished?
87
+ @end_time != nil
88
+ end
89
+
90
+ private
91
+
92
+ def valid_tag_value?(value)
93
+ value.is_a?(String) ||
94
+ value.is_a?(Numeric) ||
95
+ value.is_a?(TrueClass) ||
96
+ value.is_a?(FalseClass)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OpenTracingTestTracer
4
+ # SpanContext holds the data for a span that gets inherited to child spans
5
+ class SpanContext
6
+ def self.create_parent_context
7
+ trace_id = TraceId.generate
8
+ new(trace_id: trace_id, span_id: trace_id)
9
+ end
10
+
11
+ def self.create_from_parent_context(span_context)
12
+ new(
13
+ span_id: TraceId.generate,
14
+ parent_id: span_context.span_id,
15
+ trace_id: span_context.trace_id
16
+ )
17
+ end
18
+
19
+ attr_reader :span_id, :parent_id, :trace_id, :baggage
20
+
21
+ def initialize(span_id:, parent_id: nil, trace_id:, baggage: {})
22
+ @span_id = span_id
23
+ @parent_id = parent_id
24
+ @trace_id = trace_id
25
+ @baggage = baggage
26
+ end
27
+
28
+ def to_s
29
+ "SpanContext(trace_id=#{@trace_id}, " \
30
+ "span_id=#{@span_id}, " \
31
+ "parent_id=#{@parent_id}, " \
32
+ "baggage=#{@baggage})"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OpenTracingTestTracer
4
+ module TraceId
5
+ TRACE_ID_UPPER_BOUND = 2**64
6
+
7
+ def self.generate
8
+ rand(TRACE_ID_UPPER_BOUND).to_s(16)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'signalfx_test_tracer'
8
+ spec.version = '0.1.0'
9
+ spec.authors = ['SignalFx Engineers']
10
+ spec.email = ['eng@signalfx.com']
11
+
12
+ spec.summary = 'SignalFx OpenTracing Tracer implementation for Tests in Ruby'
13
+ spec.description = ''
14
+ spec.homepage = 'https://github.com/signalfx/test-ruby-opentracing'
15
+ spec.license = 'Apache-2.0'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 2.1'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rspec', '~> 3.0'
26
+ spec.add_development_dependency 'rubocop', '~> 0.54.0'
27
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.24.0'
28
+
29
+ spec.add_dependency 'opentracing'
30
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: signalfx_test_tracer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - SignalFx Engineers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-11-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.54.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.54.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.24.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.24.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: opentracing
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: ''
98
+ email:
99
+ - eng@signalfx.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".rubocop.yml"
107
+ - ".rubycop.yml"
108
+ - ".travis.yml"
109
+ - Gemfile
110
+ - LICENSE
111
+ - README.md
112
+ - Rakefile
113
+ - bin/console
114
+ - bin/setup
115
+ - lib/signalfx_test_tracer.rb
116
+ - lib/signalfx_test_tracer/scope.rb
117
+ - lib/signalfx_test_tracer/scope_manager.rb
118
+ - lib/signalfx_test_tracer/scope_manager/scope_identifier.rb
119
+ - lib/signalfx_test_tracer/scope_manager/scope_stack.rb
120
+ - lib/signalfx_test_tracer/span.rb
121
+ - lib/signalfx_test_tracer/span_context.rb
122
+ - lib/signalfx_test_tracer/trace_id.rb
123
+ - opentracing_test_tracer.gemspec
124
+ homepage: https://github.com/signalfx/test-ruby-opentracing
125
+ licenses:
126
+ - Apache-2.0
127
+ metadata: {}
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubygems_version: 3.1.2
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: SignalFx OpenTracing Tracer implementation for Tests in Ruby
147
+ test_files: []