honeycomb-beeline 2.1.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: deebe936665edd1d51d0526c7089c0bfa29e666722f8ccff89bf731ce3fd5def
4
- data.tar.gz: 9f4534f0b56c15c113a9c72d9e7b237a243c6ab6208af1bda1f1c79b02b1c7c8
3
+ metadata.gz: eaa9bbe8dbe9bfb0de78101ff1809ed07b87535b4d8abc19468d3285cbd60300
4
+ data.tar.gz: b31e267934ac0edf772eddfee5e3cd0be333c7469ae267fa706b670da15d5453
5
5
  SHA512:
6
- metadata.gz: f9a2f7c6a0c3bc6b92a82672aaeaf2a24a02c703d9830670448acaac6838b7286c91d33af3d9a20049891bde140f1aac1c6f7b615b92b7aa37a0f1f69ab3bddd
7
- data.tar.gz: 65f892432bc418c44d3bb482b8892a5cc0dccbf0a24ff8f051c78de4769e7e61a5d0efdb6b39b015beb759da0be9354eefb3420133d8670477c7b36f63c3ea1f
6
+ metadata.gz: 6b294598dae62f56f1ebfef7c00d8c13fd2558976797c60bb3f95c75ad68ff7f0fef77888d1e2f7c622689c080a73448e6c41f2ba436ef255d888126915839f6
7
+ data.tar.gz: b3b4b6746f630198c69b8c324004d088878000022654008fe6a8c88d88828424aad0646d5bd0fc6c0b122774acd98775f10bddcc84d311ef7330859d21ced655
@@ -26,16 +26,53 @@ commands:
26
26
  - run: BUNDLE_GEMFILE=<< parameters.gemfile >> << parameters.command >>
27
27
 
28
28
  jobs:
29
- publish:
29
+ build_artifacts:
30
30
  docker:
31
- - image: circleci/ruby:2.6
31
+ - image: circleci/ruby:2.6
32
32
  steps:
33
33
  - checkout
34
+ - run: mkdir -p ~/artifacts
35
+ - run: gem build honeycomb-beeline.gemspec
36
+ - run: cp honeycomb-beeline-*.gem ~/artifacts/
37
+ - persist_to_workspace:
38
+ root: ~/
39
+ paths:
40
+ - artifacts
41
+ - store_artifacts:
42
+ path: ~/artifacts
43
+
44
+ publish_rubygems:
45
+ docker:
46
+ - image: circleci/ruby:2.6
47
+ steps:
48
+ - attach_workspace:
49
+ at: ~/
50
+ - run:
51
+ name: "Artifacts being published"
52
+ command: |
53
+ echo "about to publish to tag ${CIRCLE_TAG}"
54
+ ls -l ~/artifacts/*
55
+ - checkout
34
56
  - run:
35
57
  name: Setup Rubygems
36
58
  command: bash .circleci/setup-rubygems.sh
37
- - run: gem build honeycomb-beeline.gemspec
38
- - run: gem push honeycomb-beeline-*.gem
59
+ - run: gem push ~/artifacts/honeycomb-beeline-*.gem
60
+
61
+ publish_github:
62
+ docker:
63
+ - image: cibuilds/github:0.13.0
64
+ steps:
65
+ - attach_workspace:
66
+ at: ~/
67
+ - run:
68
+ name: "Artifacts being published"
69
+ command: |
70
+ echo "about to publish to tag ${CIRCLE_TAG}"
71
+ ls -l ~/artifacts/*
72
+ - run:
73
+ name: "GHR Draft"
74
+ command: ghr -draft -n ${CIRCLE_TAG} -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} ${CIRCLE_TAG} ~/artifacts
75
+
39
76
  lint:
40
77
  parameters:
41
78
  ruby-version:
@@ -69,7 +106,7 @@ workflows:
69
106
  filters:
70
107
  branches:
71
108
  only:
72
- - master
109
+ - main
73
110
  jobs:
74
111
  - lint
75
112
  - test: &test
@@ -77,7 +114,7 @@ workflows:
77
114
  - lint
78
115
  matrix:
79
116
  parameters:
80
- ruby-version: ["2.3", "2.4", "2.5", "2.6", "2.7"]
117
+ ruby-version: ["2.2", "2.3", "2.4", "2.5", "2.6", "2.7"]
81
118
  gemfile:
82
119
  - gemfiles/aws_2.gemfile
83
120
  - gemfiles/aws_3.gemfile
@@ -96,6 +133,12 @@ workflows:
96
133
  - gemfiles/redis_3.gemfile
97
134
  - gemfiles/redis_4.gemfile
98
135
  exclude:
136
+ - ruby-version: "2.2"
137
+ gemfile: gemfiles/faraday_1.gemfile
138
+ - ruby-version: "2.2"
139
+ gemfile: gemfiles/rails_52.gemfile
140
+ - ruby-version: "2.2"
141
+ gemfile: gemfiles/rails_6.gemfile
99
142
  - ruby-version: "2.3"
100
143
  gemfile: gemfiles/rails_6.gemfile
101
144
  - ruby-version: "2.4"
@@ -113,16 +156,14 @@ workflows:
113
156
  beeline:
114
157
  jobs:
115
158
  - lint:
116
- filters:
159
+ filters: &regular_filters
117
160
  tags:
118
161
  only: /.*/
