honeycomb-beeline 1.3.0 → 2.2.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/.circleci/config.yml +95 -549
- data/.github/CODEOWNERS +5 -0
- data/.rubocop.yml +5 -0
- data/Appraisals +11 -2
- data/Gemfile.lock +46 -42
- data/README.md +1 -0
- data/honeycomb-beeline.gemspec +4 -2
- data/lib/generators/honeycomb/honeycomb_generator.rb +14 -0
- data/lib/honeycomb-beeline.rb +2 -1
- data/lib/honeycomb/beeline/version.rb +1 -1
- data/lib/honeycomb/client.rb +35 -24
- data/lib/honeycomb/configuration.rb +1 -1
- data/lib/honeycomb/integrations/active_support.rb +14 -2
- data/lib/honeycomb/integrations/aws.rb +5 -1
- data/lib/honeycomb/integrations/faraday.rb +1 -1
- data/lib/honeycomb/integrations/rack.rb +15 -4
- data/lib/honeycomb/integrations/rails.rb +70 -20
- data/lib/honeycomb/integrations/railtie.rb +2 -3
- data/lib/honeycomb/integrations/redis.rb +3 -1
- data/lib/honeycomb/integrations/warden.rb +2 -2
- data/lib/honeycomb/propagation.rb +4 -51
- data/lib/honeycomb/propagation/aws.rb +66 -0
- data/lib/honeycomb/propagation/honeycomb.rb +69 -0
- data/lib/honeycomb/propagation/w3c.rb +55 -0
- data/lib/honeycomb/span.rb +37 -11
- data/lib/honeycomb/trace.rb +12 -1
- metadata +46 -8
@@ -106,8 +106,12 @@ module Honeycomb
|
|
106
106
|
"aws.region" => context.config.region,
|
107
107
|
"aws.service" => context.client.class.identifier,
|
108
108
|
"aws.operation" => context.operation_name,
|
109
|
-
"aws.params" => context.params,
|
110
109
|
}
|
110
|
+
|
111
|
+
context.params && context.params.each do |key, value|
|
112
|
+
context[:honeycomb_aws_sdk_data]["aws.params.#{key}"] = value
|
113
|
+
end
|
114
|
+
|
111
115
|
span.add context[:honeycomb_aws_sdk_data]
|
112
116
|
end
|
113
117
|
|
@@ -15,7 +15,7 @@ module Honeycomb
|
|
15
15
|
|
16
16
|
@client.start_span(name: "http_client") do |span|
|
17
17
|
span.add_field "request.method", env.method.upcase
|
18
|
-
span.add_field "request.
|
18
|
+
span.add_field "request.scheme", env.url.scheme
|
19
19
|
span.add_field "request.host", env.url.host
|
20
20
|
span.add_field "request.path", env.url.path
|
21
21
|
span.add_field "meta.type", "http_client"
|
@@ -13,8 +13,14 @@ module Honeycomb
|
|
13
13
|
["HTTP_VERSION", "request.http_version"],
|
14
14
|
["HTTP_HOST", "request.host"],
|
15
15
|
["REMOTE_ADDR", "request.remote_addr"],
|
16
|
+
["HTTP_X_FORWARDED_FOR", "request.header.x_forwarded_for"],
|
17
|
+
["HTTP_X_FORWARDED_PROTO", "request.header.x_forwarded_proto"],
|
18
|
+
["HTTP_X_FORWARDED_PORT", "request.header.x_forwarded_port"],
|
19
|
+
["HTTP_ACCEPT", "request.header.accept"],
|
20
|
+
["HTTP_ACCEPT_LANGUAGE", "request.header.accept_language"],
|
21
|
+
["CONTENT_TYPE", "request.header.content_type"],
|
16
22
|
["HTTP_USER_AGENT", "request.header.user_agent"],
|
17
|
-
["rack.url_scheme", "request.
|
23
|
+
["rack.url_scheme", "request.scheme"],
|
18
24
|
].freeze
|
19
25
|
|
20
26
|
attr_reader :app, :client
|
@@ -25,16 +31,20 @@ module Honeycomb
|
|
25
31
|
end
|
26
32
|
|
27
33
|
def call(env)
|
34
|
+
req = ::Rack::Request.new(env)
|
28
35
|
hny = env["HTTP_X_HONEYCOMB_TRACE"]
|
29
36
|
client.start_span(name: "http_request", serialized_trace: hny) do |span|
|
30
37
|
add_field = lambda do |key, value|
|
31
|
-
|
32
|
-
|
33
|
-
|
38
|
+
unless value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
39
|
+
span.add_field(key, value)
|
40
|
+
end
|
34
41
|
end
|
35
42
|
|
36
43
|
extract_fields(env, RACK_FIELDS, &add_field)
|
37
44
|
|
45
|
+
span.add_field("request.secure", req.ssl?)
|
46
|
+
span.add_field("request.xhr", req.xhr?)
|
47
|
+
|
38
48
|
status, headers, body = app.call(env)
|
39
49
|
|
40
50
|
add_package_information(env, &add_field)
|
@@ -42,6 +52,7 @@ module Honeycomb
|
|
42
52
|
extract_user_information(env, &add_field)
|
43
53
|
|
44
54
|
span.add_field("response.status_code", status)
|
55
|
+
span.add_field("response.content_type", headers["Content-Type"])
|
45
56
|
|
46
57
|
[status, headers, body]
|
47
58
|
end
|
@@ -11,29 +11,79 @@ module Honeycomb
|
|
11
11
|
yield "meta.package", "rails"
|
12
12
|
yield "meta.package_version", ::Rails::VERSION::STRING
|
13
13
|
|
14
|
-
::ActionDispatch::Request.new(env)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
found_route = true
|
32
|
-
yield "request.route", "#{env['REQUEST_METHOD']} #{route.path.spec}"
|
33
|
-
end
|
14
|
+
request = ::ActionDispatch::Request.new(env)
|
15
|
+
|
16
|
+
yield "request.controller", request.path_parameters[:controller]
|
17
|
+
yield "request.action", request.path_parameters[:action]
|
18
|
+
yield "request.route", route_for(request)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def route_for(request)
|
24
|
+
router = router_for(request)
|
25
|
+
routing = routing_for(request)
|
26
|
+
|
27
|
+
return unless router && routing
|
28
|
+
|
29
|
+
router.recognize(routing) do |route, _|
|
30
|
+
return "#{request.method} #{route.path.spec}"
|
34
31
|
end
|
35
32
|
end
|
36
33
|
|
34
|
+
# Broadly compatible way of getting the ActionDispatch::Routing::RouteSet.
|
35
|
+
#
|
36
|
+
# While we'd like to just use ActionDispatch::Request#routes, that method
|
37
|
+
# was only added circa Rails 5. To support Rails 4, we have to use direct
|
38
|
+
# Rack env access.
|
39
|
+
#
|
40
|
+
# @see https://github.com/rails/rails/commit/87a75910640b83a677099198ccb3317d9850c204
|
41
|
+
def router_for(request)
|
42
|
+
routes = request.env["action_dispatch.routes"]
|
43
|
+
routes.router if routes.respond_to?(:router)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Constructs a simplified ActionDispatch::Request with the original route.
|
47
|
+
#
|
48
|
+
# This is based on ActionDispatch::Routing::RouteSet#recognize_path, which
|
49
|
+
# reconstructs an ActionDispatch::Request using a given HTTP method + path
|
50
|
+
# by making a mock Rack environment. Here, instead of taking the method +
|
51
|
+
# path from input parameters, we use the original values from the actual
|
52
|
+
# incoming request (prior to any mangling that may have been done by
|
53
|
+
# middleware).
|
54
|
+
#
|
55
|
+
# The resulting ActionDispatch::Request instance is suitable for passing to
|
56
|
+
# ActionDispatch::Journey::Router#recognize to get the original Rails
|
57
|
+
# routing information corresponding to the incoming request.
|
58
|
+
#
|
59
|
+
# @param request [ActionDispatch::Request]
|
60
|
+
# the actual incoming Rails request
|
61
|
+
#
|
62
|
+
# @return [ActionDispatch::Request]
|
63
|
+
# a simplified version of the incoming request that retains the original
|
64
|
+
# routing information, but nothing else (e.g., no HTTP parameters)
|
65
|
+
#
|
66
|
+
# @return [nil]
|
67
|
+
# if the original request's path is invalid
|
68
|
+
#
|
69
|
+
# @see https://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-method
|
70
|
+
# @see https://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-original_fullpath
|
71
|
+
# @see https://github.com/rails/rails/blob/2a44ff12c858d296797963f7aa97abfa0c840a15/actionpack/lib/action_dispatch/journey/router/utils.rb#L7-L27
|
72
|
+
# @see https://github.com/rails/rails/blob/2a44ff12c858d296797963f7aa97abfa0c840a15/actionpack/lib/action_dispatch/routing/route_set.rb#L846-L859
|
73
|
+
def routing_for(request)
|
74
|
+
verb = request.method
|
75
|
+
path = request.original_fullpath
|
76
|
+
path = normalize(path) unless path =~ %r{://}
|
77
|
+
env = ::Rack::MockRequest.env_for(path, method: verb)
|
78
|
+
::ActionDispatch::Request.new(env)
|
79
|
+
rescue URI::InvalidURIError
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def normalize(path)
|
84
|
+
::ActionDispatch::Journey::Router::Utils.normalize_path(path)
|
85
|
+
end
|
86
|
+
|
37
87
|
# Rails middleware
|
38
88
|
class Middleware
|
39
89
|
include Rack
|
@@ -9,9 +9,8 @@ module Honeycomb
|
|
9
9
|
initializer("honeycomb.install_middleware",
|
10
10
|
after: :load_config_initializers) do |app|
|
11
11
|
if Honeycomb.client
|
12
|
-
|
13
|
-
|
14
|
-
::Rails::Rack::Logger,
|
12
|
+
app.config.middleware.insert_after(
|
13
|
+
ActionDispatch::ShowExceptions,
|
15
14
|
Honeycomb::Rails::Middleware,
|
16
15
|
client: Honeycomb.client,
|
17
16
|
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Honeycomb
|
4
|
-
# Methods for
|
4
|
+
# Methods for extracting common warden/devise fields from a rack env hash
|
5
5
|
module Warden
|
6
6
|
COMMON_USER_FIELDS = %i[
|
7
7
|
email
|
@@ -24,7 +24,7 @@ module Honeycomb
|
|
24
24
|
key.match(SCOPE_PATTERN)
|
25
25
|
end
|
26
26
|
warden_scopes = keys.map do |key|
|
27
|
-
key.gsub(SCOPE_PATTERN, "
|
27
|
+
key.gsub(SCOPE_PATTERN, "\\1")
|
28
28
|
end
|
29
29
|
best_scope = warden_scopes.include?("user") ? "user" : warden_scopes.first
|
30
30
|
|
@@ -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,66 @@
|
|
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
|
+
end
|
47
|
+
|
48
|
+
# Serialize trace headers
|
49
|
+
module MarshalTraceContext
|
50
|
+
def to_trace_header
|
51
|
+
context = [""]
|
52
|
+
unless trace.fields.keys.nil?
|
53
|
+
trace.fields.keys.each do |key|
|
54
|
+
context.push("#{key}=#{trace.fields[key]}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
data_to_propagate = [
|
59
|
+
"Root=#{trace.id}",
|
60
|
+
"Parent=#{id}",
|
61
|
+
]
|
62
|
+
"#{data_to_propagate.join(';')}#{context.join(';')}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,69 @@
|
|
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(serialized_trace)
|
13
|
+
unless serialized_trace.nil?
|
14
|
+
version, payload = serialized_trace.split(";", 2)
|
15
|
+
|
16
|
+
if version == "1"
|
17
|
+
trace_id, parent_span_id, trace_fields, dataset = parse_v1(payload)
|
18
|
+
|
19
|
+
if !trace_id.nil? && !parent_span_id.nil?
|
20
|
+
return [trace_id, parent_span_id, trace_fields, dataset]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
[nil, nil, nil, nil]
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_v1(payload)
|
29
|
+
trace_id, parent_span_id, trace_fields, dataset = nil
|
30
|
+
payload.split(",").each do |entry|
|
31
|
+
key, value = entry.split("=", 2)
|
32
|
+
case key.downcase
|
33
|
+
when "dataset"
|
34
|
+
dataset = URI.decode_www_form_component(value)
|
35
|
+
when "trace_id"
|
36
|
+
trace_id = value
|
37
|
+
when "parent_id"
|
38
|
+
parent_span_id = value
|
39
|
+
when "context"
|
40
|
+
Base64.decode64(value).tap do |json|
|
41
|
+
begin
|
42
|
+
trace_fields = JSON.parse json
|
43
|
+
rescue JSON::ParserError
|
44
|
+
trace_fields = {}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
[trace_id, parent_span_id, trace_fields, dataset]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Serialize trace headers
|
55
|
+
module MarshalTraceContext
|
56
|
+
def to_trace_header
|
57
|
+
context = Base64.urlsafe_encode64(JSON.generate(trace.fields)).strip
|
58
|
+
encoded_dataset = URI.encode_www_form_component(builder.dataset)
|
59
|
+
data_to_propogate = [
|
60
|
+
"dataset=#{encoded_dataset}",
|
61
|
+
"trace_id=#{trace.id}",
|
62
|
+
"parent_id=#{id}",
|
63
|
+
"context=#{context}",
|
64
|
+
]
|
65
|
+
"1;#{data_to_propogate.join(',')}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,55 @@
|
|
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(serialized_trace)
|
12
|
+
unless serialized_trace.nil?
|
13
|
+
version, payload = serialized_trace.split("-", 2)
|
14
|
+
# version should be 2 hex characters
|
15
|
+
if version =~ /^[A-Fa-f0-9]{2}$/
|
16
|
+
trace_id, parent_span_id = parse_v1(payload)
|
17
|
+
|
18
|
+
if !trace_id.nil? && !parent_span_id.nil?
|
19
|
+
# return nil for dataset
|
20
|
+
return [trace_id, parent_span_id, nil, nil]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
[nil, nil, nil, nil]
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_v1(payload)
|
28
|
+
trace_id, parent_span_id, trace_flags = payload.split("-", 3)
|
29
|
+
|
30
|
+
if trace_flags.nil?
|
31
|
+
# if trace_flags is nil, it means a field is missing
|
32
|
+
return [nil, nil]
|
33
|
+
end
|
34
|
+
|
35
|
+
if trace_id == INVALID_TRACE_ID || parent_span_id == INVALID_SPAN_ID
|
36
|
+
return [nil, nil]
|
37
|
+
end
|
38
|
+
|
39
|
+
[trace_id, parent_span_id]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Serialize trace headers
|
44
|
+
module MarshalTraceContext
|
45
|
+
def to_trace_header
|
46
|
+
# do not propagate malformed ids
|
47
|
+
if trace.id =~ /^[A-Fa-f0-9]{32}$/ && id =~ /^[A-Fa-f0-9]{16}$/
|
48
|
+
return "00-#{trace.id}-#{id}-01"
|
49
|
+
end
|
50
|
+
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|