honeycomb-beeline 0.8.0 → 1.0.0.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cdebba0519801e3f125eb26df5e3afc711530a7a
4
- data.tar.gz: ac7f51c5a742ee3227ff2b110678558b4477c89f
2
+ SHA256:
3
+ metadata.gz: 7b8fe19de846820875150addea20fdf579e10245876d110ba705697f34247ee1
4
+ data.tar.gz: 3ba4a8a9624a211d953d5e54e68c35c961f277c8000fa93ed1d73ab8bee8153b
5
5
  SHA512:
6
- metadata.gz: c1fb07e774f58c5e7e059f140b925d50188a2a3a39f1cdd06bbd4579214f5032da14d82c22fcfa262b29089fd7b7c7a379b4f6106e56cfb92aa211b9b7511172
7
- data.tar.gz: 1db4256ea0f39086ce715e167ad770312ea6955e646b1cd9c4edb9770c391da6e725c49bcbf9d1f410e63516c740a9a4507cfa962f0572c88ced3e25753fe442
6
+ metadata.gz: 76113e85b1549790e59add2780b73c7c32e1eaa542779af3886932253a32e964617053dddd6173366511b17307e55d2462b01bfc8981bd8d933da0b40aee2064
7
+ data.tar.gz: 2cc961e948616f7dbefc5a4cba143555b243c63728cf0d4297976cc812a26f352fb714a7f94ada3de3ce38bbce3dee3292ec823c2b821c233718d49a354acd2e
metadata CHANGED
@@ -1,73 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: honeycomb-beeline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 1.0.0.pre.alpha
5
5
  platform: ruby
6
6
  authors:
7
- - Sam Stokes
7
+ - Martin Holman
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-06 00:00:00.000000000 Z
11
+ date: 2019-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: libhoney
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 1.8.1
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: 1.8.1
27
- - !ruby/object:Gem::Dependency
28
- name: activerecord-honeycomb
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 0.4.0
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
17
+ - - "~>"
39
18
  - !ruby/object:Gem::Version
40
- version: 0.4.0
41
- - !ruby/object:Gem::Dependency
42
- name: rack-honeycomb
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: 0.4.0
19
+ version: '1.8'
48
20
  type: :runtime
49
21
  prerelease: false
50
22
  version_requirements: !ruby/object:Gem::Requirement
51
23
  requirements:
52
- - - ">="
24
+ - - "~>"
53
25
  - !ruby/object:Gem::Version
54
- version: 0.4.0
26
+ version: '1.8'
55
27
  - !ruby/object:Gem::Dependency
56
- name: faraday-honeycomb
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: 0.3.0
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: 0.3.0
69
- - !ruby/object:Gem::Dependency
70
- name: activerecord
28
+ name: appraisal
71
29
  requirement: !ruby/object:Gem::Requirement
72
30
  requirements:
73
31
  - - ">="
@@ -95,7 +53,7 @@ dependencies:
95
53
  - !ruby/object:Gem::Version
96
54
  version: '0'
97
55
  - !ruby/object:Gem::Dependency
98
- name: faraday
56
+ name: bundler
99
57
  requirement: !ruby/object:Gem::Requirement
100
58
  requirements:
101
59
  - - ">="
@@ -109,21 +67,21 @@ dependencies:
109
67
  - !ruby/object:Gem::Version
110
68
  version: '0'
111
69
  - !ruby/object:Gem::Dependency
112
- name: pg
70
+ name: overcommit
113
71
  requirement: !ruby/object:Gem::Requirement
114
72
  requirements:
115
- - - ">="
73
+ - - "~>"
116
74
  - !ruby/object:Gem::Version
117
- version: '0'
75
+ version: 0.46.0
118
76
  type: :development
119
77
  prerelease: false
120
78
  version_requirements: !ruby/object:Gem::Requirement
121
79
  requirements:
122
- - - ">="
80
+ - - "~>"
123
81
  - !ruby/object:Gem::Version
124
- version: '0'
82
+ version: 0.46.0
125
83
  - !ruby/object:Gem::Dependency
126
- name: rack
84
+ name: pry-byebug
127
85
  requirement: !ruby/object:Gem::Requirement
128
86
  requirements:
129
87
  - - ">="