119
162
  - test:
120
163
  <<: *test
121
- filters:
122
- tags:
123
- only: /.*/
124
- - publish:
125
- filters:
164
+ filters: *regular_filters
165
+ - build_artifacts:
166
+ filters: &tag_filters
126
167
  tags:
127
168
  only: /^v.*/
128
169
  branches:
@@ -130,3 +171,10 @@ workflows:
130
171
  requires:
131
172
  - lint
132
173
  - test
174
+ - publish_rubygems: &publish
175
+ filters: *tag_filters
176
+ requires:
177
+ - build_artifacts
178
+ - publish_github:
179
+ <<: *publish
180
+ context: Honeycomb Secrets for Public Repos
@@ -0,0 +1,12 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 2
6
+ charset = utf-8
7
+ trim_trailing_whitespace = true
8
+ insert_final_newline = true
9
+
10
+ [*.md]
11
+ indent_size = 4
12
+ trim_trailing_whitespace = true
@@ -0,0 +1,5 @@
1
+ # Code owners file.
2
+ # This file controls who is tagged for review for any given pull request.
3
+
4
+ # For anything not explicitly taken by someone else:
5
+ * @honeycombio/integrations-team @martin308
@@ -29,6 +29,15 @@ Metrics/LineLength:
29
29
  Exclude:
30
30
  - spec/support/event_data_shared_examples.rb
31
31
 
32
+ Metrics/ParameterLists:
33
+ Max: 6
34
+
35
+ Style/AccessModifierDeclarations:
36
+ Exclude:
37
+ - lib/honeycomb/propagation/aws.rb
38
+ - lib/honeycomb/propagation/w3c.rb
39
+ - lib/honeycomb/propagation/honeycomb.rb
40
+
32
41
  Style/FrozenStringLiteralComment:
33
42
  EnforcedStyle: always
34
43
  Exclude:
