honeycomb-beeline 2.1.1 → 2.4.1

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: 8dc063924a92fcf2aa2ce4bbdcd3ab6bb79666a4d6266e957129f57bb131ee77
4
- data.tar.gz: 30111f935a4503b82321d30fbf752ff7462951c40c6249357543167d24890130
3
+ metadata.gz: b0f7fd0a816bb7817900181d110a0f95d63b3191aeca362bbe0aadbc18609d7a
4
+ data.tar.gz: '0702890fe4606c12f68ee032578a8b6f896fcc17c454794f23b3f0705a762c5a'
5
5
  SHA512:
6
- metadata.gz: 300c4652b990b2365eb4194702c16012c027beb7b13d396b0129d7ebe8a82a6bf848ab221d0158967f0e2c9d4bbc20d91715dae88f96b7d4f63714cdc03099f0
7
- data.tar.gz: 546ffeb8520ce66beb3d57223aac4e5131f9b0b0286c90618b11983e8a2821a1e8303ed9faf09a3022564956918a2dad688d8bce6bfa4e185c0e4a253fc72666
6
+ metadata.gz: 96266118829b42c517e4a20e5d42b7d6bea65d8d5d65dd750c78d9825517070f7d1c15167c1dd6d5502be585c9621229369fa036e0c84fa8482cf109b91fcad7
7
+ data.tar.gz: 9a9ced838db3c0b5948555042b80a1876270e36de75c69b490c8bf55c9150b32c59aa7d2db6e61bd845d131aa83e644e8116f12f8d266957b628a3672f79de54
data/.circleci/config.yml CHANGED
@@ -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
data/.editorconfig ADDED
@@ -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
@@ -0,0 +1,13 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "bundler" # See documentation for possible values
9
+ directory: "/gemfiles/" # Location of package manifests
10
+ schedule:
11
+ interval: "weekly"
12
+ reviewers:
13
+ - "honeycombio/integrations-team"
@@ -0,0 +1,16 @@
1
+ name: Apply project labels
2
+
3
+ on:
4
+ - issues
5
+ - label
6
+ - pull_request_target
7
+ - pull_request
8
+
9
+ jobs:
10
+ apply-labels:
11
+ runs-on: ubuntu-latest
12
+ name: Apply common project labels
13
+ steps:
14
+ - uses: honeycombio/integrations-labels@v1
15
+ with:
16
+ github-token: ${{ secrets.GITHUB_TOKEN }}
data/.rubocop.yml CHANGED
@@ -32,6 +32,12 @@ Metrics/LineLength:
32
32
  Metrics/ParameterLists:
33
33
  Max: 6
34
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
+
35
41
  Style/FrozenStringLiteralComment:
36
42
  EnforcedStyle: always
37
43
  Exclude:
