honeycomb-beeline 2.1.2 → 2.4.2
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/.circleci/bundler_version.sh +4 -0
- data/.circleci/config.yml +102 -43
- data/.editorconfig +12 -0
- data/.github/dependabot.yml +13 -0
- data/.github/workflows/apply-labels.yml +16 -0
- data/.gitignore +1 -0
- data/.rspec +4 -0
- data/.rubocop.yml +6 -0
- data/CHANGELOG.md +104 -0
- data/Gemfile.lock +15 -5
- data/README.md +8 -0
- data/UPGRADING.md +10 -0
- data/honeycomb-beeline.gemspec +2 -0
- data/lib/honeycomb-beeline.rb +2 -1
- data/lib/honeycomb/beeline/version.rb +1 -1
- data/lib/honeycomb/client.rb +10 -0
- data/lib/honeycomb/configuration.rb +23 -0
- data/lib/honeycomb/integrations/faraday.rb +3 -1
- data/lib/honeycomb/integrations/rack.rb +17 -7
- data/lib/honeycomb/integrations/rails.rb +10 -0
- data/lib/honeycomb/integrations/redis.rb +103 -94
- data/lib/honeycomb/propagation.rb +4 -51
- data/lib/honeycomb/propagation/aws.rb +85 -0
- data/lib/honeycomb/propagation/context.rb +11 -0
- data/lib/honeycomb/propagation/honeycomb.rb +96 -0
- data/lib/honeycomb/propagation/w3c.rb +79 -0
- data/lib/honeycomb/span.rb +32 -4
- data/lib/honeycomb/trace.rb +14 -1
- metadata +39 -3
@@ -4,63 +4,16 @@ require "base64"
|
|
4
4
|
require "json"
|
5
5
|
require "uri"
|
6
6
|
|
7
|
+
require "honeycomb/propagation/honeycomb"
|
8
|
+
|
7
9
|
module Honeycomb
|
8
10
|
# Parse trace headers
|
9
11
|
module PropagationParser
|
10
|
-
|
11
|
-
unless serialized_trace.nil?
|
12
|
-
version, payload = serialized_trace.split(";", 2)
|
13
|
-
|
14
|
-
if version == "1"
|
15
|
-
trace_id, parent_span_id, trace_fields, dataset = parse_v1(payload)
|
16
|
-
|
17
|
-
if !trace_id.nil? && !parent_span_id.nil?
|
18
|
-
return [trace_id, parent_span_id, trace_fields, dataset]
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
[nil, nil, nil, nil]
|
24
|
-
end
|
25
|
-
|
26
|
-
def parse_v1(payload)
|
27
|
-
trace_id, parent_span_id, trace_fields, dataset = nil
|
28
|
-
payload.split(",").each do |entry|
|
29
|
-
key, value = entry.split("=", 2)
|
30
|
-
case key
|
31
|
-
when "dataset"
|
32
|
-
dataset = URI.decode_www_form_component(value)
|
33
|
-
when "trace_id"
|
34
|
-
trace_id = value
|
35
|
-
when "parent_id"
|
36
|
-
parent_span_id = value
|
37
|
-
when "context"
|
38
|
-
Base64.decode64(value).tap do |json|
|
39
|
-
begin
|
40
|
-
trace_fields = JSON.parse json
|
41
|
-
rescue JSON::ParserError
|
42
|
-
trace_fields = {}
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
[trace_id, parent_span_id, trace_fields, dataset]
|
49
|
-
end
|
12
|
+
include HoneycombPropagation::UnmarshalTraceContext
|
50
13
|
end
|
51
14
|
|
52
15
|
# Serialize trace headers
|
53
16
|
module PropagationSerializer
|
54
|
-
|
55
|
-
context = Base64.urlsafe_encode64(JSON.generate(trace.fields)).strip
|
56
|
-
encoded_dataset = URI.encode_www_form_component(builder.dataset)
|
57
|
-
data_to_propogate = [
|
58
|
-
"dataset=#{encoded_dataset}",
|
59
|
-
"trace_id=#{trace.id}",
|
60
|
-
"parent_id=#{id}",
|
61
|
-
"context=#{context}",
|
62
|
-
]
|
63
|
-
"1;#{data_to_propogate.join(',')}"
|
64
|
-
end
|
17
|
+
include HoneycombPropagation::MarshalTraceContext
|
65
18
|
end
|
66
19
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Honeycomb
|
4
|
+
# Parsing and propagation for AWS trace headers
|
5
|
+
module AWSPropagation
|
6
|
+
# Parse trace headers
|
7
|
+
module UnmarshalTraceContext
|
8
|
+
def parse(serialized_trace)
|
9
|
+
unless serialized_trace.nil?
|
10
|
+
split = serialized_trace.split(";")
|
11
|
+
|
12
|
+
trace_id, parent_span_id, trace_fields = get_fields(split)
|
13
|
+
|
14
|
+
parent_span_id = trace_id if parent_span_id.nil?
|
15
|
+
|
16
|
+
trace_fields = nil if trace_fields.empty?
|
17
|
+
|
18
|
+
if !trace_id.nil? && !parent_span_id.nil?
|
19
|
+
# return nil for dataset
|
20
|
+
return [trace_id, parent_span_id, trace_fields, nil]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
[nil, nil, nil, nil]
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_fields(fields)
|
28
|
+
trace_id, parent_span_id = nil
|
29
|
+
trace_fields = {}
|
30
|
+
fields.each do |entry|
|
31
|
+
key, value = entry.split("=", 2)
|
32
|
+
case key.downcase
|
33
|
+
when "root"
|
34
|
+
trace_id = value
|
35
|
+
when "self"
|
36
|
+
parent_span_id = value
|
37
|
+
when "parent"
|
38
|
+
parent_span_id = value if parent_span_id.nil?
|
39
|
+
else
|
40
|
+
trace_fields[key] = value unless key.empty?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
[trace_id, parent_span_id, trace_fields]
|
45
|
+
end
|
46
|
+
|
47
|
+
module_function :parse, :get_fields
|
48
|
+
public :parse
|
49
|
+
end
|
50
|
+
|
51
|
+
# Serialize trace headers
|
52
|
+
module MarshalTraceContext
|
53
|
+
def to_trace_header
|
54
|
+
context = [""]
|
55
|
+
unless trace.fields.keys.nil?
|
56
|
+
trace.fields.keys.each do |key|
|
57
|
+
context.push("#{key}=#{trace.fields[key]}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
data_to_propagate = [
|
62
|
+
"Root=#{trace.id}",
|
63
|
+
"Parent=#{id}",
|
64
|
+
]
|
65
|
+
"#{data_to_propagate.join(';')}#{context.join(';')}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.to_trace_header(propagation_context)
|
69
|
+
context = [""]
|
70
|
+
fields = propagation_context.trace_fields
|
71
|
+
unless fields.keys.nil?
|
72
|
+
fields.keys.each do |key|
|
73
|
+
context.push("#{key}=#{fields[key]}")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
data_to_propagate = [
|
78
|
+
"Root=#{propagation_context.trace_id}",
|
79
|
+
"Parent=#{propagation_context.parent_id}",
|
80
|
+
]
|
81
|
+
"#{data_to_propagate.join(';')}#{context.join(';')}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
require "json"
|
5
|
+
require "uri"
|
6
|
+
|
7
|
+
module Honeycomb
|
8
|
+
# Parsing and propagation for honeycomb trace headers
|
9
|
+
module HoneycombPropagation
|
10
|
+
# Parse trace headers
|
11
|
+
module UnmarshalTraceContext
|
12
|
+
def parse_rack_env(env)
|
13
|
+
parse env["HTTP_X_HONEYCOMB_TRACE"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse(serialized_trace)
|
17
|
+
unless serialized_trace.nil?
|
18
|
+
version, payload = serialized_trace.split(";", 2)
|
19
|
+
|
20
|
+
if version == "1"
|
21
|
+
trace_id, parent_span_id, trace_fields, dataset = parse_v1(payload)
|
22
|
+
|
23
|
+
if !trace_id.nil? && !parent_span_id.nil?
|
24
|
+
return [trace_id, parent_span_id, trace_fields, dataset]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
[nil, nil, nil, nil]
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_v1(payload)
|
33
|
+
trace_id, parent_span_id, trace_fields, dataset = nil
|
34
|
+
payload.split(",").each do |entry|
|
35
|
+
key, value = entry.split("=", 2)
|
36
|
+
case key.downcase
|
37
|
+
when "dataset"
|
38
|
+
dataset = URI.decode_www_form_component(value)
|
39
|
+
when "trace_id"
|
40
|
+
trace_id = value
|
41
|
+
when "parent_id"
|
42
|
+
parent_span_id = value
|
43
|
+
when "context"
|
44
|
+
Base64.decode64(value).tap do |json|
|
45
|
+
begin
|
46
|
+
trace_fields = JSON.parse json
|
47
|
+
rescue JSON::ParserError
|
48
|
+
trace_fields = {}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
[trace_id, parent_span_id, trace_fields, dataset]
|
55
|
+
end
|
56
|
+
|
57
|
+
module_function :parse_rack_env, :parse, :parse_v1
|
58
|
+
public :parse_rack_env, :parse
|
59
|
+
end
|
60
|
+
|
61
|
+
# Serialize trace headers
|
62
|
+
module MarshalTraceContext
|
63
|
+
def to_trace_header
|
64
|
+
context = Base64.urlsafe_encode64(JSON.generate(trace.fields)).strip
|
65
|
+
encoded_dataset = URI.encode_www_form_component(builder.dataset)
|
66
|
+
data_to_propogate = [
|
67
|
+
"dataset=#{encoded_dataset}",
|
68
|
+
"trace_id=#{trace.id}",
|
69
|
+
"parent_id=#{id}",
|
70
|
+
"context=#{context}",
|
71
|
+
]
|
72
|
+
"1;#{data_to_propogate.join(',')}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.parse_faraday_env(_env, propagation_context)
|
76
|
+
{
|
77
|
+
"X-Honeycomb-Trace" => to_trace_header(propagation_context),
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.to_trace_header(propagation_context)
|
82
|
+
fields = propagation_context.trace_fields
|
83
|
+
context = Base64.urlsafe_encode64(JSON.generate(fields)).strip
|
84
|
+
dataset = propagation_context.dataset
|
85
|
+
encoded_dataset = URI.encode_www_form_component(dataset)
|
86
|
+
data_to_propogate = [
|
87
|
+
"dataset=#{encoded_dataset}",
|
88
|
+
"trace_id=#{propagation_context.trace_id}",
|
89
|
+
"parent_id=#{propagation_context.parent_id}",
|
90
|
+
"context=#{context}",
|
91
|
+
]
|
92
|
+
"1;#{data_to_propogate.join(',')}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Honeycomb
|
4
|
+
# Parsing and propagation for W3C trace headers
|
5
|
+
module W3CPropagation
|
6
|
+
# Parse trace headers
|
7
|
+
module UnmarshalTraceContext
|
8
|
+
INVALID_TRACE_ID = "00000000000000000000000000000000".freeze
|
9
|
+
INVALID_SPAN_ID = "0000000000000000".freeze
|
10
|
+
|
11
|
+
def parse_rack_env(env)
|
12
|
+
parse env["HTTP_TRACEPARENT"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse(serialized_trace)
|
16
|
+
unless serialized_trace.nil?
|
17
|
+
version, payload = serialized_trace.split("-", 2)
|
18
|
+
# version should be 2 hex characters
|
19
|
+
if version =~ /^[A-Fa-f0-9]{2}$/
|
20
|
+
trace_id, parent_span_id = parse_v1(payload)
|
21
|
+
|
22
|
+
if !trace_id.nil? && !parent_span_id.nil?
|
23
|
+
# return nil for dataset
|
24
|
+
return [trace_id, parent_span_id, nil, nil]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
[nil, nil, nil, nil]
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse_v1(payload)
|
32
|
+
trace_id, parent_span_id, trace_flags = payload.split("-", 3)
|
33
|
+
|
34
|
+
if trace_flags.nil?
|
35
|
+
# if trace_flags is nil, it means a field is missing
|
36
|
+
return [nil, nil]
|
37
|
+
end
|
38
|
+
|
39
|
+
if trace_id == INVALID_TRACE_ID || parent_span_id == INVALID_SPAN_ID
|
40
|
+
return [nil, nil]
|
41
|
+
end
|
42
|
+
|
43
|
+
[trace_id, parent_span_id]
|
44
|
+
end
|
45
|
+
|
46
|
+
module_function :parse_rack_env, :parse, :parse_v1
|
47
|
+
public :parse
|
48
|
+
end
|
49
|
+
|
50
|
+
# Serialize trace headers
|
51
|
+
module MarshalTraceContext
|
52
|
+
def to_trace_header
|
53
|
+
# do not propagate malformed ids
|
54
|
+
if trace.id =~ /^[A-Fa-f0-9]{32}$/ && id =~ /^[A-Fa-f0-9]{16}$/
|
55
|
+
return "00-#{trace.id}-#{id}-01"
|
56
|
+
end
|
57
|
+
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.parse_faraday_env(_env, propagation_context)
|
62
|
+
{
|
63
|
+
"traceparent" => to_trace_header(propagation_context),
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.to_trace_header(propagation_context)
|
68
|
+
trace_id = propagation_context.trace_id
|
69
|
+
parent_id = propagation_context.parent_id
|
70
|
+
# do not propagate malformed ids
|
71
|
+
if trace_id =~ /^[A-Fa-f0-9]{32}$/ && parent_id =~ /^[A-Fa-f0-9]{16}$/
|
72
|
+
return "00-#{trace_id}-#{parent_id}-01"
|
73
|
+
end
|
74
|
+
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/honeycomb/span.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "forwardable"
|
4
4
|
require "securerandom"
|
5
5
|
require "honeycomb/propagation"
|
6
|
+
require "honeycomb/propagation/context"
|
6
7
|
require "honeycomb/deterministic_sampler"
|
7
8
|
require "honeycomb/rollup_fields"
|
8
9
|
|
@@ -34,13 +35,14 @@ module Honeycomb
|
|
34
35
|
@sent = false
|
35
36
|
@started = clock_time
|
36
37
|
parse_options(**options)
|
38
|
+
parse_hooks(**options)
|
37
39
|
end
|
38
40
|
|
39
41
|
def parse_options(parent: nil,
|
40
42
|
parent_id: nil,
|
41
43
|
is_root: parent_id.nil?,
|
42
|
-
|
43
|
-
|
44
|
+
_sample_hook: nil,
|
45
|
+
_presend_hook: nil,
|
44
46
|
**_options)
|
45
47
|
@parent = parent
|
46
48
|
# parent_id should be removed in the next major version bump. It has been
|
@@ -48,8 +50,15 @@ module Honeycomb
|
|
48
50
|
# compatability
|
49
51
|
@parent_id = parent_id
|
50
52
|
@is_root = is_root
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse_hooks(sample_hook: nil,
|
56
|
+
presend_hook: nil,
|
57
|
+
propagation_hook: nil,
|
58
|
+
**_options)
|
51
59
|
@presend_hook = presend_hook
|
52
60
|
@sample_hook = sample_hook
|
61
|
+
@propagation_hook = propagation_hook
|
53
62
|
end
|
54
63
|
|
55
64
|
def create_child
|
@@ -59,7 +68,8 @@ module Honeycomb
|
|
59
68
|
parent: self,
|
60
69
|
parent_id: id,
|
61
70
|
sample_hook: sample_hook,
|
62
|
-
presend_hook: presend_hook
|
71
|
+
presend_hook: presend_hook,
|
72
|
+
propagation_hook: propagation_hook).tap do |c|
|
63
73
|
children << c
|
64
74
|
end
|
65
75
|
end
|
@@ -70,6 +80,14 @@ module Honeycomb
|
|
70
80
|
send_internal
|
71
81
|
end
|
72
82
|
|
83
|
+
def trace_headers(env)
|
84
|
+
if propagation_hook
|
85
|
+
propagation_hook.call(env, propagation_context)
|
86
|
+
else
|
87
|
+
{}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
73
91
|
protected
|
74
92
|
|
75
93
|
def send_by_parent
|
@@ -94,7 +112,17 @@ module Honeycomb
|
|
94
112
|
:builder,
|
95
113
|
:context,
|
96
114
|
:presend_hook,
|
97
|
-
:sample_hook
|
115
|
+
:sample_hook,
|
116
|
+
:propagation_hook
|
117
|
+
|
118
|
+
def propagation_context
|
119
|
+
Honeycomb::Propagation::Context.new(
|
120
|
+
trace.id,
|
121
|
+
id,
|
122
|
+
trace.fields,
|
123
|
+
builder.dataset,
|
124
|
+
)
|
125
|
+
end
|
98
126
|
|
99
127
|
def sent?
|
100
128
|
@sent
|
data/lib/honeycomb/trace.rb
CHANGED
@@ -19,7 +19,8 @@ module Honeycomb
|
|
19
19
|
|
20
20
|
def initialize(builder:, context:, serialized_trace: nil, **options)
|
21
21
|
trace_id, parent_span_id, trace_fields, dataset =
|
22
|
-
|
22
|
+
internal_parse(serialized_trace: serialized_trace, **options)
|
23
|
+
|
23
24
|
dataset && builder.dataset = dataset
|
24
25
|
@id = trace_id || generate_trace_id
|
25
26
|
@fields = trace_fields || {}
|
@@ -45,5 +46,17 @@ module Honeycomb
|
|
45
46
|
return id unless id == INVALID_TRACE_ID
|
46
47
|
end
|
47
48
|
end
|
49
|
+
|
50
|
+
def internal_parse(serialized_trace: nil, parser_hook: nil, **_options)
|
51
|
+
# previously we passed in the header directly as a string for us to parse
|
52
|
+
# now we get passed the rack env to use as an argument to the provided
|
53
|
+
# parser_hook. This preserves the current behaviour and allows us to
|
54
|
+
# move forward with the new behaviour without breaking changes
|
55
|
+
if serialized_trace.is_a?(Hash) && parser_hook
|
56
|
+
parser_hook.call(serialized_trace)
|
57
|
+
else
|
58
|
+
parse serialized_trace
|
59
|
+
end
|
60
|
+
end
|
48
61
|
end
|
49
62
|
end
|