honeycomb-beeline 2.1.0 → 2.4.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 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