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.
@@ -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
- def parse(serialized_trace)
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
- def to_trace_header
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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Honeycomb
4
+ module Propagation
5
+ Context = Struct.new(:trace_id, :parent_id, :trace_fields, :dataset) do
6
+ def to_array
7
+ [trace_id, parent_id, trace_fields, dataset]
8
+ end
9
+ end
10
+ end
11
+ 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
@@ -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
- sample_hook: nil,
43
- presend_hook: nil,
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).tap do |c|
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
@@ -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
- parse serialized_trace
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