@@ -137,21 +95,7 @@ dependencies:
137
95
  - !ruby/object:Gem::Version
138
96
  version: '0'
139
97
  - !ruby/object:Gem::Dependency
140
- name: rack-test
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: '0'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
- - !ruby/object:Gem::Dependency
154
- name: rails
98
+ name: rake
155
99
  requirement: !ruby/object:Gem::Requirement
156
100
  requirements:
157
101
  - - ">="
@@ -165,49 +109,49 @@ dependencies:
165
109
  - !ruby/object:Gem::Version
166
110
  version: '0'
167
111
  - !ruby/object:Gem::Dependency
168
- name: rake
112
+ name: rspec
169
113
  requirement: !ruby/object:Gem::Requirement
170
114
  requirements:
171
- - - ">="
115
+ - - "~>"
172
116
  - !ruby/object:Gem::Version
173
- version: '0'
117
+ version: '3.0'
174
118
  type: :development
175
119
  prerelease: false
176
120
  version_requirements: !ruby/object:Gem::Requirement
177
121
  requirements:
178
- - - ">="
122
+ - - "~>"
179
123
  - !ruby/object:Gem::Version
180
- version: '0'
124
+ version: '3.0'
181
125
  - !ruby/object:Gem::Dependency
182
- name: rspec
126
+ name: rubocop
183
127
  requirement: !ruby/object:Gem::Requirement
184
128
  requirements:
185
- - - ">="
129
+ - - "<"
186
130
  - !ruby/object:Gem::Version
187
- version: '0'
131
+ version: '0.69'
188
132
  type: :development
189
133
  prerelease: false
190
134
  version_requirements: !ruby/object:Gem::Requirement
191
135
  requirements:
192
- - - ">="
136
+ - - "<"
193
137
  - !ruby/object:Gem::Version
194
- version: '0'
138
+ version: '0.69'
195
139
  - !ruby/object:Gem::Dependency
196
- name: sinatra
140
+ name: rubocop-performance
197
141
  requirement: !ruby/object:Gem::Requirement
198
142
  requirements:
199
- - - "<="
143
+ - - "<"
200
144
  - !ruby/object:Gem::Version
201
- version: 2.0.4
145
+ version: 1.3.0
202
146
  type: :development
203
147
  prerelease: false
204
148
  version_requirements: !ruby/object:Gem::Requirement
205
149
  requirements:
206
- - - "<="
150
+ - - "<"
207
151
  - !ruby/object:Gem::Version
208
- version: 2.0.4
152
+ version: 1.3.0
209
153
  - !ruby/object:Gem::Dependency
210
- name: sequel
154
+ name: simplecov
211
155
  requirement: !ruby/object:Gem::Requirement
212
156
  requirements:
213
157
  - - ">="
@@ -221,21 +165,21 @@ dependencies:
221
165
  - !ruby/object:Gem::Version
222
166
  version: '0'
223
167
  - !ruby/object:Gem::Dependency
224
- name: sequel-honeycomb
168
+ name: simplecov-console
225
169
  requirement: !ruby/object:Gem::Requirement
226
170
  requirements:
227
171
  - - ">="
228
172
  - !ruby/object:Gem::Version
229
- version: 0.4.0
173
+ version: '0'
230
174
  type: :development
231
175
  prerelease: false
232
176
  version_requirements: !ruby/object:Gem::Requirement
233
177
  requirements:
234
178
  - - ">="
235
179
  - !ruby/object:Gem::Version
236
- version: 0.4.0
180
+ version: '0'
237
181
  - !ruby/object:Gem::Dependency
238
- name: yard
182
+ name: webmock
239
183
  requirement: !ruby/object:Gem::Requirement
240
184
  requirements:
241
185
  - - ">="
@@ -248,26 +192,18 @@ dependencies:
248
192
  - - ">="
249
193
  - !ruby/object:Gem::Version
250
194
  version: '0'
251
- description: |2
252
- The Honeycomb Beeline for Ruby is the fastest path to observability for your
253
- Ruby apps. It understands the common packages you use and automatically
254
- instruments them to send useful events to Honeycomb.
195
+ description:
255
196
  email:
256
- - support@honeycomb.io
197
+ - martin@honeycomb.io
257
198
  executables: []
258
199
  extensions: []
259
200
  extra_rdoc_files: []
