honeycomb-beeline 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +18 -204
- data/lib/honeycomb/beeline/version.rb +1 -1
- data/lib/honeycomb/client.rb +16 -6
- data/lib/honeycomb/span.rb +265 -27
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e5338862ac2eed88b9a4c9712bf47d63652531c0a6c9c06d8e2e4f0f62c2650
|
4
|
+
data.tar.gz: 7e70234ac75d0361578905460c92625744f70fae73a050db386ab5b0614870e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3730b7e2add547932ce15ed3ec4a18ab52a65c20c65542547fa8c8a6a15e19aa0a2aff498d85b8237a5603099cbca3824e5dc3e4a9ba9d260dc26ec0cf3f2d62
|
7
|
+
data.tar.gz: ac42934a9679810c693d673d88aa656ca36f7c17cbccc66cca1cd4cadb3ce0969f50926efdb4a4716913a728235b6368bef20264ebc9d7aa374ce55cdb618f8f
|
data/README.md
CHANGED
@@ -6,217 +6,31 @@
|
|
6
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
7
|
- [Usage and Examples](https://docs.honeycomb.io/getting-data-in/beelines/ruby-beeline/)
|
8
8
|
|
9
|
-
|
9
|
+
Sign up for a [Honeycomb
|
10
10
|
trial](https://ui.honeycomb.io/signup) to obtain an API key before starting.
|
11
11
|
|
12
|
-
##
|
12
|
+
## Compatible with
|
13
13
|
|
14
|
-
|
14
|
+
Requires Ruby version 2.2 or later
|
15
15
|
|
16
|
-
|
17
|
-
gem 'honeycomb-beeline'
|
18
|
-
```
|
19
|
-
Now run `bundle install` to install the gem.
|
16
|
+
Built in instrumentation for:
|
20
17
|
|
21
|
-
|
18
|
+
- Active Record 4 or later
|
19
|
+
- Rails 4 or later
|
20
|
+
- Faraday 0.8 or later
|
21
|
+
- Sequel
|
22
|
+
- Rack 1 or greater
|
22
23
|
|
23
|
-
|
24
|
-
code:
|
24
|
+
## Get in touch
|
25
25
|
|
26
|
-
|
27
|
-
|
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).
|
28
29
|
|
29
|
-
|
30
|
-
```
|
30
|
+
## Contributions
|
31
31
|
|
32
|
-
|
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!
|
33
35
|
|
34
|
-
|
35
|
-
identify itself to Honeycomb. You can find your API key on [your Account
|
36
|
-
page](https://ui.honeycomb.io/account).
|
37
|
-
|
38
|
-
You'll also need to configure the name of a dataset in your Honeycomb account to
|
39
|
-
send events to. The name of your app is a good choice.
|
40
|
-
|
41
|
-
You can specify the configuration either via environment variables, or by
|
42
|
-
passing arguments to `Honeycomb.init`:
|
43
|
-
|
44
|
-
### Configuration via environment variables
|
45
|
-
|
46
|
-
* `HONEYCOMB_WRITEKEY` - specifies the API key (aka "write key")
|
47
|
-
* `HONEYCOMB_DATASET` - specifies the dataset
|
48
|
-
* `HONEYCOMB_SERVICE` - specifies the name of your app (defaults to the dataset
|
49
|
-
name)
|
50
|
-
|
51
|
-
### Configuration via code
|
52
|
-
|
53
|
-
```ruby
|
54
|
-
Honeycomb.init(
|
55
|
-
writekey: '<MY HONEYCOMB API KEY>',
|
56
|
-
dataset: 'my-app',
|
57
|
-
service_name: 'my-app'
|
58
|
-
)
|
59
|
-
```
|
60
|
-
|
61
|
-
Note that Honeycomb API keys have the ability to create and delete data, and
|
62
|
-
should be managed in the same way as your other application secrets. For example
|
63
|
-
you might prefer to configure production API keys via environment variables,
|
64
|
-
rather than checking them into version control.
|
65
|
-
|
66
|
-
## Example questions
|
67
|
-
|
68
|
-
Now your app is instrumented and sending events, try using Honeycomb to ask
|
69
|
-
these questions:
|
70
|
-
|
71
|
-
* Which of my app's routes are the slowest?
|
72
|
-
```
|
73
|
-
BREAKDOWN: request.path
|
74
|
-
CALCULATE: P99(duration_ms)
|
75
|
-
FILTER: type == http_server
|
76
|
-
ORDER BY: P99(duration_ms) DESC
|
77
|
-
```
|
78
|
-
* Where's my app spending the most time?
|
79
|
-
```
|
80
|
-
BREAKDOWN: type
|
81
|
-
CALCULATE: SUM(duration_ms)
|
82
|
-
ORDER BY: SUM(duration_ms) DESC
|
83
|
-
```
|
84
|
-
* Which users are using the endpoint that I'd like to deprecate? First add a
|
85
|
-
[custom field](#adding-additional-context) `user.email`, then try:
|
86
|
-
```
|
87
|
-
BREAKDOWN: app.user.email
|
88
|
-
CALCULATE: COUNT
|
89
|
-
FILTER: request.path == /my/deprecated/endpoint
|
90
|
-
```
|
91
|
-
|
92
|
-
## Example event
|
93
|
-
|
94
|
-
Here is an example of an `http_server` event (recording that your web app
|
95
|
-
processed an incoming HTTP request) emitted by the Beeline:
|
96
|
-
|
97
|
-
```json
|
98
|
-
{
|
99
|
-
"meta.beeline_version": "0.2.0",
|
100
|
-
"meta.local_hostname": "killerbee",
|
101
|
-
"service_name": "my-test-app",
|
102
|
-
"meta.package": "rack",
|
103
|
-
"meta.package_version": "1.3",
|
104
|
-
"type": "http_server",
|
105
|
-
"name": "GET /dashboard",
|
106
|
-
"request.method": "GET",
|
107
|
-
"request.path": "/dashboard",
|
108
|
-
"request.protocol": "https",
|
109
|
-
"request.http_version": "HTTP/1.1",
|
110
|
-
"request.host": "my-test-app.example.com",
|
111
|
-
"request.remote_addr": "172.217.1.238",
|
112
|
-
"request.header.user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
113
|
-
"trace.trace_id": "b694512a-833f-4b35-be5f-6c742ba18e12",
|
114
|
-
"trace.span_id": "c35cc326-ed90-4881-a4a8-68526d252f2e",
|
115
|
-
"response.status_code": 200,
|
116
|
-
"duration_ms": 303.057396
|
117
|
-
}
|
118
|
-
```
|
119
|
-
|
120
|
-
## Adding additional context
|
121
|
-
|
122
|
-
The Beeline will automatically instrument your incoming HTTP requests, database
|
123
|
-
queries and outbound HTTP requests to send events to Honeycomb. However, it can
|
124
|
-
be very helpful to extend these events with additional context specific to your
|
125
|
-
app. You can add your own fields by calling `Rack::Honeycomb.add_field`. For
|
126
|
-
example, this snippet shows how to associate the currently logged-in user with
|
127
|
-
each `http_server` event:
|
128
|
-
|
129
|
-
```ruby
|
130
|
-
get '/hello' do
|
131
|
-
user = authenticate_user()
|
132
|
-
|
133
|
-
# this will add a custom field 'app.user.email' to the http_server event
|
134
|
-
Rack::Honeycomb.add_field(env, 'user.email', user.email)
|
135
|
-
|
136
|
-
"Hello, #{user.name}!"
|
137
|
-
end
|
138
|
-
```
|
139
|
-
|
140
|
-
## Instrumented packages
|
141
|
-
|
142
|
-
The Beeline will automatically send the following events if you are using one of
|
143
|
-
the listed packages:
|
144
|
-
|
145
|
-
### `http_server` (incoming HTTP requests)
|
146
|
-
|
147
|
-
* [Sinatra](http://sinatrarb.com) - via [rack-honeycomb](https://github.com/honeycombio/rack-honeycomb)
|
148
|
-
* Any other [Rack](https://rack.github.io)-based web app - via [rack-honeycomb](https://github.com/honeycombio/rack-honeycomb) (requires manually adding the middleware)
|
149
|
-
|
150
|
-
### `db` (database queries)
|
151
|
-
|
152
|
-
* [ActiveRecord](https://rubygems.org/gems/activerecord) - via
|
153
|
-
[activerecord-honeycomb](https://github.com/honeycombio/activerecord-honeycomb)
|
154
|
-
* [Sequel](https://sequel.jeremyevans.net/) - via
|
155
|
-
[sequel-honeycomb](https://github.com/honeycombio/sequel-honeycomb)
|
156
|
-
|
157
|
-
### `http_client` (outbound HTTP requests)
|
158
|
-
|
159
|
-
* [Faraday](https://github.com/lostisland/faraday) - via
|
160
|
-
[faraday-honeycomb](https://github.com/honeycombio/faraday-honeycomb)
|
161
|
-
|
162
|
-
## Known limitations
|
163
|
-
|
164
|
-
* The Beeline will try to autodetect your web framework and automatically
|
165
|
-
install its middleware. Currently this only works for Sinatra apps, and
|
166
|
-
also fails in some more exotic configurations of Sinatra. If you find you
|
167
|
-
aren't seeing any events for processing web requests, you can install the
|
168
|
-
[middleware](https://www.rubydoc.info/gems/rack-honeycomb) manually: e.g.
|
169
|
-
`use Rack::Honeycomb::Middleware`.
|
170
|
-
* Rails apps should work after installing the middleware as above, but are
|
171
|
-
currently better supported by our dedicated [Rails
|
172
|
-
integration](https://github.com/honeycombio/honeycomb-rails).
|
173
|
-
* Alternative concurrency models such as EventMachine or Celluloid are not
|
174
|
-
currently supported.
|
175
|
-
|
176
|
-
If support for one of these scenarios is important to you, please [let us
|
177
|
-
know](#get-in-touch)!
|
178
|
-
|
179
|
-
## Troubleshooting
|
180
|
-
|
181
|
-
If you've setup the Beeline as above but you aren't seeing data for your app in
|
182
|
-
Honeycomb, or you're seeing errors on startup, here are a few things to try:
|
183
|
-
|
184
|
-
### Debug mode
|
185
|
-
|
186
|
-
To verify the Beeline is working as expected, try running it in debug mode:
|
187
|
-
|
188
|
-
```ruby
|
189
|
-
Honeycomb.init(debug: true)
|
190
|
-
```
|
191
|
-
|
192
|
-
Alternatively, you can also enable debug mode with no code changes by setting
|
193
|
-
`HONEYCOMB_DEBUG=true` in your environment.
|
194
|
-
|
195
|
-
In debug mode, the Beeline will not send any events to Honeycomb, but will
|
196
|
-
instead print them to your app's standard error. It will also log startup
|
197
|
-
messages to standard error.
|
198
|
-
|
199
|
-
### Logging
|
200
|
-
|
201
|
-
By default the Beeline will log errors but otherwise keep quiet. To see more
|
202
|
-
detail about what it's doing, you can pass a logger object (compliant with the
|
203
|
-
[stdlib Logger API](https://ruby-doc.org/stdlib-2.4.1/libdoc/logger/rdoc/)) to
|
204
|
-
`Honeycomb.init`:
|
205
|
-
|
206
|
-
```ruby
|
207
|
-
require 'logger'
|
208
|
-
logger = Logger.new($stderr)
|
209
|
-
logger.level = Logger::INFO # determine how much detail you want to see
|
210
|
-
Honeycomb.init(logger: logger)
|
211
|
-
```
|
212
|
-
|
213
|
-
A level of `Logger::DEBUG` will show you detail about each library being instrumented,
|
214
|
-
whereas a level of `Logger::INFO` will just print a few progress messages.
|
215
|
-
|
216
|
-
### Get in touch
|
217
|
-
|
218
|
-
This beeline is still young, so please reach out to
|
219
|
-
[support@honeycomb.io](mailto:support@honeycomb.io) or ping us with the chat
|
220
|
-
bubble on [our website](https://www.honeycomb.io) for assistance. We also
|
221
|
-
welcome [bug reports](https://github.com/honeycombio/beeline-ruby/issues) and
|
222
|
-
[contributions](https://github.com/honeycombio/beeline-ruby/blob/master/CONTRIBUTING.md).
|
36
|
+
All contributions will be released under the Apache License 2.0.
|
data/lib/honeycomb/client.rb
CHANGED
@@ -72,7 +72,7 @@ module Honeycomb
|
|
72
72
|
@client = new_client(options)
|
73
73
|
|
74
74
|
after_init_hooks.each do |label, block|
|
75
|
-
|
75
|
+
debug "Running hook '#{label}' after Honeycomb.init"
|
76
76
|
run_hook(label, block)
|
77
77
|
end
|
78
78
|
|
@@ -106,6 +106,16 @@ module Honeycomb
|
|
106
106
|
end
|
107
107
|
|
108
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
|
+
|
109
119
|
def after_init_hooks
|
110
120
|
@after_init_hooks ||= []
|
111
121
|
end
|
@@ -116,13 +126,13 @@ module Honeycomb
|
|
116
126
|
options = {user_agent_addition: USER_AGENT_SUFFIX}.merge(options)
|
117
127
|
if @debug
|
118
128
|
raise ArgumentError, "can't specify both client and debug options", caller if client
|
119
|
-
|
129
|
+
info 'logging events to standard error instead of sending to Honeycomb'
|
120
130
|
client = Libhoney::LogClient.new(verbose: true, **options)
|
121
131
|
else
|
122
132
|
client ||= if options[:writekey] && options[:dataset]
|
123
133
|
Libhoney::Client.new(options)
|
124
134
|
else
|
125
|
-
|
135
|
+
warn "#{self.name}: no #{options[:writekey] ? 'dataset' : 'writekey'} configured, disabling sending events"
|
126
136
|
Libhoney::NullClient.new(options)
|
127
137
|
end
|
128
138
|
end
|
@@ -146,7 +156,7 @@ module Honeycomb
|
|
146
156
|
end
|
147
157
|
|
148
158
|
if defined?(@initialized)
|
149
|
-
|
159
|
+
debug "Running hook '#{label}' as Honeycomb already initialized"
|
150
160
|
run_hook(label, hook)
|
151
161
|
else
|
152
162
|
after_init_hooks << [label, hook]
|
@@ -155,12 +165,12 @@ module Honeycomb
|
|
155
165
|
|
156
166
|
def run_hook(label, block)
|
157
167
|
if @without.include?(label)
|
158
|
-
|
168
|
+
debug "Skipping hook '#{label}' due to opt-out"
|
159
169
|
else
|
160
170
|
block.call @client, @logger
|
161
171
|
end
|
162
172
|
rescue => e
|
163
|
-
|
173
|
+
warn "Honeycomb.init hook '#{label}' raised #{e.class}: #{e}"
|
164
174
|
end
|
165
175
|
end
|
166
176
|
|
data/lib/honeycomb/span.rb
CHANGED
@@ -1,42 +1,50 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'json'
|
1
3
|
require 'securerandom'
|
2
4
|
|
3
5
|
module Honeycomb
|
4
6
|
class << self
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
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)
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
with_trace trace_id: trace_id, parent_span_id: parent_span_id, context: context do
|
15
|
+
yield
|
16
|
+
end
|
16
17
|
end
|
17
18
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
25
30
|
end
|
26
31
|
|
27
|
-
#
|
28
|
-
|
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
|
+
|
29
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
|
30
44
|
|
31
|
-
|
32
|
-
event.add_field 'service_name', service_name
|
33
|
-
event.add_field 'name', name
|
34
|
-
event.add_field 'trace.span_id', span_id
|
45
|
+
start = Time.now
|
35
46
|
|
36
|
-
|
37
|
-
with_span_id(span_id) do |parent_span_id|
|
38
|
-
event.add_field 'trace.parent_id', parent_span_id if parent_span_id
|
39
|
-
yield
|
47
|
+
yield span_id, trace_id
|
40
48
|
end
|
41
49
|
rescue Exception => e
|
42
50
|
if event
|
@@ -53,5 +61,235 @@ module Honeycomb
|
|
53
61
|
event.send
|
54
62
|
end
|
55
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
|
56
294
|
end
|
57
295
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: honeycomb-beeline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Stokes
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-11-
|
11
|
+
date: 2018-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: libhoney
|
@@ -30,56 +30,56 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.4.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 0.4.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rack-honeycomb
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
47
|
+
version: 0.4.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
54
|
+
version: 0.4.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: faraday-honeycomb
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
61
|
+
version: 0.3.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
68
|
+
version: 0.3.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: sequel-honeycomb
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.
|
75
|
+
version: 0.4.0
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
82
|
+
version: 0.4.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: activerecord
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|