@@ -0,0 +1,82 @@
1
+ # beeline-ruby changelog
2
+
3
+ ## 2.4.0 2021-01-07
4
+ ### Added
5
+ - Add support for HTTP Accept-Encoding header (#125) [@irvingreid](https://github.com/irvingreid)
6
+ - Add with_field, with_trace_field wrapper methods (#51) [@ajvondrak](https://github.com/ajvondrak)
7
+
8
+ ## 2.3.0 2020-11-06
9
+ ### Improvements
10
+ - Custom trace header hooks (#117)
11
+ - Add rspec filter :focus for assisting with debugging tests (#120)
12
+ - Be more lenient in expected output from AWS gem (#119)
13
+
14
+ ## 2.2.0 2020-09-02
15
+ ### New things
16
+ - refactor parsers/propagators, add w3c and aws parsers and propagators (#104) [@katiebayes](https://github.com/katiebayes)
17
+
18
+ ### Tiny fix
19
+ - Adjusted a threshold that should resolve the occasional build failures (#107) [@katiebayes](https://github.com/katiebayes)
20
+
21
+ ## 2.1.2 2020-08-26
22
+ ### Improvements
23
+ - reference current span in start_span (#105) [@rintaun](https://github.com/rintaun)
24
+ - switch trace and span ids over to w3c-supported formats (#100) [@katiebayes](https://github.com/katiebayes)
25
+
26
+ ## 2.1.1 2020-07-28
27
+ ### Fixes
28
+ - Remove children after sending | #98 | [@martin308](https://github.com/martin308)
29
+
30
+ ## 2.1.0 2020-06-10
31
+ ### Features
32
+ - Adding X-Forwarded-For to instrumented fields | #91 | [@paulosman](https://github.com/paulosman)
33
+ - Add request.header.accept_language field | #94 | [@timcraft](https://github.com/timcraft)
34
+ - Support custom notifications based on a regular expression | #92 | [@mrchucho](https://github.com/mrchucho)
35
+
36
+ ### Fixes
37
+ - Properly pass options for Ruby 2.7 | #85 | [@terracatta](https://github.com/terracatta)
38
+ - Fix regex substitution for warden and empty? errors for Rack | #88 | [@irvingreid](https://github.com/irvingreid)
39
+
40
+ ## 2.0.0 2020-03-10
41
+ See [release notes](https://github.com/honeycombio/beeline-ruby/releases/tag/v2.0.0)
42
+
43
+ ## 1.3.0 2019-11-20
44
+ ### Features
45
+ - redis integration | #42 | [@ajvondrak](https://github.com/ajvondrak)
46
+
47
+ ## 1.2.0 2019-11-04
48
+ ### Features
49
+ - aws-sdk v2 & v3 integration | #40 | [@ajvondrak](https://github.com/ajvondrak)
50
+
51
+ ## 1.1.1 2019-10-10
52
+ ### Fixes
53
+ - Skip params when unavailable | #39 | [@martin308](https://github.com/martin308)
54
+
55
+ ## 1.1.0 2019-10-07
56
+ ### Features
57
+ - Split rails and railtie integrations | #35 | [@martin308](https://github.com/martin308)
58
+
59
+ ## 1.0.1 2019-09-03
60
+ ### Fixes
61
+ - Set sample_hook and presend_hook on child spans | #26 | [@orangejulius](https://github.com/orangejulius)
62
+ - No-op if no client found in Faraday integration | #27 | [@Sergio-Mira](https://github.com/Sergio-Mira)
63
+
64
+ ## 1.0.0 2019-07-23
65
+ Version 1 is a milestone release. A complete re-write and modernization of Honeycomb's Ruby support.
66
+ See UPGRADING.md for migrating from v0.8.0 and see https://docs.honeycomb.io for full documentation.
67
+
68
+ ## 0.8.0 2019-05-06
69
+ ### Enhancements
70
+ - Expose event to #span block | #17 | [@eternal44](https://github.com/eternal44)
71
+
72
+ ## 0.7.0 2019-03-13
73
+ ### Enhancements
74
+ - Remove default inclusion of Sequel instrumentation | #12 | [@martin308](https://github.com/martin308)
75
+
76
+ ## 0.6.0 2018-11-29
77
+ ### Enhancements
78
+ - Tracing API and cross-process tracing | #4 | [@samstokes](https://github.com/samstokes)
79
+
80
+ ## 0.5.0 2018-11-29
81
+ ### Enhancements
82
+ - Improved rails support | #3 | [@samstokes](https://github.com/samstokes)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- honeycomb-beeline (2.1.0)
4
+ honeycomb-beeline (2.4.0)
5
5
  libhoney (~> 1.14, >= 1.14.2)
6
6
 
7
7
  GEM
@@ -16,9 +16,12 @@ GEM
16
16
  thor (>= 0.14.0)
17
17
  ast (2.4.0)
18
18
  bump (0.9.0)
19
- byebug (11.1.3)
19
+ byebug (10.0.2)
20
20
  childprocess (0.9.0)
21
21
  ffi (~> 1.0, >= 1.0.11)
22
+ codecov (0.2.8)
23
+ json
24
+ simplecov
22
25
  coderay (1.1.2)
23
26
  crack (0.4.3)
24
27
  safe_yaml (~> 1.0.0)
@@ -43,7 +46,8 @@ GEM
43
46
  ffi-compiler (>= 1.0, < 2.0)
44
47
  iniparse (1.5.0)
45
48
  jaro_winkler (1.5.4)
46
- libhoney (1.14.4)
49
+ json (2.3.1)
50
+ libhoney (1.14.5)
47
51
  addressable (~> 2.0)
48
52
  http (>= 2.0, < 5.0)
49
53
  method_source (0.9.2)
@@ -56,8 +60,8 @@ GEM
56
60
  pry (0.12.2)
57
61
  coderay (~> 1.1.0)
58
62
  method_source (~> 0.9.0)
59
- pry-byebug (3.7.0)
60
- byebug (~> 11.0)
63
+ pry-byebug (3.6.0)
64
+ byebug (~> 10.0)
61
65
  pry (~> 0.10)
62
66
  public_suffix (4.0.4)
63
67
  rainbow (3.0.0)
@@ -113,10 +117,11 @@ DEPENDENCIES
113
117
  appraisal
114
118
  bump
115
119
  bundler
120
+ codecov
116
121
  honeycomb-beeline!
117
122
  overcommit (~> 0.46.0)
118
123
  pry (< 0.13.0)
119
- pry-byebug (~> 3.7.0)
124
+ pry-byebug (~> 3.6.0)
120
125
  rake
121
126
  rspec (~> 3.0)
122
127
  rubocop (< 0.69)
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Build Status](https://circleci.com/gh/honeycombio/beeline-ruby.svg?style=svg)](https://circleci.com/gh/honeycombio/beeline-ruby)
4
4
  [![Gem Version](https://badge.fury.io/rb/honeycomb-beeline.svg)](https://badge.fury.io/rb/honeycomb-beeline)
5
+ [![codecov](https://codecov.io/gh/honeycombio/beeline-ruby/branch/main/graph/badge.svg)](https://codecov.io/gh/honeycombio/beeline-ruby)
5
6
 
6
7
  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
8
  - [Usage and Examples](https://docs.honeycomb.io/getting-data-in/beelines/ruby-beeline/)
@@ -24,6 +25,13 @@ Built in instrumentation for:
24
25
  - Sequel
25
26
  - Sinatra
26
27
 
28
+ ## Testing
29
+ Find `rspec` test files in the `spec` directory.
30
+
31
+ To run tests on gem-specific instrumentations or across various dependency versions, use [appraisal](https://github.com/thoughtbot/appraisal) (further instructions in the readme for that gem). Find gem sets in the `Appraisals` config.
32
+
33
+ To run a specific file: `bundle exec appraisal <gem set> rspec <path/to/file>`
34
+
27
35
  ## Get in touch
28
36
 
29
37
  Please reach out to [support@honeycomb.io](mailto:support@honeycomb.io) or ping
@@ -1,5 +1,15 @@
1
1
  # Upgrade Guide
2
2
 
3
+ ## 1.0.0 - 2.0.0
4
+
5
+ 1. See release notes: https://github.com/honeycombio/beeline-ruby/releases/tag/v2.0.0
6
+ 1. This update requires no code changes, but you must be aware of certain instrumentation changes. New fields will be added to your dataset and other fields will be removed.
7
+ 1. ActionController::Parameters will now result in extra fields, or nested json, depending on your unfurl settings.
8
+ 1. aws.params are now exploded into separate fields.
9
+ 1. request.error becomes error.
10
+ 1. request.error_detail becomes error_detail
11
+ 1. request.protocol becomes request.scheme
12
+
3
13
  ## 0.8.0 - 1.0.0
4
14
 
5
15
  1. If you have a web application, remove beeline configuration from the `config.ru` file
@@ -42,9 +42,10 @@ Gem::Specification.new do |spec|
42
42
  spec.add_development_dependency "appraisal"
43
43
  spec.add_development_dependency "bump"
44
44
  spec.add_development_dependency "bundler"
45
+ spec.add_development_dependency "codecov"
45
46
  spec.add_development_dependency "overcommit", "~> 0.46.0"
46
47
  spec.add_development_dependency "pry", "< 0.13.0"
47
- spec.add_development_dependency "pry-byebug", "~> 3.7.0"
48
+ spec.add_development_dependency "pry-byebug", "~> 3.6.0"
48
49
  spec.add_development_dependency "rake"
49
50
  spec.add_development_dependency "rspec", "~> 3.0"
50
51
  spec.add_development_dependency "rubocop", "< 0.69"
@@ -26,7 +26,8 @@ module Honeycomb
26
26
  attr_reader :client
27
27
 
28
28
  def_delegators :@client, :libhoney, :start_span, :add_field,
29
- :add_field_to_trace, :current_span, :current_trace
29
+ :add_field_to_trace, :current_span, :current_trace,
30
+ :with_field, :with_trace_field
30
31
 
31
32
  def configure
32
33
  Configuration.new.tap do |config|
@@ -3,7 +3,7 @@
3
3
  module Honeycomb
4
4
  module Beeline
5
5
  NAME = "honeycomb-beeline".freeze
6
- VERSION = "2.1.0".freeze
6
+ VERSION = "2.4.0".freeze
7
7
  USER_AGENT_SUFFIX = "#{NAME}/#{VERSION}".freeze
8
8
  end
9
9
  end
@@ -35,6 +35,8 @@ module Honeycomb
35
35
  @additional_trace_options = {
36
36
  presend_hook: configuration.presend_hook,
37
37
  sample_hook: configuration.sample_hook,
38
+ parser_hook: configuration.http_trace_parser_hook,
39
+ propagation_hook: configuration.http_trace_propagation_hook,
38
40
  }
39
41
 
40
42
  configuration.after_initialize(self)
@@ -54,24 +56,24 @@ module Honeycomb
54
56
  context.current_span.create_child
55
57
  end
56
58
 
59
+ current_span = context.current_span
60
+
57
61
  fields.each do |key, value|
58
- context.current_span.add_field(key, value)
62
+ current_span.add_field(key, value)
59
63
  end
60
64
 
61
- context.current_span.add_field("name", name)
62
-
63
- if block_given?
64
- begin
65
- yield context.current_span
66
- rescue StandardError => e
67
- context.current_span.add_field("error", e.class.name)
68
- context.current_span.add_field("error_detail", e.message)
69
- raise e
70
- ensure
71
- context.current_span.send
72
- end
73
- else
74
- context.current_span
65
+ current_span.add_field("name", name)
66
+
67
+ return current_span unless block_given?
68
+
69
+ begin
70
+ yield current_span
71
+ rescue StandardError => e
72
+ current_span.add_field("error", e.class.name)
73
+ current_span.add_field("error_detail", e.message)
74
+ raise e
75
+ ensure
76
+ current_span.send
75
77
  end
76
78
  end
77
79
 
@@ -87,6 +89,14 @@ module Honeycomb
87
89
  context.current_span.trace.add_field("app.#{key}", value)
88
90
  end
89
91
 
92
+ def with_field(key)
93
+ yield.tap { |value| add_field(key, value) }
94
+ end
95
+
96
+ def with_trace_field(key)
97
+ yield.tap { |value| add_field_to_trace(key, value) }
98
+ end
99
+
90
100
  private
91
101
 
92
102
  attr_reader :context
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "socket"
4
+ require "honeycomb/propagation/honeycomb"
4
5
 
5
6
  module Honeycomb
6
7
  # Used to configure the Honeycomb client
@@ -60,5 +61,27 @@ module Honeycomb
60
61
  @sample_hook
61
62
  end
62
63
  end
64
+
65
+ def http_trace_parser_hook(&hook)
66
+ if block_given?
67
+ @http_trace_parser_hook = hook
68
+ elsif @http_trace_parser_hook
69
+ @http_trace_parser_hook
70
+ else
71
+ # by default we try to parse incoming honeycomb traces
72
+ HoneycombPropagation::UnmarshalTraceContext.method(:parse_rack_env)
73
+ end
74
+ end
75
+
76
+ def http_trace_propagation_hook(&hook)
77
+ if block_given?
78
+ @http_trace_propagation_hook = hook
79
+ elsif @http_trace_propagation_hook
80
+ @http_trace_propagation_hook
81
+ else
82
+ # by default we send outgoing honeycomb trace headers
83
+ HoneycombPropagation::MarshalTraceContext.method(:parse_faraday_env)
84
+ end
85
+ end
63
86
  end
64
87
  end
@@ -22,7 +22,9 @@ module Honeycomb
22
22
  span.add_field "meta.package", "faraday"
23
23
  span.add_field "meta.package_version", ::Faraday::VERSION
24
24
 
25
- env.request_headers["X-Honeycomb-Trace"] = span.to_trace_header
25
+ if (headers = span.trace_headers(env)).is_a?(Hash)
26
+ env.request_headers.merge!(headers)
27
+ end
26
28
 
27
29
  @app.call(env).tap do |response|
28
30
  span.add_field "response.status_code", response.status
@@ -17,6 +17,7 @@ module Honeycomb
17
17
  ["HTTP_X_FORWARDED_PROTO", "request.header.x_forwarded_proto"],
18
18
  ["HTTP_X_FORWARDED_PORT", "request.header.x_forwarded_port"],
19
19
  ["HTTP_ACCEPT", "request.header.accept"],
20
+ ["HTTP_ACCEPT_ENCODING", "request.header.accept_encoding"],
20
21
  ["HTTP_ACCEPT_LANGUAGE", "request.header.accept_language"],
21
22
  ["CONTENT_TYPE", "request.header.content_type"],
22
23
  ["HTTP_USER_AGENT", "request.header.user_agent"],
@@ -32,8 +33,10 @@ module Honeycomb
32
33
 
33
34
  def call(env)
34
35
  req = ::Rack::Request.new(env)
35
- hny = env["HTTP_X_HONEYCOMB_TRACE"]
36
- client.start_span(name: "http_request", serialized_trace: hny) do |span|
36
+ client.start_span(
37
+ name: "http_request",
38
+ serialized_trace: env,
39
+ ) do |span|
37
40
  add_field = lambda do |key, value|
38
41
  unless value.nil? || (value.respond_to?(:empty?) && value.empty?)
39
42
  span.add_field(key, value)
@@ -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
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "securerandom"
4
3
  require "forwardable"
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
 
@@ -24,7 +25,7 @@ module Honeycomb
24
25
  builder:,
25
26
  context:,
26
27
  **options)
27
- @id = SecureRandom.uuid
28
+ @id = generate_span_id
28
29
  @context = context
29
30
  @context.current_span = self
30
31
  @builder = builder
@@ -34,26 +35,41 @@ 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
- def parse_options(parent_id: nil,
41
+ def parse_options(parent: nil,
42
+ parent_id: nil,
40
43
  is_root: parent_id.nil?,
41
- sample_hook: nil,
42
- presend_hook: nil,
44
+ _sample_hook: nil,
45
+ _presend_hook: nil,
43
46
  **_options)
47
+ @parent = parent
48
+ # parent_id should be removed in the next major version bump. It has been
49
+ # replaced with passing the actual parent in. This is kept for backwards
50
+ # compatability
44
51
  @parent_id = parent_id
45
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)
46
59
  @presend_hook = presend_hook
47
60
  @sample_hook = sample_hook
61
+ @propagation_hook = propagation_hook
48
62
  end
49
63
 
50
64
  def create_child
51
65
  self.class.new(trace: trace,
52
66
  builder: builder,
53
67
  context: context,
68
+ parent: self,
54
69
  parent_id: id,
55
70
  sample_hook: sample_hook,
56
- presend_hook: presend_hook).tap do |c|
71
+ presend_hook: presend_hook,
72
+ propagation_hook: propagation_hook).tap do |c|
57
73
  children << c
58
74
  end
59
75
  end
@@ -64,6 +80,14 @@ module Honeycomb
64
80
  send_internal
65
81
  end
66
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
+
67
91
  protected
68
92
 
69
93
  def send_by_parent
@@ -73,15 +97,32 @@ module Honeycomb
73
97
  send_internal
74
98
  end
75
99
 
100
+ def remove_child(child)
101
+ children.delete child
102
+ end
103
+
76
104
  private
77
105
 
106
+ INVALID_SPAN_ID = ("00" * 8)
107
+
78
108
  attr_reader :event,
109
+ :parent,
79
110
  :parent_id,
80
111
  :children,
81
112
  :builder,
82
113
  :context,
83
114
  :presend_hook,
84
- :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
85
126
 
86
127
  def sent?
87
128
  @sent
@@ -92,14 +133,7 @@ module Honeycomb
92
133
  end
93
134
 
94
135
  def send_internal
95
- add_field "duration_ms", duration_ms
96
- add_field "trace.trace_id", trace.id
97
- add_field "trace.span_id", id
98
- add_field "meta.span_type", span_type
99
- parent_id && add_field("trace.parent_id", parent_id)
100
- add rollup_fields
101
- add trace.fields
102
- span_type == "root" && add(trace.rollup_fields)
136
+ add_additional_fields
103
137
  send_children
104
138
  sample = true
105
139
  if sample_hook.nil?
@@ -114,6 +148,19 @@ module Honeycomb
114
148
  end
115
149
  @sent = true
116
150
  context.span_sent(self)
151
+
152
+ parent && parent.remove_child(self)
153
+ end
154
+
155
+ def add_additional_fields
156
+ add_field "duration_ms", duration_ms
157
+ add_field "trace.trace_id", trace.id
158
+ add_field "trace.span_id", id
159
+ add_field "meta.span_type", span_type
160
+ parent_id && add_field("trace.parent_id", parent_id)
161
+ add rollup_fields
162
+ add trace.fields
163
+ span_type == "root" && add(trace.rollup_fields)
117
164
  end
118
165
 
119
166
  def send_children
@@ -139,5 +186,12 @@ module Honeycomb
139
186
  "mid"
140
187
  end
141
188
  end
189
+
190
+ def generate_span_id
191
+ loop do
192
+ id = SecureRandom.hex(8)
193
+ return id unless id == INVALID_SPAN_ID
194
+ end
195
+ end
142
196
  end
143
197
  end
@@ -19,9 +19,10 @@ 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
- @id = trace_id || SecureRandom.uuid
25
+ @id = trace_id || generate_trace_id
25
26
  @fields = trace_fields || {}
26
27
  @root_span = Span.new(trace: self,
27
28
  parent_id: parent_span_id,
@@ -34,5 +35,28 @@ module Honeycomb
34
35
  def add_field(key, value)
35
36
  @fields[key] = value
36
37
  end
38
+
39
+ private
40
+
41
+ INVALID_TRACE_ID = ("00" * 16)
42
+
43
+ def generate_trace_id
44
+ loop do
45
+ id = SecureRandom.hex(16)
46
+ return id unless id == INVALID_TRACE_ID
47
+ end
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
37
61
  end
38
62
  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: 2.1.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Holman
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-10 00:00:00.000000000 Z
11
+ date: 2021-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: libhoney
@@ -72,6 +72,20 @@ dependencies:
72
72
  - - ">="
73
73
  - !ruby/object:Gem::Version
74
74
  version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: codecov
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
75
89
  - !ruby/object:Gem::Dependency
76
90
  name: overcommit
77
91
  requirement: !ruby/object:Gem::Requirement
@@ -106,14 +120,14 @@ dependencies:
106
120
  requirements:
107
121
  - - "~>"
108
122
  - !ruby/object:Gem::Version
109
- version: 3.7.0
123
+ version: 3.6.0
110
124
  type: :development
111
125
  prerelease: false
112
126
  version_requirements: !ruby/object:Gem::Requirement
113
127
  requirements:
114
128
  - - "~>"
115
129
  - !ruby/object:Gem::Version
116
- version: 3.7.0
130
+ version: 3.6.0
117
131
  - !ruby/object:Gem::Dependency
118
132
  name: rake
119
133
  requirement: !ruby/object:Gem::Requirement
@@ -222,12 +236,15 @@ files:
222
236
  - ".circleci/bundler_version.sh"
223
237
  - ".circleci/config.yml"
224
238
  - ".circleci/setup-rubygems.sh"
239
+ - ".editorconfig"
240
+ - ".github/CODEOWNERS"
225
241
  - ".gitignore"
226
242
  - ".overcommit.yml"
227
243
  - ".rspec"
228
244
  - ".rubocop.yml"
229
245
  - ".ruby-version"
230
246
  - Appraisals
247
+ - CHANGELOG.md
231
248
  - CODE_OF_CONDUCT.md
232
249
  - CONTRIBUTORS.md
233
250
  - Gemfile
@@ -258,6 +275,10 @@ files:
258
275
  - lib/honeycomb/integrations/sinatra.rb
259
276
  - lib/honeycomb/integrations/warden.rb
260
277
  - lib/honeycomb/propagation.rb
278
+ - lib/honeycomb/propagation/aws.rb
279
+ - lib/honeycomb/propagation/context.rb
280
+ - lib/honeycomb/propagation/honeycomb.rb
281
+ - lib/honeycomb/propagation/w3c.rb
261
282
  - lib/honeycomb/rollup_fields.rb
262
283
  - lib/honeycomb/span.rb
263
284
  - lib/honeycomb/trace.rb