honeycomb-beeline 2.1.2 → 2.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|