260
- files:
261
- - README.md
262
- - lib/honeycomb-beeline.rb
263
- - lib/honeycomb/beeline/version.rb
264
- - lib/honeycomb/client.rb
265
- - lib/honeycomb/instrumentations.rb
266
- - lib/honeycomb/span.rb
267
- homepage: https://github.com/honeycombio/beeline-ruby
268
- licenses:
269
- - Apache-2.0
270
- metadata: {}
201
+ files: []
202
+ homepage: https://honeycomb.io
203
+ licenses: []
204
+ metadata:
205
+ homepage_uri: https://honeycomb.io
206
+ source_code_uri: https://github.com/honeycombio/beeline-ruby
271
207
  post_install_message:
272
208
  rdoc_options: []
273
209
  require_paths:
@@ -279,12 +215,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
279
215
  version: 2.2.0
280
216
  required_rubygems_version: !ruby/object:Gem::Requirement
281
217
  requirements:
282
- - - ">="
218
+ - - ">"
283
219
  - !ruby/object:Gem::Version
284
- version: '0'
220
+ version: 1.3.1
285
221
  requirements: []
286
- rubyforge_project:
287
- rubygems_version: 2.6.14.4
222
+ rubygems_version: 3.0.4
288
223
  signing_key:
289
224
  specification_version: 4
290
225
  summary: Instrument your Ruby apps with Honeycomb