data/CHANGELOG.md ADDED
@@ -0,0 +1,95 @@
1
+ # beeline-ruby changelog
2
+
3
+ ## 2.4.1 2021-06-01
4
+
5
+ ### Fixes
6
+
7
+ - Updates Redis event-field filter to handle string keys in options in
8
+ addition to symbol keys. (#147) [@cupakromer](https://github.com/cupakromer)
9
+
10
+ ### Maintenance
11
+
12
+ - Expanded on the Rails 5.2 example. (#141) [@robbkidd](https://github.com/robbkidd)
13
+ - Added a test case for current behavior of event emitted for an
14
+ exception raised in Rails. (@132) [@vreynolds](https://github.com/vreynolds)
15
+
16
+ ## 2.4.0 2021-01-07
17
+ ### Added
18
+ - Add support for HTTP Accept-Encoding header (#125) [@irvingreid](https://github.com/irvingreid)
19
+ - Add with_field, with_trace_field wrapper methods (#51) [@ajvondrak](https://github.com/ajvondrak)
20
+
21
+ ## 2.3.0 2020-11-06
22
+ ### Improvements
23
+ - Custom trace header hooks (#117)
24
+ - Add rspec filter :focus for assisting with debugging tests (#120)
25
+ - Be more lenient in expected output from AWS gem (#119)
26
+
27
+ ## 2.2.0 2020-09-02
28
+ ### New things
29
+ - refactor parsers/propagators, add w3c and aws parsers and propagators (#104) [@katiebayes](https://github.com/katiebayes)
30
+
31
+ ### Tiny fix
32
+ - Adjusted a threshold that should resolve the occasional build failures (#107) [@katiebayes](https://github.com/katiebayes)
33
+
34
+ ## 2.1.2 2020-08-26
35
+ ### Improvements
36
+ - reference current span in start_span (#105) [@rintaun](https://github.com/rintaun)
37
+ - switch trace and span ids over to w3c-supported formats (#100) [@katiebayes](https://github.com/katiebayes)
38
+
39
+ ## 2.1.1 2020-07-28
40
+ ### Fixes
41
+ - Remove children after sending | #98 | [@martin308](https://github.com/martin308)
42
+
43
+ ## 2.1.0 2020-06-10
44
+ ### Features
45
+ - Adding X-Forwarded-For to instrumented fields | #91 | [@paulosman](https://github.com/paulosman)
46
+ - Add request.header.accept_language field | #94 | [@timcraft](https://github.com/timcraft)
47
+ - Support custom notifications based on a regular expression | #92 | [@mrchucho](https://github.com/mrchucho)
48
+
49
+ ### Fixes
50
+ - Properly pass options for Ruby 2.7 | #85 | [@terracatta](https://github.com/terracatta)
51
+ - Fix regex substitution for warden and empty? errors for Rack | #88 | [@irvingreid](https://github.com/irvingreid)
52
+
53
+ ## 2.0.0 2020-03-10
54
+ See [release notes](https://github.com/honeycombio/beeline-ruby/releases/tag/v2.0.0)
55
+
56
+ ## 1.3.0 2019-11-20
57
+ ### Features
58
+ - redis integration | #42 | [@ajvondrak](https://github.com/ajvondrak)
59
+
60
+ ## 1.2.0 2019-11-04
61
+ ### Features
62
+ - aws-sdk v2 & v3 integration | #40 | [@ajvondrak](https://github.com/ajvondrak)
63
+
64
+ ## 1.1.1 2019-10-10
65
+ ### Fixes
66
+ - Skip params when unavailable | #39 | [@martin308](https://github.com/martin308)
67
+
68
+ ## 1.1.0 2019-10-07
69
+ ### Features
70
+ - Split rails and railtie integrations | #35 | [@martin308](https://github.com/martin308)
71
+
72
+ ## 1.0.1 2019-09-03
73
+ ### Fixes
74
+ - Set sample_hook and presend_hook on child spans | #26 | [@orangejulius](https://github.com/orangejulius)
75
+ - No-op if no client found in Faraday integration | #27 | [@Sergio-Mira](https://github.com/Sergio-Mira)
76
+
77
+ ## 1.0.0 2019-07-23
78
+ Version 1 is a milestone release. A complete re-write and modernization of Honeycomb's Ruby support.
79
+ See UPGRADING.md for migrating from v0.8.0 and see https://docs.honeycomb.io for full documentation.
80
+
81
+ ## 0.8.0 2019-05-06
82
+ ### Enhancements
83
+ - Expose event to #span block | #17 | [@eternal44](https://github.com/eternal44)
84
+
85
+ ## 0.7.0 2019-03-13
86
+ ### Enhancements
87
+ - Remove default inclusion of Sequel instrumentation | #12 | [@martin308](https://github.com/martin308)
88
+
89
+ ## 0.6.0 2018-11-29
90
+ ### Enhancements
91
+ - Tracing API and cross-process tracing | #4 | [@samstokes](https://github.com/samstokes)
92
+
93
+ ## 0.5.0 2018-11-29
94
+ ### Enhancements
95
+ - Improved rails support | #3 | [@samstokes](https://github.com/samstokes)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- honeycomb-beeline (2.1.1)
4
+ honeycomb-beeline (2.4.1)
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)
@@ -26,6 +29,7 @@ GEM
26
29
  docile (1.3.2)
27
30
  domain_name (0.5.20190701)
28
31
  unf (>= 0.0.5, < 1.0.0)
32
+ excon (0.82.0)
29
33
  ffi (1.12.2)
30
34
  ffi-compiler (1.0.1)
31
35
  ffi (>= 1.0.0)
@@ -39,12 +43,14 @@ GEM
39
43
  http-cookie (1.0.3)
40
44
  domain_name (~> 0.5)
41
45
  http-form_data (2.3.0)
42
- http-parser (1.2.1)
46
+ http-parser (1.2.3)
43
47
  ffi-compiler (>= 1.0, < 2.0)
44
48
  iniparse (1.5.0)
45
49
  jaro_winkler (1.5.4)
46
- libhoney (1.14.4)
50
+ json (2.3.1)
51
+ libhoney (1.18.0)
47
52
  addressable (~> 2.0)
53
+ excon
48
54
  http (>= 2.0, < 5.0)
49
55
  method_source (0.9.2)
50
56
  overcommit (0.46.0)
@@ -56,8 +62,8 @@ GEM
56
62
  pry (0.12.2)
57
63
  coderay (~> 1.1.0)
58
64
  method_source (~> 0.9.0)
59
- pry-byebug (3.7.0)
60
- byebug (~> 11.0)
65
+ pry-byebug (3.6.0)
66
+ byebug (~> 10.0)
61
67
  pry (~> 0.10)
62
68
  public_suffix (4.0.4)
63
69
  rainbow (3.0.0)
@@ -113,10 +119,11 @@ DEPENDENCIES
113
119
  appraisal
114
120
  bump
115
121
  bundler
122
+ codecov
116
123
  honeycomb-beeline!
117
124
  overcommit (~> 0.46.0)
118
125
  pry (< 0.13.0)
119
- pry-byebug (~> 3.7.0)
126
+ pry-byebug (~> 3.6.0)
120
127
  rake
121
128
  rspec (~> 3.0)
122
129
  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
data/UPGRADING.md CHANGED
@@ -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.1".freeze
6
+ VERSION = "2.4.1".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)
@@ -159,7 +159,17 @@ module Honeycomb
159
159
  # * :logger - just some Ruby object, not useful
160
160
  # * :_parsed - implementation detail
161
161
  def ignore?(option)
162
- %i[url password logger _parsed].include?(option)
162
+ # Redis options may be symbol or string keys.
163
+ #
164
+ # This normalizes `option` using `to_sym` as benchmarking on Ruby MRI
165
+ # v2.6.6 and v2.7.3 has shown that was faster compared to `to_s`.
166
+ # However, `nil` does not support `to_sym`. This uses a guard clause to
167
+ # handle the `nil` case because this is still faster than safe
168
+ # navigation. Also this lib still supports Ruby 2.2.0; which does not
169
+ # include safe navigation.
170
+ return true unless option
171
+
172
+ %i[url password logger _parsed].include?(option.to_sym)
163
173
  end
164
174
 
165
175
  def format(cmd)
@@ -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,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
@@ -85,6 +103,8 @@ module Honeycomb
85
103
 
86
104
  private
87
105
 
106
+ INVALID_SPAN_ID = ("00" * 8)
107
+
88
108
  attr_reader :event,
89
109
  :parent,
90
110
  :parent_id,
@@ -92,7 +112,17 @@ module Honeycomb
92
112
  :builder,
93
113
  :context,
94
114
  :presend_hook,
95
- :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
96
126
 
97
127
  def sent?
98
128
  @sent
@@ -156,5 +186,12 @@ module Honeycomb
156
186
  "mid"
157
187
  end
158
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
159
196
  end
160
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.1
4
+ version: 2.4.1
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-07-02 00:00:00.000000000 Z
11
+ date: 2021-06-01 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,17 @@ files:
222
236
  - ".circleci/bundler_version.sh"
223
237
  - ".circleci/config.yml"
224
238
  - ".circleci/setup-rubygems.sh"
239
+ - ".editorconfig"
240
+ - ".github/CODEOWNERS"
241
+ - ".github/dependabot.yml"
242
+ - ".github/workflows/apply-labels.yml"
225
243
  - ".gitignore"
226
244
  - ".overcommit.yml"
227
245
  - ".rspec"
228
246
  - ".rubocop.yml"
229
247
  - ".ruby-version"
230
248
  - Appraisals
249
+ - CHANGELOG.md
231
250
  - CODE_OF_CONDUCT.md
232
251
  - CONTRIBUTORS.md
233
252
  - Gemfile
@@ -258,6 +277,10 @@ files:
258
277
  - lib/honeycomb/integrations/sinatra.rb
259
278
  - lib/honeycomb/integrations/warden.rb
260
279
  - lib/honeycomb/propagation.rb
280
+ - lib/honeycomb/propagation/aws.rb
281
+ - lib/honeycomb/propagation/context.rb
282
+ - lib/honeycomb/propagation/honeycomb.rb
283
+ - lib/honeycomb/propagation/w3c.rb
261
284
  - lib/honeycomb/rollup_fields.rb
262
285
  - lib/honeycomb/span.rb
263
286
  - lib/honeycomb/trace.rb
@@ -283,7 +306,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
283
306
  - !ruby/object:Gem::Version
284
307
  version: '0'
285
308
  requirements: []
286
- rubygems_version: 3.0.3
309
+ rubygems_version: 3.0.3.1
287
310
  signing_key:
288
311
  specification_version: 4
289
312
  summary: Instrument your Ruby apps with Honeycomb