data/README.md DELETED
@@ -1,36 +0,0 @@
1
- # Honeycomb Beeline for Ruby
2
-
3
- [![Build Status](https://travis-ci.org/honeycombio/beeline-ruby.svg?branch=master)](https://travis-ci.org/honeycombio/beeline-ruby)
4
- [![Gem Version](https://badge.fury.io/rb/honeycomb-beeline.svg)](https://badge.fury.io/rb/honeycomb-beeline)
5
-
6
- This package makes it easy to instrument your Ruby web app to send useful events to [Honeycomb](https://www.honeycomb.io), a service for debugging your software in production.
7
- - [Usage and Examples](https://docs.honeycomb.io/getting-data-in/beelines/ruby-beeline/)
8
-
9
- Sign up for a [Honeycomb
10
- trial](https://ui.honeycomb.io/signup) to obtain an API key before starting.
11
-
12
- ## Compatible with
13
-
14
- Requires Ruby version 2.2 or later
15
-
16
- Built in instrumentation for:
17
-
18
- - Active Record 4 or later
19
- - Rails 4 or later
20
- - Faraday 0.8 or later
21
- - Sequel
22
- - Rack 1 or greater
23
-
24
- ## Get in touch
25
-
26
- Please reach out to [support@honeycomb.io](mailto:support@honeycomb.io) or ping
27
- us with the chat bubble on [our website](https://www.honeycomb.io) for any
28
- assistance. We also welcome [bug reports](https://github.com/honeycombio/beeline-ruby/issues).
29
-
30
- ## Contributions
31
-
32
- Features, bug fixes and other changes to `beeline-ruby` are gladly accepted. Please
33
- open issues or a pull request with your change. Remember to add your name to the
34
- CONTRIBUTORS file!
35
-
36
- All contributions will be released under the Apache License 2.0.
@@ -1,13 +0,0 @@
1
- # Main entrypoint for the 'honeycomb-beeline' gem.
2
-
3
- require 'libhoney'
4
-
5
- # Namespace for the Honeycomb Beeline.
6
- #
7
- # Call {.init} to initialize the Honeycomb Beeline at app startup.
8
- module Honeycomb
9
- end
10
-
11
- require 'honeycomb/client'
12
- require 'honeycomb/instrumentations'
13
- require 'honeycomb/span'
@@ -1,6 +0,0 @@
1
- module Honeycomb
2
- module Beeline
3
- GEM_NAME = 'honeycomb-beeline'
4
- VERSION = '0.8.0'
5
- end
6
- end
@@ -1,180 +0,0 @@
1
- require 'honeycomb/beeline/version'
2
-
3
- require 'libhoney'
4
-
5
- require 'logger'
6
- require 'socket'
7
-
8
- module Honeycomb
9
- USER_AGENT_SUFFIX = "#{Beeline::GEM_NAME}/#{Beeline::VERSION}"
10
-
11
- class << self
12
- attr_reader :client
13
- attr_reader :logger
14
- attr_reader :service_name
15
-
16
- # Initialize the Honeycomb Beeline. You should call this only once, as
17
- # early as possible in your app's startup process, to automatically
18
- # instrument your app and prepare to send events.
19
- #
20
- # @example Providing config in code
21
- # Honeycomb.init(
22
- # writekey: '0123face4567cafe8901beef2345feed',
23
- # dataset: 'myapp-development',
24
- # service_name: 'myapp'
25
- # )
26
- # @example Providing config via environment variables
27
- # # Assumes environment variables e.g.
28
- # # HONEYCOMB_WRITEKEY=0123face4567cafe8901beef2345feed
29
- # # HONEYCOMB_DATASET=myapp-development
30
- # # HONEYCOMB_SERVICE=myapp
31
- #
32
- # Honeycomb.init
33
- #
34
- # @param writekey [String] (required) the Honeycomb API key (aka "write
35
- # key") - get yours from your {https://ui.honeycomb.io/account Account
36
- # Page}. Can also be specified via the HONEYCOMB_WRITEKEY environment
37
- # variable.
38
- # @param dataset [String] (required) the name of the Honeycomb
39
- # {https://docs.honeycomb.io/getting-data-in/datasets/best-practices/
40
- # dataset} your app should send events to. Can also be specified via the
41
- # HONEYCOMB_DATASET environment variable.
42
- # @param service_name [String] the name of your app, included in all events
43
- # your app will send. Defaults to the dataset name if not specified. Can
44
- # also be specified via the HONEYCOMB_SERVICE environment variable.
45
- # @param debug [Boolean] if true, your app will not send any events to
46
- # Honeycomb, but will instead print them to your app's standard error.
47
- # It will also log diagnostic messages to standard error.
48
- # @param logger [Logger] provide a logger to receive diagnostic messages,
49
- # e.g. to override the default logging device or verbosity. By default
50
- # warnings and above will be logged to standard error; under normal
51
- # operation nothing will be logged.
52
- def init(
53
- writekey: ENV['HONEYCOMB_WRITEKEY'],
54
- dataset: ENV['HONEYCOMB_DATASET'],
55
- service_name: ENV['HONEYCOMB_SERVICE'] || dataset,
56
- without: [],
57
- debug: ENV.key?('HONEYCOMB_DEBUG'),
58
- logger: nil,
59
- **options
60
- )
61
- reset
62
-
63
- @without = without
64
- @service_name = service_name
65
- @logger = logger || Logger.new($stderr).tap {|l| l.level = Logger::WARN }
66
- @debug = debug
67
- if debug
68
- @logger ||= Logger.new($stderr)
69
- end
70
-
71
- options = options.merge(writekey: writekey, dataset: dataset)
72
- @client = new_client(options)
73
-
74
- after_init_hooks.each do |label, block|
75
- debug "Running hook '#{label}' after Honeycomb.init"
76
- run_hook(label, block)
77
- end
78
-
79
- @initialized = true
80
- end
81
-
82
- # Call at app shutdown to ensure all pending events have been sent to
83
- # Honeycomb.
84
- def shutdown
85
- if defined?(@client) && @client
86
- @client.close
87
- end
88
- end
89
-
90
- # Reset the Beeline to a pristine state, ready to be `.init`ed again.
91
- # Intended for testing purposes only.
92
- #
93
- # @api private
94
- def reset
95
- # TODO encapsulate all this into a Beeline object so we don't need
96
- # explicit cleanup
97
-
98
- shutdown
99
-
100
- @logger = nil
101
- @without = nil
102
- @service_name = nil
103
- @debug = nil
104
- @client = nil
105
- @initialized = false
106
- end
107
-
108
- private
109
- def debug(msg)
110
- logger.debug msg if logger
111
- end
112
- def info(msg)
113
- logger.info msg if logger
114
- end
115
- def warn(msg)
116
- logger.warn msg if logger
117
- end
118
-
119
- def after_init_hooks
120
- @after_init_hooks ||= []
121
- end
122
-
123
- def new_client(options)
124
- client = options.delete :client
125
-
126
- options = {user_agent_addition: USER_AGENT_SUFFIX}.merge(options)
127
- if @debug
128
- raise ArgumentError, "can't specify both client and debug options", caller if client
129
- info 'logging events to standard error instead of sending to Honeycomb'
130
- client = Libhoney::LogClient.new(verbose: true, **options)
131
- else
132
- client ||= if options[:writekey] && options[:dataset]
133
- Libhoney::Client.new(options)
134
- else
135
- warn "#{self.name}: no #{options[:writekey] ? 'dataset' : 'writekey'} configured, disabling sending events"
136
- Libhoney::NullClient.new(options)
137
- end
138
- end
139
- client.add_field 'meta.beeline_version', Beeline::VERSION
140
- client.add_field 'meta.local_hostname', Socket.gethostname rescue nil
141
- client.add_field 'service_name', @service_name
142
- client
143
- end
144
-
145
- def after_init(label, &block)
146
- raise ArgumentError unless block_given?
147
-
148
- hook = if block.arity == 0
149
- ->(_, _) { block.call }
150
- elsif block.arity == 1
151
- ->(client, _) { block.call client }
152
- elsif block.arity > 2
153
- raise ArgumentError, 'Honeycomb.after_init block should take 2 arguments'
154
- else
155
- block
156
- end
157
-
158
- if defined?(@initialized)
159
- debug "Running hook '#{label}' as Honeycomb already initialized"
160
- run_hook(label, hook)
161
- else
162
- after_init_hooks << [label, hook]
163
- end
164
- end
165
-
166
- def run_hook(label, block)
167
- if @without.include?(label)
168
- debug "Skipping hook '#{label}' due to opt-out"
169
- else
170
- block.call @client, @logger
171
- end
172
- rescue => e
173
- warn "Honeycomb.init hook '#{label}' raised #{e.class}: #{e}"
174
- end
175
- end
176
-
177
- after_init :log do |_, logger|
178
- logger.info "Honeycomb inited" if logger
179
- end
180
- end
@@ -1,25 +0,0 @@
1
- require 'honeycomb/client'
2
-
3
- require 'activerecord-honeycomb/auto_install'
4
- require 'faraday-honeycomb/auto_install'
5
- require 'rack-honeycomb/auto_install'
6
-
7
- module Honeycomb
8
- INSTRUMENTATIONS = [
9
- ActiveRecord::Honeycomb,
10
- Faraday::Honeycomb,
11
- Rack::Honeycomb,
12
- ].freeze
13
-
14
- INSTRUMENTATIONS.each do |instrumentation|
15
- auto = instrumentation::AutoInstall
16
- hook_label = instrumentation.name.sub(/::Honeycomb$/, '').downcase.to_sym
17
- after_init(hook_label) do |client, logger|
18
- if auto.available?(logger: logger)
19
- auto.auto_install!(honeycomb_client: client, logger: logger)
20
- else
21
- logger.debug "Not autoinitialising #{instrumentation.name}" if logger
22
- end
23
- end
24
- end
25
- end
@@ -1,295 +0,0 @@
1
- require 'base64'
2
- require 'json'
3
- require 'securerandom'
4
-
5
- module Honeycomb
6
- class << self
7
- # Start a new trace. Calling {.span} will automatically start a new trace if
8
- # one is not already active, so you do not need to call this explicitly
9
- # unless you want to specify the trace id or parent span id (e.g. to
10
- # propagate a trace id received from upstream).
11
- def trace(trace_id: nil, parent_span_id: nil, context: {}, **extra_context)
12
- context = context.merge(extra_context)
13
-
14
- with_trace trace_id: trace_id, parent_span_id: parent_span_id, context: context do
15
- yield
16
- end
17
- end
18
-
19
- # Continue a trace from a serialized trace context, e.g. propagated from
20
- # another process.
21
- def trace_from_encoded_context(encoded_context = nil, additional_context: {})
22
- trace_context = decode_trace_context(encoded_context) || {}
23
- trace_id = trace_context[:trace_id]
24
- parent_span_id = trace_context[:parent_span_id]
25
- context = trace_context[:context] || {}
26
-
27
- trace(trace_id: trace_id, parent_span_id: parent_span_id, context: context.merge(additional_context)) do
28
- yield
29
- end
30
- end
31
-
32
- # Start a new span, and send it at the end of the supplied code block. This
33
- # will start a new trace if one is not already active.
34
- def span(name = nil, type: 'app', fields: {}, **extra_fields)
35
- fields = fields.merge(extra_fields)
36
-
37
- start = nil
38
-
39
- event = client.event
40
- span_for_existing_event(event, name: name, type: type) do |span_id, trace_id|
41
- fields.each do |field, value|
42
- event.add_field "app.#{field}", value
43
- end
44
-
45
- start = Time.now
46
-
47
- yield span_id, trace_id, event
48
- end
49
- rescue Exception => e
50
- if event
51
- # TODO what should the prefix be?
52
- event.add_field 'app.error', e.class.name
53
- event.add_field 'app.error_detail', e.message
54
- end
55
- raise
56
- ensure
57
- if start && event
58
- finish = Time.now
59
- duration = finish - start
60
- event.add_field 'duration_ms', duration * 1000
61
- event.send
62
- end
63
- end
64
-
65
- # Start a new span, and annotate an existing {Libhoney::Event} with its
66
- # tracing fields. Most users should call {.span} instead, since it has
67
- # simpler semantics (e.g. it will time the execution of the code block for
68
- # you, record any exceptions that were thrown, and send the event at the end
69
- # of the code block). This method is mainly useful if you are writing a
70
- # library instrumentation which needs to also work independently of the
71
- # Beeline, and which therefore needs to implement those semantics itself; or
72
- # which needs custom error handling, e.g. adding custom fields in case of
73
- # error.
74
- def span_for_existing_event(event, name:, type:)
75
- with_trace do |trace_id, context|
76
- with_span do |parent_span_id, span_id|
77
- event.add_field 'trace.trace_id', trace_id
78
- event.add_field 'trace.parent_id', parent_span_id if parent_span_id
79
- event.add_field 'trace.span_id', span_id
80
- event.add_field 'name', name if name
81
- event.add_field 'type', type
82
-
83
- context.each do |field, value|
84
- event.add_field "app.#{field}", value
85
- end
86
-
87
- yield span_id, trace_id
88
- end
89
- end
90
- end
91
-
92
- # Add a trace field, which will get added to all spans sent after this call.
93
- def add_trace_field(name, value)
94
- self.active_trace_context[name] = value
95
- # TODO right now this will only add the field to all spans *started* after
96
- # this call, which unfortunately excludes the actual active span when the
97
- # call was made. One way to fix this is to change .span_for_existing_event
98
- # to add fields from .active_trace_context _after_ the yield (in a
99
- # begin/ensure block) instead of before.
100
- end
101
-
102
- def decode_trace_context(encoded_context)
103
- return nil unless encoded_context
104
- version, payload = encoded_context.split(';', 2)
105
- case version
106
- when '1'
107
- decode_payload_v1(payload)
108
- else
109
- warn "#{self}.decode_trace_context: unrecognized trace context version #{version.inspect}"
110
- nil
111
- end
112
- end
113
-
114
- def encode_trace_context_v1(trace_id, parent_span_id, context)
115
- version = 1
116
-
117
- encoded_payload = encode_payload_v1(
118
- trace_id: trace_id,
119
- parent_id: parent_span_id,
120
- context: context,
121
- )
122
-
123
- "#{version};#{encoded_payload}"
124
- end
125
- alias encode_trace_context encode_trace_context_v1
126
-
127
- def active_trace_id
128
- Thread.current[:honeycomb_trace_id]
129
- end
130
- def active_trace_id=(trace_id)
131
- Thread.current[:honeycomb_trace_id] = trace_id
132
- end
133
-
134
- def active_parent_span_id
135
- Thread.current[:honeycomb_parent_span_id]
136
- end
137
- def active_parent_span_id=(parent_span_id)
138
- Thread.current[:honeycomb_parent_span_id] = parent_span_id
139
- end
140
-
141
- def active_trace_context
142
- Thread.current[:honeycomb_trace_context]
143
- end
144
- def active_trace_context=(trace_context)
145
- Thread.current[:honeycomb_trace_context] = trace_context
146
- end
147
-
148
- private
149
- def with_trace(trace_id: nil, parent_span_id: nil, context: nil)
150
- if self.active_trace_id
151
- if trace_id
152
- warn "#{self}.with_trace called while another trace is already active; ignoring supplied trace_id and preserving existing one"
153
- end
154
- yield self.active_trace_id, self.active_trace_context
155
- else
156
- begin
157
- trace_id, context = start_trace!(trace_id: trace_id, parent_span_id: parent_span_id, context: context)
158
-
159
- yield trace_id, context
160
- ensure
161
- finish_trace!
162
- end
163
- end
164
- end
165
-
166
- def start_trace!(trace_id: nil, parent_span_id: nil, context: nil)
167
- raise "#{self}.start_trace! called while another trace is already active" if self.active_trace_id
168
-
169
- trace_id ||= SecureRandom.uuid
170
- self.active_trace_id = trace_id
171
-
172
- self.active_parent_span_id = parent_span_id if parent_span_id
173
-
174
- context ||= {}
175
- self.active_trace_context = context
176
-
177
- [trace_id, context]
178
- end
179
-
180
- def finish_trace!
181
- self.active_trace_id = nil
182
- self.active_parent_span_id = nil
183
- self.active_trace_context = nil
184
- end
185
-
186
- def with_span
187
- parent_span_id, span_id = start_span!
188
-
189
- yield parent_span_id, span_id
190
- ensure
191
- finish_span!(parent_span_id)
192
- end
193
-
194
- def start_span!
195
- span_id = SecureRandom.uuid
196
-
197
- parent_span_id = self.active_parent_span_id
198
- self.active_parent_span_id = span_id
199
-
200
- return parent_span_id, span_id
201
- end
202
-
203
- def finish_span!(parent_span_id)
204
- self.active_parent_span_id = parent_span_id
205
- end
206
-
207
- def decode_payload_v1(encoded_payload)
208
- trace_id, parent_span_id, context = nil
209
-
210
- encoded_payload.split(',').each do |entry|
211
- k, v = entry.split('=', 2)
212
- case k
213
- when 'trace_id'
214
- trace_id = v
215
- when 'parent_id'
216
- parent_span_id = v
217
- when 'context'
218
- context = decode_payload_context_v1(v)
219
- else
220
- debug "#{self}.decode_payload_v1: unrecognized payload key #{k.inspect}"
221
- end
222
- end
223
-
224
- if trace_id.nil?
225
- warn "#{self}.decode_payload_v1: no trace_id in context"
226
- return nil
227
- elsif parent_span_id.nil?
228
- warn "#{self}.decode_payload_v1: no parent_id in context"
229
- return nil
230
- end
231
-
232
- payload = {
233
- trace_id: trace_id,
234
- parent_span_id: parent_span_id,
235
- }
236
- payload[:context] = context if context
237
- payload
238
- rescue StandardError => e
239
- warn "#{self}.decode_payload_v1: encountered #{e.class} decoding payload: #{e}"
240
- nil
241
- end
242
-
243
- def decode_payload_context_v1(encoded_payload_context)
244
- return {} if encoded_payload_context.empty?
245
- json = Base64.decode64(encoded_payload_context)
246
- JSON.parse(json)
247
- end
248
-
249
- def encode_payload_v1(payload_parts)
250
- payload_parts.map do |k, v|
251
- encoded_part = encode_payload_part_v1(k, v)
252
- encoded_part ? "#{k}=#{encoded_part}" : nil
253
- end
254
- .compact # strip out parts that failed to encode
255
- .join(',')
256
- end
257
-
258
- def encode_payload_part_v1(param, value)
259
- case param
260
- when :trace_id, :parent_id
261
- encode_payload_id_v1(value)
262
- when :context
263
- encode_payload_context_v1(value)
264
- end
265
- end
266
-
267
- def encode_payload_id_v1(id)
268
- case id
269
- when nil
270
- nil
271
- when String, Symbol
272
- id = id.to_s
273
- if id.include? ','
274
- raise ArgumentError, "can't include ','"
275
- end
276
- id
277
- when Numeric
278
- id.to_s
279
- else
280
- raise ArgumentError, "invalid type #{id.class}"
281
- end
282
- end
283
-
284
- def encode_payload_context_v1(context)
285
- case context
286
- when nil
287
- nil
288
- when Hash
289
- Base64.urlsafe_encode64(JSON.generate(context)).strip
290
- else
291
- raise ArgumentError, "invalid type #{context.class}"
292
- end
293
- end
294
- end
295
- end