`
38 |
39 | ## Get in touch
40 |
41 | Please reach out to [support@honeycomb.io](mailto:support@honeycomb.io) or ping
42 | us with the chat bubble on [our website](https://www.honeycomb.io) for any
43 | assistance. We also welcome [bug reports](https://github.com/honeycombio/beeline-ruby/issues).
44 |
45 | ## Contributions
46 |
47 | Features, bug fixes and other changes to `beeline-ruby` are gladly accepted. Please
48 | open issues or a pull request with your change. Remember to add your name to the
49 | CONTRIBUTORS file!
50 |
51 | All contributions will be released under the Apache License 2.0.
52 |
--------------------------------------------------------------------------------
/RELEASING.md:
--------------------------------------------------------------------------------
1 | # Releasing
2 |
3 | - Update the version number in `lib/honeycomb/beeline/version.rb`.
4 | - Update `CHANGELOG.md` with the changes since the last release. Consider automating with a command such as these two:
5 | - `git log $(git describe --tags --abbrev=0)..HEAD --no-merges --oneline > new-in-this-release.log`
6 | - `git log --pretty='%C(green)%d%Creset- %s | %an'`
7 | - Commit changes, push, and open a release preparation pull request for review.
8 | - Once the pull request is merged, fetch the updated `main` branch.
9 | - Apply a tag for the new version on the merged commit (e.g. `git tag -a v2.3.1 -m "v2.3.1"`)
10 | - Push the tag upstream (this will kick off the release pipeline in CI) e.g. `git push origin v2.3.1`
11 | - Ensure that there is a draft GitHub release created as part of CI publish steps (this will also publish to Gems registry).
12 | - Click "generate release notes" in GitHub for full changelog notes and any new contributors
13 | - Publish the draft release in GitHub.
14 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "bundler/gem_tasks"
4 | require "rspec/core/rake_task"
5 | require "rubocop/rake_task"
6 | require "appraisal"
7 |
8 | RSpec::Core::RakeTask.new(:spec)
9 |
10 | RuboCop::RakeTask.new(:rubocop)
11 |
12 | task test: :spec
13 |
14 | task default: %i[rubocop test]
15 |
16 | !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"] && task(default: :appraisal)
17 |
18 | namespace :coverage do
19 | desc "Collates all result sets generated by the different test runners"
20 | task :report do
21 | require "simplecov"
22 |
23 | SimpleCov.collate Dir["coverage/**/.resultset.json"] do
24 | add_group "Integrations", "lib/honeycomb/integrations"
25 | add_group "Propagation", "lib/honeycomb/propagation"
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | This security policy applies to public projects under the [honeycombio organization][gh-organization] on GitHub.
4 | For security reports involving the services provided at `(ui|ui-eu|api|api-eu).honeycomb.io`, refer to the [Honeycomb Bug Bounty Program][bugbounty] for scope, expectations, and reporting procedures.
5 |
6 | ## Security/Bugfix Versions
7 |
8 | Security and bug fixes are generally provided only for the last minor version.
9 | Fixes are released either as part of the next minor version or as an on-demand patch version.
10 |
11 | Security fixes are given priority and might be enough to cause a new version to be released.
12 |
13 | ## Reporting a Vulnerability
14 |
15 | We encourage responsible disclosure of security vulnerabilities.
16 | If you find something suspicious, we encourage and appreciate your report!
17 |
18 | ### Ways to report
19 |
20 | In order for the vulnerability reports to reach maintainers as soon as possible, the preferred way is to use the "Report a vulnerability" button under the "Security" tab of the associated GitHub project.
21 | This creates a private communication channel between the reporter and the maintainers.
22 |
23 | If you are absolutely unable to or have strong reasons not to use GitHub's vulnerability reporting workflow, please reach out to the Honeycomb security team at [security@honeycomb.io](mailto:security@honeycomb.io).
24 |
25 | [gh-organization]: https://github.com/honeycombio
26 | [bugbounty]: https://www.honeycomb.io/bugbountyprogram
27 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # How to Get Help
2 |
3 | This project uses GitHub issues to track bugs, feature requests, and questions about using the project. Please search for existing issues before filing a new one.
4 |
--------------------------------------------------------------------------------
/UPGRADING.md:
--------------------------------------------------------------------------------
1 | # Upgrade Guide
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 |
13 | ## 0.8.0 - 1.0.0
14 |
15 | 1. If you have a web application, remove beeline configuration from the `config.ru` file
16 | 1. If you have a rails application, run the honeycomb generator `bundle exec rails generate honeycomb {writekey} --dataset {dataset}`
17 | 1. Replace call to `Honeycomb.init` with the following (if using rails, this will now live in `config/initializers/honeycomb.rb`)
18 | ```ruby
19 | Honeycomb.configure do |config|
20 | config.write_key = "{writekey}"
21 | config.dataset = "{dataset}"
22 | end
23 | ```
24 | 1. Replace any `Rack::Honeycomb.add_field` calls with the following
25 | ```ruby
26 | Honeycomb.add_field("name", "value")
27 | ```
28 | 1. Replace any `Honeycomb.span` calls with the following
29 | ```ruby
30 | Honeycomb.start_span(name: "interesting") do |span|
31 | span.add_field("name", "value")
32 | end
33 | ```
34 |
35 | ## honeycomb-rails to beeline-ruby
36 |
37 | 1. Update Gemfile, remove `honeycomb-rails` and add `honeycomb-beeline`
38 | 1. Run `bundle install`
39 | 1. Remove the `honeycomb.rb` initializer from `config/initializers`
40 | 1. Add the following to the `config.ru` file
41 | ```ruby
42 | # config.ru
43 | require 'honeycomb-beeline'
44 |
45 | Honeycomb.init(writekey: 'YOUR_API_KEY', dataset: 'YOUR_DATASET')
46 |
47 | # these next two lines should already exist in some form in this file, it's important to init the honeycomb library before this
48 | require ::File.expand_path('../config/environment', __FILE__)
49 | run Rails.application
50 | ```
51 | 1. You can use the same write key and dataset from the honeycomb initialiser above, note: the honeycomb-beeline only supports sending events to one dataset. This is due to the fact that the new beeline will include traces for your application by default and these are only viewable from within the same dataset
52 | 1. Replace any `honeycomb_metadata` calls in your controllers like the following
53 | ```ruby
54 | def index
55 | @bees = Bee.all
56 | Rack::Honeycomb.add_field(request.env, :bees_count, @bees.count)
57 | # honeycomb_metadata[:bees_count] = @bees.count
58 | end
59 | ```
60 | 1. If you are manually using the libhoney client as well, it is suggested that you remove the usages of it and rely on the beeline.
61 | 1. Instrument interesting calls using the new `span` API as per the example below
62 | ```ruby
63 | class HomeController < ApplicationController
64 | def index
65 | Honeycomb.span do
66 | @interesting_information = perform_intensive_calculations(params[:honey])
67 | end
68 | end
69 | end
70 | ```
71 | 1. `honeycomb-rails` had the ability to automatically populate user information onto your events. Unfortunately `beeline-ruby` does not support this out of the box. You can use something like this snippet below to continue populating this (example for Devise)
72 | ```ruby
73 | class ApplicationController < ActionController::Base
74 | before_action do
75 | Rack::Honeycomb.add_field(request.env, "user.id", current_user.id)
76 | Rack::Honeycomb.add_field(request.env, "user.email", current_user.email)
77 | end
78 | end
79 | ```
80 | 1. (Optional) If you are using `Sequel` for database access there are some additional steps to configure
81 | ```ruby
82 | # config.ru
83 | require 'honeycomb-beeline'
84 | require 'sequel-honeycomb/auto_install'
85 |
86 | Honeycomb.init(writekey: 'YOUR_API_KEY', dataset: 'YOUR_DATASET')
87 | Sequel::Honeycomb::AutoInstall.auto_install!(honeycomb_client: Honeycomb.client, logger: Honeycomb.logger)
88 |
89 | # these next two lines should already exist in some form in this file, it's important to init the honeycomb library before this
90 | require ::File.expand_path('../config/environment', __FILE__)
91 | run Rails.application
92 | ```
93 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | require "bundler/setup"
5 | require_relative "../lib/honeycomb-beeline"
6 |
7 | # You can add fixtures and/or initialization code here to make experimenting
8 | # with your gem easier. You can also use a different console, if you like.
9 |
10 | # (If you use this, don't forget to add pry to your Gemfile!)
11 | # require "pry"
12 | # Pry.start
13 |
14 | require "irb"
15 | IRB.start(__FILE__)
16 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
8 | # Do any other automated setup that you need to do here
9 |
--------------------------------------------------------------------------------
/examples/hooks/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
6 |
7 | gem 'honeycomb-beeline', path: "../.."
8 |
--------------------------------------------------------------------------------
/examples/hooks/main.rb:
--------------------------------------------------------------------------------
1 | require "honeycomb-beeline"
2 |
3 | class CustomSampler
4 | extend Honeycomb::DeterministicSampler
5 |
6 | def self.sample(fields)
7 | case fields["app.response_code"]
8 | when 0
9 | [false, 0]
10 | when 200
11 | rate = 100
12 | [should_sample(rate, fields["trace.trace_id"]), rate]
13 | else
14 | [true, 1]
15 | end
16 | end
17 | end
18 |
19 | Honeycomb.configure do |config|
20 | config.write_key = "write_key"
21 | config.dataset = "dataset"
22 | config.service_name = "service_name"
23 | config.client = Libhoney::LogClient.new
24 | config.presend_hook do |fields|
25 | if fields.has_key? "app.credit_card_number"
26 | fields["app.credit_card_number"] = "[REDACTED]"
27 | end
28 | end
29 | config.sample_hook do |fields|
30 | CustomSampler.sample(fields)
31 | end
32 | end
33 |
34 | Honeycomb.start_span(name: "hook_span") do
35 | Honeycomb.add_field("response_code", 200)
36 | Honeycomb.add_field("credit_card_number", "4242424242424242")
37 | end
38 |
--------------------------------------------------------------------------------
/examples/propagation/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
6 |
7 | gem "faraday"
8 | gem "sinatra"
9 |
10 | gem 'honeycomb-beeline', path: "../.."
11 |
--------------------------------------------------------------------------------
/examples/propagation/main.rb:
--------------------------------------------------------------------------------
1 | require "faraday"
2 | require "sinatra"
3 | require "honeycomb-beeline"
4 | require "honeycomb/propagation/w3c"
5 |
6 | Honeycomb.configure do |config|
7 | config.write_key = "write_key"
8 | config.dataset = "dataset"
9 | config.service_name = "service_name"
10 | config.client = Libhoney::LogClient.new
11 | config.http_trace_parser_hook do |env|
12 | # env is a rack env
13 | case env["REQUEST_PATH"]
14 | when "/propagation/honeycomb"
15 | Honeycomb::HoneycombPropagation::UnmarshalTraceContext.parse_rack_env env
16 | when "/propagation/w3c"
17 | Honeycomb::W3CPropagation::UnmarshalTraceContext.parse_rack_env env
18 | else
19 | # don't start a trace for requests to other paths
20 | end
21 | end
22 | config.http_trace_propagation_hook do |env, context|
23 | # env is a faraday env and the context is a propagation context
24 | case env.url.path
25 | when "/propagation/w3c"
26 | Honeycomb::W3CPropagation::MarshalTraceContext.parse_faraday_env env, context
27 | when "/propagation/honeycomb"
28 | Honeycomb::HoneycombPropagation::MarshalTraceContext.parse_faraday_env env, context
29 | else
30 | # do not propagate any trace headers
31 | end
32 | end
33 | end
34 |
35 | fork do
36 | use Honeycomb::Sinatra::Middleware, client: Honeycomb.client
37 | set :port, 4567
38 | get "/propagation/honeycomb" do
39 | Honeycomb.start_span(name: "honeycomb_trace") do
40 | Faraday.get "http://localhost:4568/propagation/w3c"
41 | end
42 |
43 | "OK"
44 | end
45 | end
46 |
47 | fork do
48 | use Honeycomb::Sinatra::Middleware, client: Honeycomb.client
49 | set :port, 4568
50 | get "/propagation/w3c" do
51 | Honeycomb.start_span(name: "w3c_trace") do
52 | Faraday.get "http://localhost:4569/propagation/none"
53 | end
54 |
55 | "OK"
56 | end
57 | end
58 |
59 | fork do
60 | use Honeycomb::Sinatra::Middleware, client: Honeycomb.client
61 | set :port, 4569
62 | get "/propagation/none" do
63 | "OK"
64 | end
65 | end
66 |
67 | at_exit do
68 | Process.wait
69 | end
70 |
71 | sleep 3
72 | Faraday.get "http://localhost:4567/propagation/honeycomb"
73 |
--------------------------------------------------------------------------------
/examples/rack/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
6 |
7 | gem "rack", ">= 3.0"
8 | gem "rackup"
9 | gem 'honeycomb-beeline', path: "../.."
10 |
--------------------------------------------------------------------------------
/examples/rack/main.rb:
--------------------------------------------------------------------------------
1 | # Run me with: "bundle install && rackup ./main.rb"
2 |
3 | require "rack"
4 | require "honeycomb-beeline"
5 |
6 | Honeycomb.configure do |config|
7 | config.write_key = "write_key"
8 | config.dataset = "dataset"
9 | config.service_name = "service_name"
10 | config.client = Libhoney::LogClient.new
11 | end
12 |
13 | handler = Rack::Handler::WEBrick
14 |
15 | class RackApp
16 | def call(env)
17 | Honeycomb.start_span(name: "inner span") do
18 | [200, {"Content-Type" => "text/plain"}, ["Hello from Honeycomb"]]
19 | end
20 | end
21 | end
22 |
23 | app = Rack::Builder.new do |builder|
24 | builder.use Honeycomb::Rack::Middleware, client: Honeycomb.client
25 | builder.run RackApp.new
26 | end
27 |
28 | handler.run app
29 |
--------------------------------------------------------------------------------
/examples/rails52/.dockerignore:
--------------------------------------------------------------------------------
1 | .envrc
2 | .direnv
3 |
4 | tmp
5 | log
6 |
--------------------------------------------------------------------------------
/examples/rails52/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile '~/.gitignore_global'
6 |
7 | # Ignore bundler config.
8 | /.bundle
9 |
10 | # Ignore the default SQLite database.
11 | /db/*.sqlite3
12 | /db/*.sqlite3-journal
13 |
14 | # Ignore all logfiles and tempfiles.
15 | /log/*
16 | /tmp/*
17 | !/log/.keep
18 | !/tmp/.keep
19 |
20 | # Ignore uploaded files in development
21 | /storage/*
22 | !/storage/.keep
23 |
24 | /node_modules
25 | /yarn-error.log
26 |
27 | /public/assets
28 | .byebug_history
29 |
30 | # Ignore master key for decrypting credentials and more.
31 | /config/master.key
32 |
--------------------------------------------------------------------------------
/examples/rails52/.node-version:
--------------------------------------------------------------------------------
1 | 9.10.0
2 |
--------------------------------------------------------------------------------
/examples/rails52/.proxyconfig/htpasswd:
--------------------------------------------------------------------------------
1 | pxuser:$apr1$wr8E9m1.$Jyz5NrKJf7I3x19a2NYgH0
2 |
--------------------------------------------------------------------------------
/examples/rails52/.proxyconfig/squid.conf:
--------------------------------------------------------------------------------
1 | acl localnet src 10.0.0.0/8 # RFC1918 possible internal network
2 | acl localnet src 172.16.0.0/12 # RFC1918 possible internal network
3 | acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
4 | acl localnet src fc00::/7 # RFC 4193 local private network range
5 | acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
6 | acl SSL_ports port 443
7 | acl Safe_ports port 80 # http
8 | acl Safe_ports port 21 # ftp
9 | acl Safe_ports port 443 # https
10 | acl Safe_ports port 70 # gopher
11 | acl Safe_ports port 210 # wais
12 | acl Safe_ports port 1025-65535 # unregistered ports
13 | acl Safe_ports port 280 # http-mgmt
14 | acl Safe_ports port 488 # gss-http
15 | acl Safe_ports port 591 # filemaker
16 | acl Safe_ports port 777 # multiling http
17 | acl CONNECT method CONNECT
18 | http_access deny !Safe_ports
19 | http_access deny CONNECT !SSL_ports
20 | http_access allow localhost manager
21 | http_access deny manager
22 |
23 | # reverse proxy from localhost:3000 to rails web host port 3000
24 | http_port 3000 accel defaultsite=localhost
25 | cache_peer web parent 3000 0 name=rails-web proxy-only no-digest
26 | acl rails_acl dstdomain localhost
27 | http_access allow rails_acl
28 | cache_peer_access rails-web allow rails_acl
29 |
30 | # forward proxy for any authenticated proxy user
31 | auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/htpasswd
32 | acl ncsa_users proxy_auth REQUIRED
33 | http_access allow ncsa_users
34 | http_access deny all
35 | http_port 3128
36 |
37 | cache_dir ufs /var/spool/squid 100 16 256
38 | coredump_dir /var/spool/squid
39 | refresh_pattern ^ftp: 1440 20% 10080
40 | refresh_pattern ^gopher: 1440 0% 1440
41 | refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
42 | refresh_pattern (Release|Packages(.gz)*)$ 0 20% 2880
43 | refresh_pattern . 0 20% 4320
44 |
--------------------------------------------------------------------------------
/examples/rails52/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.5.8
--------------------------------------------------------------------------------
/examples/rails52/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:2.7.6
2 | RUN apt-get update -qq && apt-get install -y nodejs sqlite3
3 | RUN gem install bundler
4 | WORKDIR /myapp
5 | ENV BEELINE_FROM_RUBYGEMS=true
6 | COPY Gemfile /myapp/Gemfile
7 | RUN bundle install
8 | COPY . /myapp
9 | RUN bundle exec rails db:setup
10 |
11 | EXPOSE 3000
12 | CMD [ "bundle", "exec", "rails", "server", "-p", "3000", "-b", "0.0.0.0" ]
13 |
--------------------------------------------------------------------------------
/examples/rails52/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 |
4 | ruby '2.7.6'
5 |
6 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
7 | gem 'rails', '~> 5.2.2'
8 | # Use sqlite3 as the database for Active Record
9 | gem 'sqlite3', '~> 1.3.6'
10 | # Use Puma as the app server
11 | gem 'puma', '~> 5.6', '>= 5.6.7'
12 | # Use SCSS for stylesheets
13 | gem 'sass-rails', '~> 5.0'
14 | # Use Uglifier as compressor for JavaScript assets
15 | gem 'uglifier', '>= 1.3.0'
16 | # See https://github.com/rails/execjs#readme for more supported runtimes
17 | # gem 'mini_racer', platforms: :ruby
18 |
19 | # Use CoffeeScript for .coffee assets and views
20 | gem 'coffee-rails', '~> 4.2'
21 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
22 | gem 'turbolinks', '~> 5'
23 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
24 | gem 'jbuilder', '~> 2.5'
25 | # Use Redis adapter to run Action Cable in production
26 | # gem 'redis', '~> 4.0'
27 | # Use ActiveModel has_secure_password
28 | # gem 'bcrypt', '~> 3.1.7'
29 |
30 | # background job processing
31 | gem 'sidekiq'
32 | # simple scheduled jobs
33 | gem "sidekiq-cron"
34 | # Community library for Sidekiq job instrumentation
35 | gem 'honeykiq'
36 |
37 | # the Beeline has autoinstrumentation for this HTTP client
38 | gem "faraday"
39 |
40 | # Use ActiveStorage variant
41 | # gem 'mini_magick', '~> 4.8'
42 |
43 | # Use Capistrano for deployment
44 | # gem 'capistrano-rails', group: :development
45 |
46 | # Reduces boot times through caching; required in config/boot.rb
47 | gem 'bootsnap', '>= 1.1.0', require: false
48 |
49 | group :development, :test do
50 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
51 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
52 | end
53 |
54 | group :development do
55 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
56 | gem 'web-console', '>= 3.3.0'
57 | gem 'listen', '>= 3.0.5', '< 3.2'
58 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
59 | gem 'spring'
60 | gem 'spring-watcher-listen', '~> 2.0.0'
61 | end
62 |
63 | group :test do
64 | # Adds support for Capybara system testing and selenium driver
65 | gem 'capybara', '>= 2.15'
66 | gem 'selenium-webdriver'
67 | # Easy installation and use of chromedriver to run system tests with Chrome
68 | gem 'chromedriver-helper'
69 | end
70 |
71 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
72 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
73 |
74 | if File.exist?('/.dockerenv') || ENV['BEELINE_FROM_RUBYGEMS']
75 | puts "Using the beeline from RubyGems, not from this repository."
76 | gem 'honeycomb-beeline'
77 | else
78 | puts "Using the beeline's current state in this repository clone."
79 | gem 'honeycomb-beeline', path: "../.."
80 | end
81 |
--------------------------------------------------------------------------------
/examples/rails52/README.md:
--------------------------------------------------------------------------------
1 | # Honeycomb Beeline Rails 5.2 Sample Application
2 |
3 | ## Getting Started
4 |
5 | * Have [Docker Desktop](https://www.docker.com/products/docker-desktop) or some form of Docker Compose available.
6 | * [Sign up for a honeycomb account](https://ui.honeycomb.io/signup).
7 | * Set environment variables!
8 | * `HONEYCOMB_WRITE_KEY`: the value of the writekey for your Honeycomb team
9 | * `HONEYCOMB_DATASET`: (optional) a name for a dataset within your team you would like this application's data to go to.
10 | If left unset, data will appear in your team under a `rails52example` dataset.
11 | * Run `docker-compose up`!
12 | * Visit the [sample application site](http://localhost:3000)
13 | * Create some bees!
14 | * Load your [Honeycomb dashboard](https://ui.honeycomb.io) and see the built-in instrumentation!
15 | * An interesting query:
16 | * VISUALIZE: COUNT, HEATMAP(duration_ms)
17 | * GROUP BY: name, request.header.user_agent, request.host, request.header.x_forwarded_for
18 | * ORDER BY: name asc
19 |
20 |
21 | ## What services are running in this example?
22 |
23 | * on an interal-only network with no direct internet access:
24 | * a Rails web service to store bees
25 | * a Sidekiq background job runner with a job scheduled to visit the bees endpoint periodically
26 | * a Redis instance for Sidekiq
27 | * on both a bridged network and the internal network:
28 | * a Squid proxy:
29 | * reverse proxy connections back to the Rails web app on localhost:3000
30 | * provides authenticated HTTP/S forwarding to the internet for the services above
31 |
--------------------------------------------------------------------------------
/examples/rails52/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative 'config/application'
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/examples/rails52/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/examples/rails52/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/app/assets/images/.keep
--------------------------------------------------------------------------------
/examples/rails52/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
5 | // vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file. JavaScript code in this file should be added after the last require_* statement.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require rails-ujs
14 | //= require activestorage
15 | //= require turbolinks
16 | //= require_tree .
17 |
--------------------------------------------------------------------------------
/examples/rails52/app/assets/javascripts/bees.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/examples/rails52/app/assets/javascripts/cable.js:
--------------------------------------------------------------------------------
1 | // Action Cable provides the framework to deal with WebSockets in Rails.
2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3 | //
4 | //= require action_cable
5 | //= require_self
6 | //= require_tree ./channels
7 |
8 | (function() {
9 | this.App || (this.App = {});
10 |
11 | App.cable = ActionCable.createConsumer();
12 |
13 | }).call(this);
14 |
--------------------------------------------------------------------------------
/examples/rails52/app/assets/javascripts/channels/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/app/assets/javascripts/channels/.keep
--------------------------------------------------------------------------------
/examples/rails52/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
6 | * vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/examples/rails52/app/assets/stylesheets/bees.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the Bees controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/examples/rails52/app/assets/stylesheets/scaffolds.scss:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #fff;
3 | color: #333;
4 | margin: 33px;
5 | font-family: verdana, arial, helvetica, sans-serif;
6 | font-size: 13px;
7 | line-height: 18px;
8 | }
9 |
10 | p, ol, ul, td {
11 | font-family: verdana, arial, helvetica, sans-serif;
12 | font-size: 13px;
13 | line-height: 18px;
14 | }
15 |
16 | pre {
17 | background-color: #eee;
18 | padding: 10px;
19 | font-size: 11px;
20 | }
21 |
22 | a {
23 | color: #000;
24 |
25 | &:visited {
26 | color: #666;
27 | }
28 |
29 | &:hover {
30 | color: #fff;
31 | background-color: #000;
32 | }
33 | }
34 |
35 | th {
36 | padding-bottom: 5px;
37 | }
38 |
39 | td {
40 | padding: 0 5px 7px;
41 | }
42 |
43 | div {
44 | &.field, &.actions {
45 | margin-bottom: 10px;
46 | }
47 | }
48 |
49 | #notice {
50 | color: green;
51 | }
52 |
53 | .field_with_errors {
54 | padding: 2px;
55 | background-color: red;
56 | display: table;
57 | }
58 |
59 | #error_explanation {
60 | width: 450px;
61 | border: 2px solid red;
62 | padding: 7px 7px 0;
63 | margin-bottom: 20px;
64 | background-color: #f0f0f0;
65 |
66 | h2 {
67 | text-align: left;
68 | font-weight: bold;
69 | padding: 5px 5px 5px 15px;
70 | font-size: 12px;
71 | margin: -7px -7px 0;
72 | background-color: #c00;
73 | color: #fff;
74 | }
75 |
76 | ul li {
77 | font-size: 12px;
78 | list-style: square;
79 | }
80 | }
81 |
82 | label {
83 | display: block;
84 | }
85 |
--------------------------------------------------------------------------------
/examples/rails52/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Channel < ActionCable::Channel::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/examples/rails52/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Connection < ActionCable::Connection::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/examples/rails52/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | end
3 |
--------------------------------------------------------------------------------
/examples/rails52/app/controllers/bees_controller.rb:
--------------------------------------------------------------------------------
1 | class BeesController < ApplicationController
2 | before_action :set_bee, only: [:show, :edit, :update, :destroy]
3 |
4 | # GET /bees
5 | # GET /bees.json
6 | def index
7 | @bees = Bee.all
8 | end
9 |
10 | # GET /bees/1
11 | # GET /bees/1.json
12 | def show
13 | end
14 |
15 | # GET /bees/new
16 | def new
17 | @bee = Bee.new
18 | end
19 |
20 | # GET /bees/1/edit
21 | def edit
22 | end
23 |
24 | # POST /bees
25 | # POST /bees.json
26 | def create
27 | @bee = Bee.new(bee_params)
28 |
29 | respond_to do |format|
30 | if @bee.save
31 | format.html { redirect_to @bee, notice: 'Bee was successfully created.' }
32 | format.json { render :show, status: :created, location: @bee }
33 | else
34 | format.html { render :new }
35 | format.json { render json: @bee.errors, status: :unprocessable_entity }
36 | end
37 | end
38 | end
39 |
40 | # PATCH/PUT /bees/1
41 | # PATCH/PUT /bees/1.json
42 | def update
43 | respond_to do |format|
44 | if @bee.update(bee_params)
45 | format.html { redirect_to @bee, notice: 'Bee was successfully updated.' }
46 | format.json { render :show, status: :ok, location: @bee }
47 | else
48 | format.html { render :edit }
49 | format.json { render json: @bee.errors, status: :unprocessable_entity }
50 | end
51 | end
52 | end
53 |
54 | # DELETE /bees/1
55 | # DELETE /bees/1.json
56 | def destroy
57 | @bee.destroy
58 | respond_to do |format|
59 | format.html { redirect_to bees_url, notice: 'Bee was successfully destroyed.' }
60 | format.json { head :no_content }
61 | end
62 | end
63 |
64 | private
65 | # Use callbacks to share common setup or constraints between actions.
66 | def set_bee
67 | @bee = Bee.find(params[:id])
68 | end
69 |
70 | # Never trust parameters from the scary internet, only allow the white list through.
71 | def bee_params
72 | params.require(:bee).permit(:name)
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/examples/rails52/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/examples/rails52/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/examples/rails52/app/helpers/bees_helper.rb:
--------------------------------------------------------------------------------
1 | module BeesHelper
2 | end
3 |
--------------------------------------------------------------------------------
/examples/rails52/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | end
3 |
--------------------------------------------------------------------------------
/examples/rails52/app/jobs/poke_job.rb:
--------------------------------------------------------------------------------
1 | require "faraday"
2 |
3 | class PokeJob < ApplicationJob
4 | queue_as :default
5 |
6 | def perform(*args)
7 | seconds_to_sleep = args.first
8 |
9 | Honeycomb.start_span(name: "fakework") do |span|
10 | span.add_field('app.fakework.sleepytime', seconds_to_sleep)
11 | sleep seconds_to_sleep
12 | end
13 |
14 | uri = "http://web:3000/"
15 | begin
16 | Faraday.get(uri) do |request|
17 | request.headers['User-Agent'] = 'rails52 example / Poke background job'
18 | end
19 | rescue => exception
20 | puts "Nope. #{uri} didn't work."
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/examples/rails52/app/jobs/schedule_pokes_job.rb:
--------------------------------------------------------------------------------
1 | class SchedulePokesJob < ApplicationJob
2 | queue_as :default
3 |
4 | def perform(*args)
5 | 10.times { |n| PokeJob.perform_later(n) }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/examples/rails52/app/jobs/sidekiq_report_job.rb:
--------------------------------------------------------------------------------
1 | require 'honeykiq/periodic_reporter'
2 |
3 | class SidekiqReportJob < ApplicationJob
4 | queue_as :default
5 |
6 | def perform(*args)
7 | Honeykiq::PeriodicReporter.new.report do |_type|
8 | { 'name': 'sidekiq_stats'}
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/examples/rails52/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: 'from@example.com'
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/examples/rails52/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/examples/rails52/app/models/bee.rb:
--------------------------------------------------------------------------------
1 | class Bee < ApplicationRecord
2 | end
3 |
--------------------------------------------------------------------------------
/examples/rails52/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/app/models/concerns/.keep
--------------------------------------------------------------------------------
/examples/rails52/app/views/bees/_bee.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.extract! bee, :id, :name, :created_at, :updated_at
2 | json.url bee_url(bee, format: :json)
3 |
--------------------------------------------------------------------------------
/examples/rails52/app/views/bees/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with(model: bee, local: true) do |form| %>
2 | <% if bee.errors.any? %>
3 |
4 |
<%= pluralize(bee.errors.count, "error") %> prohibited this bee from being saved:
5 |
6 |
7 | <% bee.errors.full_messages.each do |message| %>
8 | - <%= message %>
9 | <% end %>
10 |
11 |
12 | <% end %>
13 |
14 |
15 | <%= form.label :name %>
16 | <%= form.text_field :name %>
17 |
18 |
19 |
20 | <%= form.submit %>
21 |
22 | <% end %>
23 |
--------------------------------------------------------------------------------
/examples/rails52/app/views/bees/edit.html.erb:
--------------------------------------------------------------------------------
1 | Editing Bee
2 |
3 | <%= render 'form', bee: @bee %>
4 |
5 | <%= link_to 'Show', @bee %> |
6 | <%= link_to 'Back', bees_path %>
7 |
--------------------------------------------------------------------------------
/examples/rails52/app/views/bees/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 | Bees
4 |
5 |
6 |
7 |
8 | Name |
9 | |
10 |
11 |
12 |
13 |
14 | <% @bees.each do |bee| %>
15 |
16 | <%= bee.name %> |
17 | <%= link_to 'Show', bee %> |
18 | <%= link_to 'Edit', edit_bee_path(bee) %> |
19 | <%= link_to 'Destroy', bee, method: :delete, data: { confirm: 'Are you sure?' } %> |
20 |
21 | <% end %>
22 |
23 |
24 |
25 |
26 |
27 | <%= link_to 'New Bee', new_bee_path %>
28 |
--------------------------------------------------------------------------------
/examples/rails52/app/views/bees/index.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.array! @bees, partial: 'bees/bee', as: :bee
2 |
--------------------------------------------------------------------------------
/examples/rails52/app/views/bees/new.html.erb:
--------------------------------------------------------------------------------
1 | New Bee
2 |
3 | <%= render 'form', bee: @bee %>
4 |
5 | <%= link_to 'Back', bees_path %>
6 |
--------------------------------------------------------------------------------
/examples/rails52/app/views/bees/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 |
4 | Name:
5 | <%= @bee.name %>
6 |
7 |
8 | <%= link_to 'Edit', edit_bee_path(@bee) %> |
9 | <%= link_to 'Back', bees_path %>
10 |
--------------------------------------------------------------------------------
/examples/rails52/app/views/bees/show.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.partial! "bees/bee", bee: @bee
2 |
--------------------------------------------------------------------------------
/examples/rails52/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Honeycomb
5 | <%= csrf_meta_tags %>
6 | <%= csp_meta_tag %>
7 |
8 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
9 | <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
10 |
11 |
12 |
13 | <%= yield %>
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/rails52/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/rails52/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/examples/rails52/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/examples/rails52/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | APP_PATH = File.expand_path('../config/application', __dir__)
8 | require_relative '../config/boot'
9 | require 'rails/commands'
10 |
--------------------------------------------------------------------------------
/examples/rails52/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | require_relative '../config/boot'
8 | require 'rake'
9 | Rake.application.run
10 |
--------------------------------------------------------------------------------
/examples/rails52/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a starting point to setup your application.
14 | # Add necessary setup steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | # puts "\n== Copying sample files =="
24 | # unless File.exist?('config/database.yml')
25 | # cp 'config/database.yml.sample', 'config/database.yml'
26 | # end
27 |
28 | puts "\n== Preparing database =="
29 | system! 'bin/rails db:setup'
30 |
31 | puts "\n== Removing old logs and tempfiles =="
32 | system! 'bin/rails log:clear tmp:clear'
33 |
34 | puts "\n== Restarting application server =="
35 | system! 'bin/rails restart'
36 | end
37 |
--------------------------------------------------------------------------------
/examples/rails52/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require 'rubygems'
8 | require 'bundler'
9 |
10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" }
12 | if spring
13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
14 | gem 'spring', spring.version
15 | require 'spring/binstub'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/examples/rails52/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a way to update your development environment automatically.
14 | # Add necessary update steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | puts "\n== Updating database =="
24 | system! 'bin/rails db:migrate'
25 |
26 | puts "\n== Removing old logs and tempfiles =="
27 | system! 'bin/rails log:clear tmp:clear'
28 |
29 | puts "\n== Restarting application server =="
30 | system! 'bin/rails restart'
31 | end
32 |
--------------------------------------------------------------------------------
/examples/rails52/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | begin
5 | exec "yarnpkg", *ARGV
6 | rescue Errno::ENOENT
7 | $stderr.puts "Yarn executable was not detected in the system."
8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9 | exit 1
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/examples/rails52/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative 'config/environment'
4 |
5 | run Rails.application
6 |
--------------------------------------------------------------------------------
/examples/rails52/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative 'boot'
2 |
3 | require 'rails/all'
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module Honeycomb
10 | class Application < ::Rails::Application
11 | # Initialize configuration defaults for originally generated Rails version.
12 | config.load_defaults 5.2
13 |
14 | # Settings in config/environments/* take precedence over those specified here.
15 | # Application configuration can go into files in config/initializers
16 | # -- all .rb files in that directory are automatically loaded after loading
17 | # the framework and any gems in your application.
18 |
19 | config.active_job.queue_adapter = :sidekiq
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/examples/rails52/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
2 |
3 | require 'bundler/setup' # Set up gems listed in the Gemfile.
4 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/examples/rails52/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: async
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: honeycomb_production
11 |
--------------------------------------------------------------------------------
/examples/rails52/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | 9+mDKHWCnFvUvsZZPchPWBIINinav4jaEKGdc6KDQ43HAQbZe1oTJu1gyCE5kww4vAvjYGGLTMJYCW0PmTpFWkUa41VIlrAynlys8L7zyjZIrhiGqlXjX4Av9QjQl3+sjdD5ZtLmI9evdrWxpuTO4P+5FY/X+xJVamKo5USPjwSlO0zP+pNQtC611+gNfN1ES+6AAsdGw29zOJRAg88FjlTNTEVnMMYRWjWSZa8iqNXRbrHiWCt2/bfXjdk3dsC76h1hfbH90UTysLZIxFGn8imHQpxAYiQ0Ocl3h10VwkTw0xnopN47Z1anmVlKzoIE6MUhIMSlKJf8hVwioVuhrkRj6reN9h6DbOkHZZDoFA2KmbvU8z5nyGtl57F2yLCYXhMBG3Bi5ewnKgagr4ni9Oo8EB0kXaumdvK5--Z6eJqtdUjbj4+mz4--8gfp+ULUcY6w11wN6kUD6A==
--------------------------------------------------------------------------------
/examples/rails52/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/examples/rails52/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/examples/rails52/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable/disable caching. By default caching is disabled.
16 | # Run rails dev:cache to toggle caching.
17 | if Rails.root.join('tmp', 'caching-dev.txt').exist?
18 | config.action_controller.perform_caching = true
19 |
20 | config.cache_store = :memory_store
21 | config.public_file_server.headers = {
22 | 'Cache-Control' => "public, max-age=#{2.days.to_i}"
23 | }
24 | else
25 | config.action_controller.perform_caching = false
26 |
27 | config.cache_store = :null_store
28 | end
29 |
30 | # Store uploaded files on the local file system (see config/storage.yml for options)
31 | config.active_storage.service = :local
32 |
33 | # Don't care if the mailer can't send.
34 | config.action_mailer.raise_delivery_errors = false
35 |
36 | config.action_mailer.perform_caching = false
37 |
38 | # Print deprecation notices to the Rails logger.
39 | config.active_support.deprecation = :log
40 |
41 | # Raise an error on page load if there are pending migrations.
42 | config.active_record.migration_error = :page_load
43 |
44 | # Highlight code that triggered database queries in logs.
45 | config.active_record.verbose_query_logs = true
46 |
47 | # Debug mode disables concatenation and preprocessing of assets.
48 | # This option may cause significant delays in view rendering with a large
49 | # number of complex assets.
50 | config.assets.debug = true
51 |
52 | # Suppress logger output for asset requests.
53 | config.assets.quiet = true
54 |
55 | # Raises error for missing translations
56 | # config.action_view.raise_on_missing_translations = true
57 |
58 | # Use an evented file watcher to asynchronously detect changes in source code,
59 | # routes, locales, etc. This feature depends on the listen gem.
60 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
61 | end
62 |
--------------------------------------------------------------------------------
/examples/rails52/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
19 | # config.require_master_key = true
20 |
21 | # Disable serving static files from the `/public` folder by default since
22 | # Apache or NGINX already handles this.
23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
24 |
25 | # Compress JavaScripts and CSS.
26 | config.assets.js_compressor = :uglifier
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fallback to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
33 |
34 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
35 | # config.action_controller.asset_host = 'http://assets.example.com'
36 |
37 | # Specifies the header that your server uses for sending files.
38 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
40 |
41 | # Store uploaded files on the local file system (see config/storage.yml for options)
42 | config.active_storage.service = :local
43 |
44 | # Mount Action Cable outside main process or domain
45 | # config.action_cable.mount_path = nil
46 | # config.action_cable.url = 'wss://example.com/cable'
47 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
48 |
49 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
50 | # config.force_ssl = true
51 |
52 | # Use the lowest log level to ensure availability of diagnostic information
53 | # when problems arise.
54 | config.log_level = :debug
55 |
56 | # Prepend all log lines with the following tags.
57 | config.log_tags = [ :request_id ]
58 |
59 | # Use a different cache store in production.
60 | # config.cache_store = :mem_cache_store
61 |
62 | # Use a real queuing backend for Active Job (and separate queues per environment)
63 | # config.active_job.queue_adapter = :resque
64 | # config.active_job.queue_name_prefix = "honeycomb_#{Rails.env}"
65 |
66 | config.action_mailer.perform_caching = false
67 |
68 | # Ignore bad email addresses and do not raise email delivery errors.
69 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
70 | # config.action_mailer.raise_delivery_errors = false
71 |
72 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
73 | # the I18n.default_locale when a translation cannot be found).
74 | config.i18n.fallbacks = true
75 |
76 | # Send deprecation notices to registered listeners.
77 | config.active_support.deprecation = :notify
78 |
79 | # Use default logging formatter so that PID and timestamp are not suppressed.
80 | config.log_formatter = ::Logger::Formatter.new
81 |
82 | # Use a different logger for distributed setups.
83 | # require 'syslog/logger'
84 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
85 |
86 | if ENV["RAILS_LOG_TO_STDOUT"].present?
87 | logger = ActiveSupport::Logger.new(STDOUT)
88 | logger.formatter = config.log_formatter
89 | config.logger = ActiveSupport::TaggedLogging.new(logger)
90 | end
91 |
92 | # Do not dump schema after migrations.
93 | config.active_record.dump_schema_after_migration = false
94 | end
95 |
--------------------------------------------------------------------------------
/examples/rails52/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure public file server for tests with Cache-Control for performance.
16 | config.public_file_server.enabled = true
17 | config.public_file_server.headers = {
18 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
19 | }
20 |
21 | # Show full error reports and disable caching.
22 | config.consider_all_requests_local = true
23 | config.action_controller.perform_caching = false
24 |
25 | # Raise exceptions instead of rendering exception templates.
26 | config.action_dispatch.show_exceptions = false
27 |
28 | # Disable request forgery protection in test environment.
29 | config.action_controller.allow_forgery_protection = false
30 |
31 | # Store uploaded files on the local file system in a temporary directory
32 | config.active_storage.service = :test
33 |
34 | config.action_mailer.perform_caching = false
35 |
36 | # Tell Action Mailer not to deliver emails to the real world.
37 | # The :test delivery method accumulates sent emails in the
38 | # ActionMailer::Base.deliveries array.
39 | config.action_mailer.delivery_method = :test
40 |
41 | # Print deprecation notices to the stderr.
42 | config.active_support.deprecation = :stderr
43 |
44 | # Raises error for missing translations
45 | # config.action_view.raise_on_missing_translations = true
46 | end
47 |
--------------------------------------------------------------------------------
/examples/rails52/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/examples/rails52/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 | # Add Yarn node_modules folder to the asset load path.
9 | Rails.application.config.assets.paths << Rails.root.join('node_modules')
10 |
11 | # Precompile additional assets.
12 | # application.js, application.css, and all non-JS/CSS in the app/assets
13 | # folder are already added.
14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 |
--------------------------------------------------------------------------------
/examples/rails52/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/examples/rails52/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy
4 | # For further information see the following documentation
5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6 |
7 | # Rails.application.config.content_security_policy do |policy|
8 | # policy.default_src :self, :https
9 | # policy.font_src :self, :https, :data
10 | # policy.img_src :self, :https, :data
11 | # policy.object_src :none
12 | # policy.script_src :self, :https
13 | # policy.style_src :self, :https
14 |
15 | # # Specify URI for violation reports
16 | # # policy.report_uri "/csp-violation-report-endpoint"
17 | # end
18 |
19 | # If you are using UJS then enable automatic nonce generation
20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
21 |
22 | # Report CSP violations to a specified URI
23 | # For further information see the following documentation:
24 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
25 | # Rails.application.config.content_security_policy_report_only = true
26 |
--------------------------------------------------------------------------------
/examples/rails52/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/examples/rails52/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/examples/rails52/config/initializers/honeycomb.rb:
--------------------------------------------------------------------------------
1 | Honeycomb.configure do |config|
2 | config.write_key = ENV["HONEYCOMB_WRITE_KEY"]
3 | config.dataset = ENV.fetch("HONEYCOMB_DATASET", "rails52example")
4 | config.notification_events = %w[
5 | sql.active_record
6 | render_template.action_view
7 | render_partial.action_view
8 | render_collection.action_view
9 | process_action.action_controller
10 | send_file.action_controller
11 | send_data.action_controller
12 | deliver.action_mailer
13 | ].freeze
14 | end
15 |
--------------------------------------------------------------------------------
/examples/rails52/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/examples/rails52/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/examples/rails52/config/initializers/sidekiq.rb:
--------------------------------------------------------------------------------
1 | schedule_file = "config/job_schedule.yml"
2 |
3 | if File.exist?(schedule_file) && Sidekiq.server?
4 | Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file)
5 | end
6 |
7 | # Add Honeykiq instrumentation to Sidekiq's middleware chains.
8 | # - https://github.com/carwow/honeykiq
9 | # Several Honeycomb users have used the Honeykiq library--open
10 | # source and community-maintained--to successfully instrument
11 | # their Sidekiq-run background jobs.
12 | Sidekiq.configure_server do |config|
13 | config.server_middleware do |chain|
14 | # tracing_mode: options are :link or :child
15 | # - :link will use link events https://docs.honeycomb.io/getting-data-in/tracing/send-trace-data/#links
16 | # - :child will use add the job as a span to the enqueing trace
17 | # :child used here to clearly roll-up jobs spawns from other jobs
18 | chain.add Honeykiq::ServerMiddleware, tracing_mode: :child
19 | end
20 |
21 | config.client_middleware do |chain|
22 | chain.add Honeykiq::ClientMiddleware
23 | end
24 | end
25 |
26 | Sidekiq.configure_client do |config|
27 | config.client_middleware do |chain|
28 | chain.add Honeykiq::ClientMiddleware
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/examples/rails52/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/examples/rails52/config/job_schedule.yml:
--------------------------------------------------------------------------------
1 | schedule_pokes:
2 | cron: "*/1 * * * *"
3 | class: "SchedulePokesJob"
4 | queue: default
5 |
6 | # schedule sending Sidekiq stats even when other
7 | # jobs are idle
8 | sidekiq_report:
9 | cron: "*/1 * * * *"
10 | class: "SidekiqReportJob"
11 | queue: default
12 |
--------------------------------------------------------------------------------
/examples/rails52/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at http://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/examples/rails52/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8 | threads threads_count, threads_count
9 |
10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
11 | #
12 | port ENV.fetch("PORT") { 3000 }
13 |
14 | # Specifies the `environment` that Puma will run in.
15 | #
16 | environment ENV.fetch("RAILS_ENV") { "development" }
17 |
18 | # Specifies the number of `workers` to boot in clustered mode.
19 | # Workers are forked webserver processes. If using threads and workers together
20 | # the concurrency of the application would be max `threads` * `workers`.
21 | # Workers do not work on JRuby or Windows (both of which do not support
22 | # processes).
23 | #
24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
25 |
26 | # Use the `preload_app!` method when specifying a `workers` number.
27 | # This directive tells Puma to first boot the application and load code
28 | # before forking the application. This takes advantage of Copy On Write
29 | # process behavior so workers use less memory.
30 | #
31 | # preload_app!
32 |
33 | # Allow puma to be restarted by `rails restart` command.
34 | plugin :tmp_restart
35 |
--------------------------------------------------------------------------------
/examples/rails52/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | root "bees#index"
3 | resources :bees
4 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
5 | end
6 |
--------------------------------------------------------------------------------
/examples/rails52/config/spring.rb:
--------------------------------------------------------------------------------
1 | %w[
2 | .ruby-version
3 | .rbenv-vars
4 | tmp/restart.txt
5 | tmp/caching-dev.txt
6 | ].each { |path| Spring.watch(path) }
7 |
--------------------------------------------------------------------------------
/examples/rails52/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/examples/rails52/db/migrate/20190319182235_create_bees.rb:
--------------------------------------------------------------------------------
1 | class CreateBees < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :bees do |t|
4 | t.string :name
5 |
6 | t.timestamps
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/examples/rails52/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # Note that this schema.rb definition is the authoritative source for your
6 | # database schema. If you need to create the application database on another
7 | # system, you should be using db:schema:load, not running all the migrations
8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 | # you'll amass, the slower it'll run and the greater likelihood for issues).
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 2019_03_19_182235) do
14 |
15 | create_table "bees", force: :cascade do |t|
16 | t.string "name"
17 | t.datetime "created_at", null: false
18 | t.datetime "updated_at", null: false
19 | end
20 |
21 | end
22 |
--------------------------------------------------------------------------------
/examples/rails52/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
7 | # Character.create(name: 'Luke', movie: movies.first)
8 |
--------------------------------------------------------------------------------
/examples/rails52/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2.4"
2 |
3 | x-app_base: &app
4 | build: .
5 | image: rails52example
6 | environment:
7 | HONEYCOMB_WRITE_KEY: # set these environment variables on the host
8 | HONEYCOMB_DATASET: # running docker-compose to pass secrets in
9 | RAILS_ENV: development
10 | SECRET_KEY_BASE: abunchofjibberishbecausethisisnotontheinternet
11 | REDIS_URL: redis://redis:6379/0
12 | # proxy users are in htpasswd file given to squid service below
13 | http_proxy: http://pxuser:lemmeout@squid:3128
14 | https_proxy: http://pxuser:lemmeout@squid:3128
15 | no_proxy: web
16 | LOG_LEVEL: debug
17 | depends_on:
18 | - redis
19 | - squid
20 |
21 | services:
22 | web:
23 | <<: *app
24 | tmpfs:
25 | - /myapp/tmp/pids
26 |
27 | worker:
28 | <<: *app
29 | command: "bundle exec sidekiq"
30 |
31 | redis:
32 | image: redis:latest
33 |
34 | squid:
35 | image: ubuntu/squid
36 | ports:
37 | # for reverse proxy back to web app
38 | - "127.0.0.1:3000:3000"
39 | volumes:
40 | - ./.proxyconfig/squid.conf:/etc/squid/squid.conf
41 | - ./.proxyconfig/htpasswd:/etc/squid/htpasswd
42 | networks:
43 | - default
44 | - internet
45 |
46 | networks:
47 | default:
48 | # turn off direct internet access for containers on the default network
49 | internal: true
50 | internet:
51 |
--------------------------------------------------------------------------------
/examples/rails52/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/lib/assets/.keep
--------------------------------------------------------------------------------
/examples/rails52/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/lib/tasks/.keep
--------------------------------------------------------------------------------
/examples/rails52/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/log/.keep
--------------------------------------------------------------------------------
/examples/rails52/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "honeycomb",
3 | "private": true,
4 | "dependencies": {}
5 | }
6 |
--------------------------------------------------------------------------------
/examples/rails52/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/examples/rails52/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/examples/rails52/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/examples/rails52/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/examples/rails52/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/examples/rails52/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/public/favicon.ico
--------------------------------------------------------------------------------
/examples/rails52/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/examples/rails52/storage/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/storage/.keep
--------------------------------------------------------------------------------
/examples/rails52/test/application_system_test_case.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
5 | end
6 |
--------------------------------------------------------------------------------
/examples/rails52/test/controllers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/test/controllers/.keep
--------------------------------------------------------------------------------
/examples/rails52/test/controllers/bees_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class BeesControllerTest < ActionDispatch::IntegrationTest
4 | setup do
5 | @bee = bees(:one)
6 | end
7 |
8 | test "should get index" do
9 | get bees_url
10 | assert_response :success
11 | end
12 |
13 | test "should get new" do
14 | get new_bee_url
15 | assert_response :success
16 | end
17 |
18 | test "should create bee" do
19 | assert_difference('Bee.count') do
20 | post bees_url, params: { bee: { name: @bee.name } }
21 | end
22 |
23 | assert_redirected_to bee_url(Bee.last)
24 | end
25 |
26 | test "should show bee" do
27 | get bee_url(@bee)
28 | assert_response :success
29 | end
30 |
31 | test "should get edit" do
32 | get edit_bee_url(@bee)
33 | assert_response :success
34 | end
35 |
36 | test "should update bee" do
37 | patch bee_url(@bee), params: { bee: { name: @bee.name } }
38 | assert_redirected_to bee_url(@bee)
39 | end
40 |
41 | test "should destroy bee" do
42 | assert_difference('Bee.count', -1) do
43 | delete bee_url(@bee)
44 | end
45 |
46 | assert_redirected_to bees_url
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/examples/rails52/test/fixtures/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/test/fixtures/.keep
--------------------------------------------------------------------------------
/examples/rails52/test/fixtures/bees.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | name: MyString
5 |
6 | two:
7 | name: MyString
8 |
--------------------------------------------------------------------------------
/examples/rails52/test/fixtures/files/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/test/fixtures/files/.keep
--------------------------------------------------------------------------------
/examples/rails52/test/helpers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/test/helpers/.keep
--------------------------------------------------------------------------------
/examples/rails52/test/integration/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/test/integration/.keep
--------------------------------------------------------------------------------
/examples/rails52/test/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/test/mailers/.keep
--------------------------------------------------------------------------------
/examples/rails52/test/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/test/models/.keep
--------------------------------------------------------------------------------
/examples/rails52/test/models/bee_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class BeeTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/examples/rails52/test/system/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/test/system/.keep
--------------------------------------------------------------------------------
/examples/rails52/test/system/bees_test.rb:
--------------------------------------------------------------------------------
1 | require "application_system_test_case"
2 |
3 | class BeesTest < ApplicationSystemTestCase
4 | setup do
5 | @bee = bees(:one)
6 | end
7 |
8 | test "visiting the index" do
9 | visit bees_url
10 | assert_selector "h1", text: "Bees"
11 | end
12 |
13 | test "creating a Bee" do
14 | visit bees_url
15 | click_on "New Bee"
16 |
17 | fill_in "Name", with: @bee.name
18 | click_on "Create Bee"
19 |
20 | assert_text "Bee was successfully created"
21 | click_on "Back"
22 | end
23 |
24 | test "updating a Bee" do
25 | visit bees_url
26 | click_on "Edit", match: :first
27 |
28 | fill_in "Name", with: @bee.name
29 | click_on "Update Bee"
30 |
31 | assert_text "Bee was successfully updated"
32 | click_on "Back"
33 | end
34 |
35 | test "destroying a Bee" do
36 | visit bees_url
37 | page.accept_confirm do
38 | click_on "Destroy", match: :first
39 | end
40 |
41 | assert_text "Bee was successfully destroyed"
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/examples/rails52/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV['RAILS_ENV'] ||= 'test'
2 | require_relative '../config/environment'
3 | require 'rails/test_help'
4 |
5 | class ActiveSupport::TestCase
6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
7 | fixtures :all
8 |
9 | # Add more helper methods to be used by all tests here...
10 | end
11 |
--------------------------------------------------------------------------------
/examples/rails52/tmp/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/tmp/.keep
--------------------------------------------------------------------------------
/examples/rails52/vendor/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/honeycombio/beeline-ruby/c2f63ecbd1c990ea1371fb0d8290afe6cd2894c1/examples/rails52/vendor/.keep
--------------------------------------------------------------------------------
/examples/sequel/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem "sequel"
4 | gem "sqlite3"
5 | gem 'honeycomb-beeline', path: "../.."
6 |
--------------------------------------------------------------------------------
/examples/sequel/README.md:
--------------------------------------------------------------------------------
1 | # Honeycomb Beeline Sequel Sample Application
2 |
3 | ## Getting started
4 |
5 | - run `bundle install`
6 | - run `bundle exec ruby main.rb`
7 | - events generated during the execution of the script will be output to the console
8 |
--------------------------------------------------------------------------------
/examples/sequel/main.rb:
--------------------------------------------------------------------------------
1 | require "sequel"
2 | require "honeycomb-beeline"
3 |
4 | Honeycomb.configure do |config|
5 | config.write_key = "write_key"
6 | config.dataset = "dataset"
7 | config.service_name = "service_name"
8 | # by providing debug, the above options are ignored
9 | config.debug = true
10 | end
11 |
12 | # connect to an in-memory database
13 | DB = Sequel.sqlite
14 |
15 | # create an items table
16 | DB.create_table :items do
17 | primary_key :id
18 | String :name, unique: true, null: false
19 | Float :price, null: false
20 | end
21 |
22 | # create a dataset from the items table
23 | items = DB[:items]
24 |
25 | # populate the table
26 | items.insert(name: 'abc', price: rand * 100)
27 | items.insert(name: 'def', price: rand * 100)
28 | items.insert(name: 'ghi', price: rand * 100)
29 |
30 | # print out the number of records
31 | puts "Item count: #{items.count}"
32 |
33 | # print out the average price
34 | puts "The average price is: #{items.avg(:price)}"
35 |
--------------------------------------------------------------------------------
/examples/sinatra/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
6 |
7 | gem "sinatra"
8 | gem 'honeycomb-beeline', path: "../.."
9 |
--------------------------------------------------------------------------------
/examples/sinatra/main.rb:
--------------------------------------------------------------------------------
1 | require "sinatra"
2 | require "honeycomb-beeline"
3 |
4 | Honeycomb.configure do |config|
5 | config.write_key = "write_key"
6 | config.dataset = "dataset"
7 | config.service_name = "service_name"
8 | config.client = Libhoney::LogClient.new
9 | end
10 |
11 | use Honeycomb::Sinatra::Middleware, client: Honeycomb.client
12 |
13 | get "/" do
14 | "Hello from Honeycomb"
15 | end
16 |
--------------------------------------------------------------------------------
/gemfiles/.bundle/config:
--------------------------------------------------------------------------------
1 | ---
2 | BUNDLE_RETRY: "1"
3 |
--------------------------------------------------------------------------------
/gemfiles/.gitignore:
--------------------------------------------------------------------------------
1 | *.lock
2 |
--------------------------------------------------------------------------------
/gemfiles/aws_2.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "aws-sdk", "~> 2"
6 | gem "webrick"
7 |
8 | gemspec path: "../"
9 |
--------------------------------------------------------------------------------
/gemfiles/aws_3.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "aws-sdk", "~> 3"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/faraday_0.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "faraday", "~> 0"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/faraday_1.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "faraday", "~> 1"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/rack_2.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rack", "~>2.0"
6 | gem "rack-test"
7 | gem "warden"
8 |
9 | gemspec path: "../"
10 |
--------------------------------------------------------------------------------
/gemfiles/rack_3.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rack", "~>3.0"
6 | gem "rack-session"
7 | gem "rack-test"
8 | gem "warden"
9 |
10 | gemspec path: "../"
11 |
--------------------------------------------------------------------------------
/gemfiles/rails_5.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rack-test"
6 | gem "rails", "~> 5.0.0"
7 | gem "sprockets", "~> 3"
8 | gem "warden"
9 |
10 | gemspec path: "../"
11 |
--------------------------------------------------------------------------------
/gemfiles/rails_51.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rack-test"
6 | gem "rails", "~> 5.1.0"
7 | gem "sprockets", "~> 3"
8 | gem "warden"
9 |
10 | gemspec path: "../"
11 |
--------------------------------------------------------------------------------
/gemfiles/rails_52.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rack-test"
6 | gem "rails", "~> 5.2.0"
7 | gem "sprockets", "~> 3"
8 | gem "warden"
9 |
10 | gemspec path: "../"
11 |
--------------------------------------------------------------------------------
/gemfiles/rails_6.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rack-test"
6 | gem "rails", "~> 6.0.0"
7 | gem "warden"
8 |
9 | gemspec path: "../"
10 |
--------------------------------------------------------------------------------
/gemfiles/rails_61.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rack-test"
6 | gem "rails", "~> 6.1.0"
7 | gem "warden"
8 |
9 | gemspec path: "../"
10 |
--------------------------------------------------------------------------------
/gemfiles/redis_3.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "redis", "~> 3"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/redis_4.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "redis", "~> 4"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/sequel4.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "sequel", "~> 4"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/sequel5.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "sequel", "~> 5"
6 |
7 | gemspec path: "../"
8 |
--------------------------------------------------------------------------------
/gemfiles/sinatra.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "rack-test"
6 | gem "sinatra"
7 | gem "warden"
8 |
9 | gemspec path: "../"
10 |
--------------------------------------------------------------------------------
/honeycomb-beeline.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | lib = File.expand_path("lib", __dir__)
4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5 | require "honeycomb/beeline/version"
6 |
7 | Gem::Specification.new do |spec|
8 | spec.name = Honeycomb::Beeline::NAME
9 | spec.version = Honeycomb::Beeline::VERSION
10 | spec.authors = ["The Honeycomb.io Team"]
11 | spec.email = ["support@honeycomb.io"]
12 |
13 | spec.summary = "Instrument your Ruby apps with Honeycomb"
14 | spec.homepage = "https://honeycomb.io"
15 |
16 | spec.license = "Apache-2.0"
17 |
18 | spec.required_ruby_version = ">= 2.5.0"
19 |
20 | if spec.respond_to?(:metadata)
21 | spec.metadata["homepage_uri"] = spec.homepage
22 | spec.metadata["source_code_uri"] = "https://github.com/honeycombio/beeline-ruby"
23 | else
24 | raise "RubyGems 2.0 or newer is required to protect against " \
25 | "public gem pushes."
26 | end
27 |
28 | # Specify which files should be added to the gem when it is released.
29 | # The `git ls-files -z` loads the files in the RubyGem that have been added
30 | # into git.
31 | spec.files = Dir.chdir(File.dirname(__FILE__)) do
32 | `git ls-files -z`.split("\x0").reject do |file|
33 | file.match(%r{^(test|spec|features|examples|gemfiles)/})
34 | end
35 | end
36 | spec.bindir = "exe"
37 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
38 | spec.require_paths = ["lib"]
39 |
40 | spec.add_dependency "libhoney", ">= 2.3.0"
41 |
42 | spec.add_development_dependency "appraisal"
43 | spec.add_development_dependency "bump"
44 | spec.add_development_dependency "bundler"
45 | spec.add_development_dependency "overcommit", "~> 0.46.0"
46 | spec.add_development_dependency "pry"
47 | spec.add_development_dependency "pry-byebug"
48 | spec.add_development_dependency "rake"
49 | spec.add_development_dependency "rspec", "~> 3.0"
50 | spec.add_development_dependency "rspec_junit_formatter", ">= 0.5.1"
51 | spec.add_development_dependency "rubocop", [">= 0.60.0", "< 0.69"]
52 | spec.add_development_dependency "rubocop-performance", "< 1.3.0"
53 | spec.add_development_dependency "simplecov"
54 | spec.add_development_dependency "simplecov-console"
55 | spec.add_development_dependency "webmock"
56 | end
57 |
--------------------------------------------------------------------------------
/lib/generators/honeycomb/honeycomb_generator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "rails/generators"
4 | require "active_support/core_ext/string/strip"
5 |
6 | ##
7 | # Generates an intializer for configuring the Honeycomb beeline
8 | #
9 | class HoneycombGenerator < Rails::Generators::Base
10 | source_root File.expand_path("templates", __dir__)
11 |
12 | argument :write_key, required: true, desc: "required"
13 |
14 | class_option :service_name, type: :string, default: "rails"
15 |
16 | gem "honeycomb-beeline"
17 |
18 | desc "Configures honeycomb with your write key"
19 |
20 | def create_initializer_file
21 | initializer "honeycomb.rb" do
22 | <<-RUBY.strip_heredoc
23 | Honeycomb.configure do |config|
24 | config.write_key = #{write_key.inspect}
25 | config.service_name = #{options['service_name'].inspect}
26 | config.presend_hook do |fields|
27 | if fields["name"] == "redis" && fields.has_key?("redis.command")
28 | # remove potential PII from the redis command
29 | if fields["redis.command"].respond_to? :split
30 | fields["redis.command"] = fields["redis.command"].split.first
31 | end
32 | end
33 | if fields["name"] == "sql.active_record"
34 | # remove potential PII from the active record events
35 | fields.delete("sql.active_record.binds")
36 | fields.delete("sql.active_record.type_casted_binds")
37 | end
38 | end
39 | config.notification_events = %w[
40 | sql.active_record
41 | render_template.action_view
42 | render_partial.action_view
43 | render_collection.action_view
44 | process_action.action_controller
45 | send_file.action_controller
46 | send_data.action_controller
47 | deliver.action_mailer
48 | ].freeze
49 | end
50 | RUBY
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/lib/honeycomb-beeline.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "forwardable"
4 | require "libhoney"
5 | require "honeycomb/beeline/version"
6 | require "honeycomb/client"
7 | require "honeycomb/trace"
8 |
9 | # main module
10 | module Honeycomb
11 | INTEGRATIONS = %i[
12 | active_support
13 | aws
14 | faraday
15 | rack
16 | rails
17 | railtie
18 | rake
19 | redis
20 | sequel
21 | sinatra
22 | ].freeze
23 |
24 | class << self
25 | extend Forwardable
26 | attr_reader :client
27 |
28 | def_delegators :client, :libhoney, :start_span, :add_field,
29 | :add_field_to_trace, :current_span, :current_trace,
30 | :with_field, :with_trace_field
31 |
32 | def configure
33 | Configuration.new.tap do |config|
34 | yield config
35 | @client = Honeycomb::Client.new(configuration: config)
36 | end
37 |
38 | @client
39 | end
40 |
41 | def load_integrations
42 | integrations_to_load.each do |integration|
43 | require "honeycomb/integrations/#{integration}"
44 | rescue LoadError
45 | end
46 | end
47 |
48 | def integrations_to_load
49 | if ENV["HONEYCOMB_INTEGRATIONS"]
50 | ENV["HONEYCOMB_INTEGRATIONS"].split(",")
51 | else
52 | INTEGRATIONS
53 | end
54 | end
55 | end
56 | end
57 |
58 | Honeycomb.load_integrations unless ENV["HONEYCOMB_DISABLE_AUTOCONFIGURE"]
59 |
--------------------------------------------------------------------------------
/lib/honeycomb/beeline/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Honeycomb
4 | module Beeline
5 | NAME = "honeycomb-beeline"
6 | VERSION = "3.1.0"
7 | USER_AGENT_SUFFIX = "#{NAME}/#{VERSION}"
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/honeycomb/client.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "forwardable"
4 | require "honeycomb/beeline/version"
5 | require "honeycomb/configuration"
6 | require "honeycomb/context"
7 |
8 | module Honeycomb
9 | # The Honeycomb Beeline client
10 | class Client
11 | extend Forwardable
12 |
13 | attr_reader :libhoney
14 |
15 | def_delegators :@context, :current_span, :current_trace
16 |
17 | def initialize(configuration:)
18 | @libhoney = configuration.client
19 | # attempt to set the user_agent_addition, this will only work if the
20 | # client has not sent an event prior to being passed in here. This should
21 | # be most cases
22 | @libhoney.instance_variable_set(:@user_agent_addition,
23 | Honeycomb::Beeline::USER_AGENT_SUFFIX)
24 | @libhoney.add_field "meta.beeline_version", Honeycomb::Beeline::VERSION
25 | @libhoney.add_field "meta.local_hostname", configuration.host_name
26 |
27 | integrations = Honeycomb.integrations_to_load
28 | @libhoney.add_field "meta.instrumentations_count", integrations.count
29 | @libhoney.add_field "meta.instrumentations", integrations.map(&:to_s).to_s
30 |
31 | # maybe make `service_name` a required parameter
32 | @libhoney.add_field "service_name", configuration.service_name
33 | @libhoney.add_field "service.name", configuration.service_name
34 | @context = Context.new
35 |
36 | @context.classic = configuration.classic?
37 |
38 | @additional_trace_options = {
39 | presend_hook: configuration.presend_hook,
40 | sample_hook: configuration.sample_hook,
41 | parser_hook: configuration.http_trace_parser_hook,
42 | propagation_hook: configuration.http_trace_propagation_hook,
43 | }
44 | @error_backtrace_limit = configuration.error_backtrace_limit.to_i
45 |
46 | configuration.after_initialize(self)
47 |
48 | at_exit do
49 | libhoney.close
50 | end
51 | end
52 |
53 | def start_span(name:, serialized_trace: nil, **fields)
54 | current_span = new_span_for_context(serialized_trace: serialized_trace)
55 |
56 | fields.each do |key, value|
57 | current_span.add_field(key, value)
58 | end
59 |
60 | current_span.add_field("name", name)
61 |
62 | return current_span unless block_given?
63 |
64 | begin
65 | yield current_span
66 | rescue StandardError => e
67 | add_exception_data(current_span, e)
68 |
69 | raise e
70 | ensure
71 | current_span.send
72 | end
73 | end
74 |
75 | def add_field(key, value)
76 | return if context.current_span.nil?
77 |
78 | context.current_span.add_field("app.#{key}", value)
79 | end
80 |
81 | def add_field_to_trace(key, value)
82 | return if context.current_span.nil?
83 |
84 | context.current_span.trace.add_field("app.#{key}", value)
85 | end
86 |
87 | def with_field(key)
88 | yield.tap { |value| add_field(key, value) }
89 | end
90 |
91 | def with_trace_field(key)
92 | yield.tap { |value| add_field_to_trace(key, value) }
93 | end
94 |
95 | private
96 |
97 | attr_reader :context, :error_backtrace_limit
98 |
99 | def new_span_for_context(serialized_trace:)
100 | if context.current_trace.nil?
101 | Trace.new(
102 | serialized_trace: serialized_trace,
103 | builder: libhoney.builder,
104 | context: context,
105 | **@additional_trace_options,
106 | )
107 | else
108 | context.current_span.create_child
109 | end
110 |
111 | context.current_span
112 | end
113 |
114 | def add_exception_data(span, exception)
115 | span.add_field("error", exception.class.name)
116 | span.add_field("error_detail", exception.message)
117 |
118 | return if error_backtrace_limit <= 0
119 |
120 | span.add_field(
121 | "error_backtrace",
122 | exception
123 | .backtrace
124 | .take(error_backtrace_limit)
125 | .join("\n")
126 | .encode("UTF-8", invalid: :replace, undef: :replace, replace: "�"),
127 | )
128 | span.add_field("error_backtrace_limit", error_backtrace_limit)
129 | span.add_field("error_backtrace_total_length", exception.backtrace.length)
130 | end
131 | end
132 | end
133 |
--------------------------------------------------------------------------------
/lib/honeycomb/configuration.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "socket"
4 | require "honeycomb/propagation/default"
5 | require "honeycomb/propagation/default_modern"
6 |
7 | module Honeycomb
8 | # Used to configure the Honeycomb client
9 | class Configuration
10 | attr_accessor :write_key,
11 | :api_host,
12 | :debug
13 |
14 | attr_writer :service_name, :client, :host_name, :dataset
15 | attr_reader :error_backtrace_limit
16 |
17 | def initialize
18 | @write_key = ENV["HONEYCOMB_WRITEKEY"]
19 | @dataset = ENV["HONEYCOMB_DATASET"]
20 | @service_name = ENV["HONEYCOMB_SERVICE"]
21 | @debug = ENV.key?("HONEYCOMB_DEBUG")
22 | @error_backtrace_limit = 0
23 | @client = nil
24 | end
25 |
26 | def classic?
27 | Libhoney.classic_api_key?(@write_key)
28 | end
29 |
30 | def service_name
31 | return @service_name if service_name_given?
32 | return @dataset if classic?
33 |
34 | "unknown_service:" + $PROGRAM_NAME.split("/").last
35 | end
36 |
37 | def dataset
38 | return @dataset if classic?
39 | return "unknown_service" if service_name.nil?
40 |
41 | stripped_service_name = service_name.strip
42 |
43 | warn("found extra whitespace in service name") if stripped_service_name != service_name
44 |
45 | if stripped_service_name.empty? || stripped_service_name.start_with?("unknown_service")
46 | # don't use process name in dataset
47 | "unknown_service"
48 | else
49 | stripped_service_name
50 | end
51 | end
52 |
53 | def error_backtrace_limit=(val)
54 | @error_backtrace_limit = Integer(val)
55 | end
56 |
57 | def client
58 | # memoized:
59 | # either the user has supplied a pre-configured Libhoney client
60 | @client ||=
61 | # or we'll create one and return it from here on
62 | if debug
63 | Libhoney::LogClient.new
64 | else
65 | validate_options
66 | Libhoney::Client.new(**libhoney_client_options)
67 | end
68 | end
69 |
70 | def after_initialize(client)
71 | super(client) if defined?(super)
72 | end
73 |
74 | def host_name
75 | # Send the heroku dyno name instead of hostname if available
76 | @host_name || ENV["DYNO"] || Socket.gethostname
77 | end
78 |
79 | def presend_hook(&hook)
80 | if block_given?
81 | @presend_hook = hook
82 | else
83 | @presend_hook
84 | end
85 | end
86 |
87 | def sample_hook(&hook)
88 | if block_given?
89 | @sample_hook = hook
90 | else
91 | @sample_hook
92 | end
93 | end
94 |
95 | def http_trace_parser_hook(&hook)
96 | if block_given?
97 | @http_trace_parser_hook = hook
98 | elsif @http_trace_parser_hook
99 | @http_trace_parser_hook
100 | elsif classic?
101 | DefaultPropagation::UnmarshalTraceContext.method(:parse_rack_env)
102 | else
103 | # by default we try to parse incoming honeycomb traces
104 | DefaultModernPropagation::UnmarshalTraceContext.method(:parse_rack_env)
105 | end
106 | end
107 |
108 | def http_trace_propagation_hook(&hook)
109 | if block_given?
110 | @http_trace_propagation_hook = hook
111 | elsif @http_trace_propagation_hook
112 | @http_trace_propagation_hook
113 | elsif classic?
114 | HoneycombPropagation::MarshalTraceContext.method(:parse_faraday_env)
115 | else
116 | # by default we send outgoing honeycomb trace headers
117 | HoneycombModernPropagation::MarshalTraceContext.method(:parse_faraday_env)
118 | end
119 | end
120 |
121 | private
122 |
123 | def libhoney_client_options
124 | {
125 | writekey: write_key,
126 | dataset: dataset,
127 | user_agent_addition: Honeycomb::Beeline::USER_AGENT_SUFFIX,
128 | }.tap do |options|
129 | # only set the API host for the client if one has been given
130 | options[:api_host] = api_host if api_host
131 | end
132 | end
133 |
134 | def validate_options
135 | warn("missing write_key") if write_key.nil? || write_key.empty?
136 | if classic?
137 | validate_options_classic
138 | else
139 | warn("service_name is unknown, will set to " + service_name) \
140 | if service_name.start_with?("unknown_service")
141 | warn("dataset will be ignored, sending data to " + service_name) \
142 | if dataset_given?
143 | end
144 | end
145 |
146 | def validate_options_classic
147 | warn("empty service_name option") unless service_name_given?
148 | warn("empty dataset option") unless dataset_given?
149 | end
150 |
151 | def service_name_given?
152 | # check the instance variables, not the accessor method
153 | @service_name && !@service_name.empty?
154 | end
155 |
156 | def dataset_given?
157 | @dataset && !@dataset.empty?
158 | end
159 | end
160 | end
161 |
--------------------------------------------------------------------------------
/lib/honeycomb/context.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Honeycomb
4 | # Stores the current span and trace context
5 | class Context
6 | attr_writer :classic
7 | def current_trace
8 | return if current_span.nil?
9 |
10 | current_span.trace
11 | end
12 |
13 | def current_span
14 | spans.last
15 | end
16 |
17 | def current_span=(span)
18 | spans << span
19 | end
20 |
21 | def span_sent(span)
22 | spans.last != span && raise(ArgumentError, "Incorrect span sent")
23 |
24 | spans.pop
25 | end
26 |
27 | def classic?
28 | @classic
29 | end
30 |
31 | private
32 |
33 | def spans
34 | storage["spans"] ||= []
35 | end
36 |
37 | def storage
38 | Thread.current[thread_key] ||= {}
39 | end
40 |
41 | def thread_key
42 | @thread_key ||= ["honeycomb", self.class.name, object_id].join("-")
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/lib/honeycomb/deterministic_sampler.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "digest"
4 |
5 | module Honeycomb
6 | ##
7 | # Provides a should_sample method which can be used for deterministic
8 | # sampling
9 | #
10 | module DeterministicSampler
11 | MAX_INT32 = 2**32 - 1
12 |
13 | def should_sample(rate, value)
14 | return true if rate == 1
15 |
16 | upper_bound = MAX_INT32 / rate
17 | digest = Digest::SHA1.digest(value)[0, 4]
18 | value = digest.unpack1("I!>")
19 | value <= upper_bound
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/honeycomb/integrations/active_support.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "active_support/notifications"
4 |
5 | module Honeycomb
6 | module ActiveSupport
7 | ##
8 | # Included in the configuration object to specify events that should be
9 | # subscribed to
10 | module Configuration
11 | attr_writer :notification_events
12 |
13 | def after_initialize(client)
14 | super(client) if defined?(super)
15 |
16 | events = notification_events | active_support_handlers.keys
17 |
18 | ActiveSupport::Subscriber.new(client: client).tap do |sub|
19 | events.each do |event|
20 | sub.subscribe(event, &method(:handle_notification_event))
21 | end
22 | end
23 | end
24 |
25 | def on_notification_event(event_name = nil, &hook)
26 | if event_name
27 | active_support_handlers[event_name] = hook
28 | else
29 | @default_handler = hook
30 | end
31 | end
32 |
33 | def handle_notification_event(name, span, payload)
34 | handler = active_support_handlers.fetch(name, default_handler)
35 |
36 | handler.call(name, span, payload)
37 | end
38 |
39 | def active_support_handlers
40 | @active_support_handlers ||= {}
41 | end
42 |
43 | def notification_events
44 | @notification_events ||= []
45 | end
46 |
47 | def default_handler
48 | @default_handler ||= lambda do |name, span, payload|
49 | payload.each do |key, value|
50 | # Make ActionController::Parameters parseable by libhoney.
51 | value = value.to_unsafe_hash if value.respond_to?(:to_unsafe_hash)
52 | span.add_field("#{name}.#{key}", value)
53 | end
54 |
55 | # If the notification event has recorded an exception, add the
56 | # Beeline's usual error fields to the span.
57 | # * Uses the 2-element array on :exception in the event payload
58 | # to support Rails 4. If Rails 4 support is dropped, consider
59 | # the :exception_object added in Rails 5.
60 | error, error_detail = payload[:exception]
61 | span.add_field("error", error) if error
62 | span.add_field("error_detail", error_detail) if error_detail
63 | end
64 | end
65 | end
66 |
67 | # Handles ActiveSupport::Notification subscriptions, relaying them to a
68 | # Honeycomb client
69 | class Subscriber
70 | def initialize(client:)
71 | @client = client
72 | @handlers = {}
73 | @key = ["honeycomb", self.class.name, object_id].join("-")
74 | end
75 |
76 | def subscribe(event, &block)
77 | return unless block_given?
78 |
79 | handlers[event] = block
80 | ::ActiveSupport::Notifications.subscribe(event, self)
81 | end
82 |
83 | def start(name, id, _payload)
84 | spans[id] << client.start_span(name: name)
85 | end
86 |
87 | def finish(name, id, payload)
88 | return unless (span = spans[id].pop)
89 |
90 | handler_for(name).call(name, span, payload)
91 |
92 | span.send
93 | end
94 |
95 | private
96 |
97 | attr_reader :key, :client, :handlers
98 |
99 | def spans
100 | Thread.current[key] ||= Hash.new { |h, id| h[id] = [] }
101 | end
102 |
103 | def handler_for(name)
104 | handlers.fetch(name) do
105 | handlers[
106 | handlers.keys.detect do |key|
107 | key.is_a?(Regexp) && key =~ name
108 | end
109 | ]
110 | end
111 | end
112 | end
113 | end
114 | end
115 |
116 | Honeycomb::Configuration.include Honeycomb::ActiveSupport::Configuration
117 |
--------------------------------------------------------------------------------
/lib/honeycomb/integrations/faraday.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "faraday"
4 |
5 | module Honeycomb
6 | # Faraday middleware to create spans around outgoing http requests
7 | class Faraday < ::Faraday::Middleware
8 | def initialize(app, options)
9 | super(app)
10 | @client = options[:client]
11 | end
12 |
13 | def call(env)
14 | return @app.call(env) if @client.nil?
15 |
16 | @client.start_span(name: "http_client") do |span|
17 | span.add_field "request.method", env.method.upcase
18 | span.add_field "request.scheme", env.url.scheme
19 | span.add_field "request.host", env.url.host
20 | span.add_field "request.path", env.url.path
21 | span.add_field "meta.type", "http_client"
22 | span.add_field "meta.package", "faraday"
23 | span.add_field "meta.package_version", ::Faraday::VERSION
24 |
25 | if (headers = span.trace_headers(env)).is_a?(Hash)
26 | env.request_headers.merge!(headers)
27 | end
28 |
29 | @app.call(env).tap do |response|
30 | span.add_field "response.status_code", response.status
31 | end
32 | end
33 | end
34 | end
35 | end
36 |
37 | ::Faraday::Connection.module_eval do
38 | alias_method :standard_initialize, :initialize
39 |
40 | def initialize(url = nil, options = nil, &block)
41 | standard_initialize(url, options, &block)
42 |
43 | return if @builder.handlers.include? Honeycomb::Faraday
44 |
45 | adapter_index = @builder.handlers.find_index do |handler|
46 | handler.klass.ancestors.include? Faraday::Adapter
47 | end
48 |
49 | if adapter_index
50 | @builder.insert_before(
51 | adapter_index,
52 | Honeycomb::Faraday,
53 | client: Honeycomb.client,
54 | )
55 | else
56 | @builder.use(Honeycomb::Faraday, client: Honeycomb.client)
57 | end
58 | end
59 | end
60 |
61 | Faraday::Middleware.register_middleware honeycomb: -> { Honeycomb::Faraday }
62 |
--------------------------------------------------------------------------------
/lib/honeycomb/integrations/rack.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "rack"
4 | require "honeycomb/integrations/warden"
5 |
6 | module Honeycomb
7 | # Rack specific methods for building middleware
8 | module Rack
9 | RACK_FIELDS = [
10 | ["REQUEST_METHOD", "request.method"],
11 | ["PATH_INFO", "request.path"],
12 | ["QUERY_STRING", "request.query_string"],
13 | ["HTTP_VERSION", "request.http_version"],
14 | ["HTTP_HOST", "request.host"],
15 | ["REMOTE_ADDR", "request.remote_addr"],
16 | ["HTTP_X_FORWARDED_FOR", "request.header.x_forwarded_for"],
17 | ["HTTP_X_FORWARDED_PROTO", "request.header.x_forwarded_proto"],
18 | ["HTTP_X_FORWARDED_PORT", "request.header.x_forwarded_port"],
19 | ["HTTP_ACCEPT", "request.header.accept"],
20 | ["HTTP_ACCEPT_ENCODING", "request.header.accept_encoding"],
21 | ["HTTP_ACCEPT_LANGUAGE", "request.header.accept_language"],
22 | ["CONTENT_TYPE", "request.header.content_type"],
23 | ["HTTP_USER_AGENT", "request.header.user_agent"],
24 | ["rack.url_scheme", "request.scheme"],
25 | ["HTTP_REFERER", "request.header.referer"],
26 | ].freeze
27 |
28 | attr_reader :app, :client
29 |
30 | def initialize(app, options)
31 | @app = app
32 | @client = options[:client]
33 | end
34 |
35 | def call(env)
36 | req = ::Rack::Request.new(env)
37 | client.start_span(
38 | name: "http_request",
39 | serialized_trace: env,
40 | ) do |span|
41 | add_field = lambda do |key, value|
42 | unless value.nil? || (value.respond_to?(:empty?) && value.empty?)
43 | span.add_field(key, value)
44 | end
45 | end
46 |
47 | extract_fields(env, RACK_FIELDS, &add_field)
48 |
49 | span.add_field("request.secure", req.ssl?)
50 | span.add_field("request.xhr", req.xhr?)
51 |
52 | begin
53 | status, headers, body = call_with_hook(env, span, &add_field)
54 | ensure
55 | add_package_information(env, &add_field)
56 | extract_user_information(env, &add_field)
57 | end
58 |
59 | span.add_field("response.status_code", status)
60 | span.add_field("response.content_type", headers["Content-Type"])
61 |
62 | [status, headers, body]
63 | end
64 | end
65 |
66 | def add_package_information(_env)
67 | yield "meta.package", "rack"
68 | yield "meta.package_version", ::Rack.release
69 | end
70 |
71 | def extract_fields(env, fields)
72 | fields.each do |key, value|
73 | yield value, env[key]
74 | end
75 | end
76 |
77 | private
78 |
79 | def call_with_hook(env, _span, &_add_field)
80 | app.call(env)
81 | end
82 |
83 | # Rack middleware
84 | class Middleware
85 | include Rack
86 | include Warden
87 | end
88 | end
89 | end
90 |
--------------------------------------------------------------------------------
/lib/honeycomb/integrations/rails.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "honeycomb/integrations/active_support"
4 | require "honeycomb/integrations/rack"
5 | require "honeycomb/integrations/warden"
6 |
7 | module Honeycomb
8 | # Rails specific methods for building middleware
9 | module Rails
10 | def add_package_information(env)
11 | yield "meta.package", "rails"
12 | yield "meta.package_version", ::Rails::VERSION::STRING
13 |
14 | request = ::ActionDispatch::Request.new(env)
15 |
16 | yield "request.controller", request.path_parameters[:controller]
17 | yield "request.action", request.path_parameters[:action]
18 | yield "request.route", route_for(request)
19 | end
20 |
21 | private
22 |
23 | def route_for(request)
24 | router = router_for(request)
25 | routing = routing_for(request)
26 |
27 | return unless router && routing
28 |
29 | router.recognize(routing) do |route, _|
30 | return "#{request.method} #{route.path.spec}"
31 | end
32 | end
33 |
34 | # Broadly compatible way of getting the ActionDispatch::Routing::RouteSet.
35 | #
36 | # While we'd like to just use ActionDispatch::Request#routes, that method
37 | # was only added circa Rails 5. To support Rails 4, we have to use direct
38 | # Rack env access.
39 | #
40 | # @see https://github.com/rails/rails/commit/87a75910640b83a677099198ccb3317d9850c204
41 | def router_for(request)
42 | routes = request.env["action_dispatch.routes"]
43 | routes.router if routes.respond_to?(:router)
44 | end
45 |
46 | # Constructs a simplified ActionDispatch::Request with the original route.
47 | #
48 | # This is based on ActionDispatch::Routing::RouteSet#recognize_path, which
49 | # reconstructs an ActionDispatch::Request using a given HTTP method + path
50 | # by making a mock Rack environment. Here, instead of taking the method +
51 | # path from input parameters, we use the original values from the actual
52 | # incoming request (prior to any mangling that may have been done by
53 | # middleware).
54 | #
55 | # The resulting ActionDispatch::Request instance is suitable for passing to
56 | # ActionDispatch::Journey::Router#recognize to get the original Rails
57 | # routing information corresponding to the incoming request.
58 | #
59 | # @param request [ActionDispatch::Request]
60 | # the actual incoming Rails request
61 | #
62 | # @return [ActionDispatch::Request]
63 | # a simplified version of the incoming request that retains the original
64 | # routing information, but nothing else (e.g., no HTTP parameters)
65 | #
66 | # @return [nil]
67 | # if the original request's path is invalid
68 | #
69 | # @see https://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-method
70 | # @see https://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-original_fullpath
71 | # @see https://github.com/rails/rails/blob/2a44ff12c858d296797963f7aa97abfa0c840a15/actionpack/lib/action_dispatch/journey/router/utils.rb#L7-L27
72 | # @see https://github.com/rails/rails/blob/2a44ff12c858d296797963f7aa97abfa0c840a15/actionpack/lib/action_dispatch/routing/route_set.rb#L846-L859
73 | def routing_for(request)
74 | verb = request.method
75 | path = request.original_fullpath
76 | path = normalize(path) unless path =~ %r{://}
77 | env = ::Rack::MockRequest.env_for(path, method: verb)
78 | ::ActionDispatch::Request.new(env)
79 | rescue URI::InvalidURIError
80 | nil
81 | end
82 |
83 | def normalize(path)
84 | ::ActionDispatch::Journey::Router::Utils.normalize_path(path)
85 | end
86 |
87 | # Rails middleware
88 | class Middleware
89 | include Rack
90 | include Warden
91 | include Rails
92 |
93 | def call_with_hook(env, span, &_add_field)
94 | super
95 | rescue StandardError => e
96 | wrapped = ActionDispatch::ExceptionWrapper.new(nil, e)
97 |
98 | span.add_field "response.status_code", wrapped.status_code
99 |
100 | raise e
101 | end
102 | end
103 | end
104 | end
105 |
--------------------------------------------------------------------------------
/lib/honeycomb/integrations/railtie.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "rails/railtie"
4 | require "honeycomb/integrations/rails"
5 |
6 | module Honeycomb
7 | # Automatically capture rack requests and create a trace
8 | class Railtie < ::Rails::Railtie
9 | initializer("honeycomb.install_middleware",
10 | after: :load_config_initializers) do |app|
11 | if Honeycomb.client
12 | app.config.middleware.insert_after(
13 | ActionDispatch::ShowExceptions,
14 | Honeycomb::Rails::Middleware,
15 | client: Honeycomb.client,
16 | )
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/honeycomb/integrations/rake.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "rake"
4 |
5 | Rake::TaskManager.record_task_metadata = true
6 |
7 | module Honeycomb
8 | module Rake
9 | ##
10 | # Automatically capture rake tasks and create a trace
11 | #
12 | module Task
13 | def execute(args = nil)
14 | return super(args) if honeycomb_client.nil?
15 |
16 | honeycomb_client.start_span(name: "rake.#{name}") do |span|
17 | span.add_field("meta.package", "rake")
18 | span.add_field("meta.package_version", ::Rake::VERSION)
19 | full_comment && span.add_field("rake.description", full_comment)
20 | arg_description && span.add_field("rake.arguments", arg_description)
21 | super(args)
22 | end
23 | end
24 |
25 | attr_writer :honeycomb_client
26 |
27 | def honeycomb_client
28 | return @honeycomb_client if defined?(@honeycomb_client)
29 |
30 | application.honeycomb_client
31 | end
32 | end
33 |
34 | ##
35 | # Provide access to the honeycomb_client for the rake tasks, can be
36 | # provided or uses the default global honeycomb client
37 | #
38 | module Application
39 | attr_writer :honeycomb_client
40 |
41 | def honeycomb_client
42 | return @honeycomb_client if defined?(@honeycomb_client)
43 |
44 | Honeycomb.client
45 | end
46 | end
47 | end
48 | end
49 |
50 | Rake::Application.include(Honeycomb::Rake::Application)
51 | Rake::Task.prepend(Honeycomb::Rake::Task)
52 |
--------------------------------------------------------------------------------
/lib/honeycomb/integrations/sequel.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "sequel"
4 |
5 | module Honeycomb
6 | # Wrap sequel commands in a span
7 | module Sequel
8 | def honeycomb_client
9 | @honeycomb_client || Honeycomb.client
10 | end
11 |
12 | def honeycomb_client=(client)
13 | @honeycomb_client = client
14 | end
15 |
16 | def log_connection_yield(sql, conn, args = nil)
17 | return super if honeycomb_client.nil?
18 |
19 | honeycomb_client.start_span(name: sql.sub(/\s+.*/, "").upcase) do |span|
20 | span.add_field "meta.package", "sequel"
21 | span.add_field "meta.package_version", ::Sequel::VERSION
22 | span.add_field "type", "db"
23 | span.add_field "db.sql", sql
24 | super
25 | end
26 | end
27 | end
28 | end
29 |
30 | Sequel::Database.register_extension(:honeycomb, Honeycomb::Sequel)
31 | Sequel::Database.extension :honeycomb
32 |
--------------------------------------------------------------------------------
/lib/honeycomb/integrations/sinatra.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "sinatra"
4 | require "honeycomb/integrations/rack"
5 | require "honeycomb/integrations/warden"
6 |
7 | module Honeycomb
8 | # Sinatra specific methods for building middleware
9 | module Sinatra
10 | def add_package_information(env)
11 | yield "meta.package", "sinatra"
12 | yield "meta.package_version", ::Sinatra::VERSION
13 |
14 | yield "request.route", env["sinatra.route"]
15 | end
16 |
17 | # Sinatra middleware
18 | class Middleware
19 | include Rack
20 | include Warden
21 | include Sinatra
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/honeycomb/integrations/warden.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Honeycomb
4 | # Methods for extracting common warden/devise fields from a rack env hash
5 | module Warden
6 | COMMON_USER_FIELDS = %i[
7 | email
8 | name
9 | first_name
10 | last_name
11 | created_at
12 | id
13 | ].freeze
14 |
15 | SCOPE_PATTERN = /^warden\.user\.([^.]+)\.key$/.freeze
16 |
17 | def extract_user_information(env)
18 | warden = env["warden"]
19 |
20 | return unless warden
21 |
22 | session = env["rack.session"] || {}
23 | keys = session.keys.select do |key|
24 | key.match(SCOPE_PATTERN)
25 | end
26 | warden_scopes = keys.map do |key|
27 | key.gsub(SCOPE_PATTERN, "\\1")
28 | end
29 | best_scope = warden_scopes.include?("user") ? "user" : warden_scopes.first
30 |
31 | return unless best_scope
32 |
33 | env["warden"].user(scope: best_scope, run_callbacks: false).tap do |user|
34 | COMMON_USER_FIELDS.each do |field|
35 | user.respond_to?(field) && yield("user.#{field}", user.send(field))
36 | end
37 | end
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/honeycomb/propagation.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "base64"
4 | require "json"
5 | require "uri"
6 |
7 | require "honeycomb/propagation/honeycomb"
8 |
9 | module Honeycomb
10 | # Parse trace headers
11 | module PropagationParser
12 | include HoneycombPropagation::UnmarshalTraceContext
13 | end
14 |
15 | # Serialize trace headers
16 | module PropagationSerializer
17 | include HoneycombPropagation::MarshalTraceContext
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/honeycomb/propagation/aws.rb:
--------------------------------------------------------------------------------
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 | trace.fields.keys&.each do |key|
56 | context.push("#{key}=#{trace.fields[key]}")
57 | end
58 |
59 | data_to_propagate = [
60 | "Root=#{trace.id}",
61 | "Parent=#{id}",
62 | ]
63 | "#{data_to_propagate.join(';')}#{context.join(';')}"
64 | end
65 |
66 | def self.to_trace_header(propagation_context)
67 | context = [""]
68 | fields = propagation_context.trace_fields
69 | fields.keys&.each do |key|
70 | context.push("#{key}=#{fields[key]}")
71 | end
72 |
73 | data_to_propagate = [
74 | "Root=#{propagation_context.trace_id}",
75 | "Parent=#{propagation_context.parent_id}",
76 | ]
77 | "#{data_to_propagate.join(';')}#{context.join(';')}"
78 | end
79 | end
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/lib/honeycomb/propagation/context.rb:
--------------------------------------------------------------------------------
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
12 |
--------------------------------------------------------------------------------
/lib/honeycomb/propagation/default.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "honeycomb/propagation/honeycomb"
4 | require "honeycomb/propagation/w3c"
5 |
6 | module Honeycomb
7 | # Default behavior for handling trace propagation
8 | module DefaultPropagation
9 | # Parse incoming trace headers.
10 | #
11 | # Checks for and parses Honeycomb's trace header or, if not found,
12 | # then checks for and parses W3C trace parent header.
13 | module UnmarshalTraceContext
14 | def parse_rack_env(env)
15 | if env["HTTP_X_HONEYCOMB_TRACE"]
16 | HoneycombPropagation::UnmarshalTraceContext.parse_rack_env env
17 | elsif env["HTTP_TRACEPARENT"]
18 | W3CPropagation::UnmarshalTraceContext.parse_rack_env env
19 | else
20 | [nil, nil, nil, nil]
21 | end
22 | end
23 | module_function :parse_rack_env
24 | public :parse_rack_env
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/honeycomb/propagation/default_modern.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "honeycomb/propagation/honeycomb_modern"
4 | require "honeycomb/propagation/w3c"
5 |
6 | module Honeycomb
7 | # Default behavior for handling trace propagation
8 | module DefaultModernPropagation
9 | # Parse incoming trace headers.
10 | #
11 | # Checks for and parses Honeycomb's trace header or, if not found,
12 | # then checks for and parses W3C trace parent header.
13 | module UnmarshalTraceContext
14 | def parse_rack_env(env)
15 | if env["HTTP_X_HONEYCOMB_TRACE"]
16 | HoneycombModernPropagation::UnmarshalTraceContext.parse_rack_env env
17 | elsif env["HTTP_TRACEPARENT"]
18 | W3CPropagation::UnmarshalTraceContext.parse_rack_env env
19 | else
20 | [nil, nil, nil, nil]
21 | end
22 | end
23 | module_function :parse_rack_env
24 | public :parse_rack_env
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/honeycomb/propagation/honeycomb.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "base64"
4 | require "json"
5 | require "uri"
6 | require "libhoney/cleaner"
7 |
8 | module Honeycomb
9 | # Parsing and propagation for honeycomb trace headers
10 | module HoneycombPropagation
11 | # Parse trace headers
12 | module UnmarshalTraceContext
13 | def parse_rack_env(env)
14 | parse env["HTTP_X_HONEYCOMB_TRACE"]
15 | end
16 |
17 | def parse(serialized_trace)
18 | unless serialized_trace.nil?
19 | version, payload = serialized_trace.split(";", 2)
20 |
21 | if version == "1"
22 | trace_id, parent_span_id, trace_fields, dataset = parse_v1(payload)
23 |
24 | if !trace_id.nil? && !parent_span_id.nil?
25 | return [trace_id, parent_span_id, trace_fields, dataset]
26 | end
27 | end
28 | end
29 |
30 | [nil, nil, nil, nil]
31 | end
32 |
33 | def parse_v1(payload)
34 | trace_id, parent_span_id, trace_fields, dataset = nil
35 | payload.split(",").each do |entry|
36 | key, value = entry.split("=", 2)
37 | case key.downcase
38 | when "dataset"
39 | dataset = URI.decode_www_form_component(value)
40 | when "trace_id"
41 | trace_id = value
42 | when "parent_id"
43 | parent_span_id = value
44 | when "context"
45 | Base64.urlsafe_decode64(value).tap do |json|
46 | trace_fields = JSON.parse json
47 | rescue JSON::ParserError
48 | trace_fields = {}
49 | end
50 | end
51 | end
52 |
53 | [trace_id, parent_span_id, trace_fields, dataset]
54 | end
55 |
56 | module_function :parse_rack_env, :parse, :parse_v1
57 | public :parse_rack_env, :parse
58 | end
59 |
60 | # Serialize trace headers
61 | module MarshalTraceContext
62 | # for cleaning data in trace fields before serializing to prop header value
63 | include Libhoney::Cleaner
64 | # promote cleaner instance methods to module methods so that self.to_trace_header can use them
65 | module_function :clean_data, :clean_string
66 |
67 | def to_trace_header
68 | data_to_propogate = [
69 | "dataset=#{encode_dataset(builder.dataset)}",
70 | "trace_id=#{trace.id}",
71 | "parent_id=#{id}",
72 | "context=#{encode_trace_fields(trace.fields)}",
73 | ]
74 | "1;#{data_to_propogate.join(',')}"
75 | end
76 |
77 | def self.parse_faraday_env(_env, propagation_context)
78 | {
79 | "X-Honeycomb-Trace" => to_trace_header(propagation_context),
80 | }
81 | end
82 |
83 | def self.to_trace_header(propagation_context)
84 | data_to_propogate = [
85 | "dataset=#{encode_dataset(propagation_context.dataset)}",
86 | "trace_id=#{propagation_context.trace_id}",
87 | "parent_id=#{propagation_context.parent_id}",
88 | "context=#{encode_trace_fields(propagation_context.trace_fields)}",
89 | ]
90 | "1;#{data_to_propogate.join(',')}"
91 | end
92 |
93 | def encode_trace_fields(fields)
94 | Base64.urlsafe_encode64(
95 | JSON.generate(
96 | clean_data(fields),
97 | ),
98 | ).strip
99 | end
100 | module_function :encode_trace_fields
101 |
102 | def encode_dataset(dataset)
103 | URI.encode_www_form_component(dataset)
104 | end
105 | module_function :encode_dataset
106 | end
107 | end
108 | end
109 |
--------------------------------------------------------------------------------
/lib/honeycomb/propagation/honeycomb_modern.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "base64"
4 | require "json"
5 | require "uri"
6 | require "libhoney/cleaner"
7 |
8 | module Honeycomb
9 | # Parsing and propagation for honeycomb trace headers
10 | module HoneycombModernPropagation
11 | # Parse trace headers
12 | module UnmarshalTraceContext
13 | def parse_rack_env(env)
14 | parse env["HTTP_X_HONEYCOMB_TRACE"]
15 | end
16 |
17 | def parse(serialized_trace)
18 | unless serialized_trace.nil?
19 | version, payload = serialized_trace.split(";", 2)
20 |
21 | if version == "1"
22 | trace_id, parent_span_id, trace_fields = parse_v1(payload)
23 |
24 | if !trace_id.nil? && !parent_span_id.nil?
25 | return [trace_id, parent_span_id, trace_fields, nil]
26 | end
27 | end
28 | end
29 |
30 | [nil, nil, nil, nil]
31 | end
32 |
33 | def parse_v1(payload)
34 | trace_id, parent_span_id, trace_fields = nil
35 | payload.split(",").each do |entry|
36 | key, value = entry.split("=", 2)
37 | case key.downcase
38 | when "trace_id"
39 | trace_id = value
40 | when "parent_id"
41 | parent_span_id = value
42 | when "context"
43 | Base64.urlsafe_decode64(value).tap do |json|
44 | trace_fields = JSON.parse json
45 | rescue JSON::ParserError
46 | trace_fields = {}
47 | end
48 | end
49 | end
50 |
51 | [trace_id, parent_span_id, trace_fields, nil]
52 | end
53 |
54 | module_function :parse_rack_env, :parse, :parse_v1
55 | public :parse_rack_env, :parse
56 | end
57 |
58 | # Serialize trace headers
59 | module MarshalTraceContext
60 | # for cleaning data in trace fields before serializing to prop header value
61 | include Libhoney::Cleaner
62 | # promote cleaner instance methods to module methods so that self.to_trace_header can use them
63 | module_function :clean_data, :clean_string
64 |
65 | def to_trace_header
66 | data_to_propogate = [
67 | "trace_id=#{trace.id}",
68 | "parent_id=#{id}",
69 | "context=#{encode_trace_fields(trace.fields)}",
70 | ]
71 | "1;#{data_to_propogate.join(',')}"
72 | end
73 |
74 | def self.parse_faraday_env(_env, propagation_context)
75 | {
76 | "X-Honeycomb-Trace" => to_trace_header(propagation_context),
77 | }
78 | end
79 |
80 | def self.to_trace_header(propagation_context)
81 | data_to_propogate = [
82 | "trace_id=#{propagation_context.trace_id}",
83 | "parent_id=#{propagation_context.parent_id}",
84 | "context=#{encode_trace_fields(propagation_context.trace_fields)}",
85 | ]
86 | "1;#{data_to_propogate.join(',')}"
87 | end
88 |
89 | def encode_trace_fields(fields)
90 | Base64.urlsafe_encode64(
91 | JSON.generate(
92 | clean_data(fields),
93 | ),
94 | ).strip
95 | end
96 | module_function :encode_trace_fields
97 | end
98 | end
99 | end
100 |
--------------------------------------------------------------------------------
/lib/honeycomb/propagation/w3c.rb:
--------------------------------------------------------------------------------
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"
9 | INVALID_SPAN_ID = "0000000000000000"
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 /^[A-Fa-f0-9]{2}$/.match?(version)
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 is nil, it means a field is missing
35 | if trace_flags.nil? || trace_id == INVALID_TRACE_ID || parent_span_id == INVALID_SPAN_ID
36 | return [nil, nil]
37 | end
38 |
39 | [trace_id, parent_span_id]
40 | end
41 |
42 | module_function :parse_rack_env, :parse, :parse_v1
43 | public :parse
44 | end
45 |
46 | # Serialize trace headers
47 | module MarshalTraceContext
48 | def to_trace_header
49 | # do not propagate malformed ids
50 | if trace.id =~ /^[A-Fa-f0-9]{32}$/ && id =~ /^[A-Fa-f0-9]{16}$/
51 | return "00-#{trace.id}-#{id}-01"
52 | end
53 |
54 | nil
55 | end
56 |
57 | def self.parse_faraday_env(_env, propagation_context)
58 | {
59 | "traceparent" => to_trace_header(propagation_context),
60 | }
61 | end
62 |
63 | def self.to_trace_header(propagation_context)
64 | trace_id = propagation_context.trace_id
65 | parent_id = propagation_context.parent_id
66 | # do not propagate malformed ids
67 | if trace_id =~ /^[A-Fa-f0-9]{32}$/ && parent_id =~ /^[A-Fa-f0-9]{16}$/
68 | return "00-#{trace_id}-#{parent_id}-01"
69 | end
70 |
71 | nil
72 | end
73 | end
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/lib/honeycomb/rollup_fields.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Honeycomb
4 | ##
5 | # Functionality for including 'rollup_fields'. Which are fields that can be
6 | # tracked numerically and will also be propogated up to an existing trace.
7 | #
8 | module RollupFields
9 | def rollup_fields
10 | @rollup_fields ||= Hash.new(0)
11 | end
12 |
13 | def add_rollup_field(key, value)
14 | return unless value.is_a? Numeric
15 |
16 | respond_to?(:trace) && trace.add_rollup_field(key, value)
17 | rollup_fields[key] += value
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/honeycomb/trace.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "forwardable"
4 | require "securerandom"
5 | require "honeycomb/span"
6 | require "honeycomb/propagation"
7 | require "honeycomb/rollup_fields"
8 |
9 | module Honeycomb
10 | # Represents a Honeycomb trace, which groups spans together
11 | class Trace
12 | include RollupFields
13 | extend Forwardable
14 |
15 | def_delegators :@root_span, :send
16 |
17 | attr_reader :id, :fields, :root_span
18 |
19 | def initialize(builder:, context:, serialized_trace: nil, **options)
20 | trace_id, parent_span_id, trace_fields, dataset =
21 | internal_parse(context: context, serialized_trace: serialized_trace, **options)
22 |
23 | # if dataset is not nil,
24 | # set trace's builder.dataset = dataset from trace header
25 | if context.classic?
26 | dataset && builder.dataset = dataset
27 | end
28 |
29 | @id = trace_id || generate_trace_id
30 | @fields = trace_fields || {}
31 | @root_span = Span.new(trace: self,
32 | parent_id: parent_span_id,
33 | is_root: true,
34 | builder: builder,
35 | context: context,
36 | **options)
37 | end
38 |
39 | def add_field(key, value)
40 | @fields[key] = value
41 | end
42 |
43 | private
44 |
45 | INVALID_TRACE_ID = ("00" * 16)
46 |
47 | def generate_trace_id
48 | loop do
49 | id = SecureRandom.hex(16)
50 | return id unless id == INVALID_TRACE_ID
51 | end
52 | end
53 |
54 | def internal_parse(context:, serialized_trace: nil, parser_hook: nil, **_options)
55 | # previously we passed in the header directly as a string for us to parse
56 | # now we get passed the rack env to use as an argument to the provided
57 | # parser_hook. This preserves the current behaviour and allows us to
58 | # move forward with the new behaviour without breaking changes
59 | if serialized_trace.is_a?(Hash) && parser_hook
60 | parser_hook.call(serialized_trace)
61 | elsif context.classic?
62 | HoneycombPropagation::UnmarshalTraceContext.parse serialized_trace
63 | else
64 | HoneycombModernPropagation::UnmarshalTraceContext.parse serialized_trace
65 | end
66 | end
67 | end
68 | end
69 |
--------------------------------------------------------------------------------
/lib/sequel/extensions/honeycomb.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Needed for the sequel extension to work ¯\_(ツ)_/¯
4 |
--------------------------------------------------------------------------------
/spec/generators/honeycomb/honeycomb_generator_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | if defined?(Honeycomb::Rails)
4 | require "generators/honeycomb/honeycomb_generator"
5 |
6 | RSpec.describe HoneycombGenerator do
7 | describe "simple execution" do
8 | let(:name) { "honeycomb" }
9 | let(:write_key) { "classic_generator_write_key_test" }
10 | let(:service_name) { "a_service_name" }
11 | let(:init_file) { File.join(@dir, "config/initializers/honeycomb.rb") }
12 | let(:config) { Honeycomb::Configuration.new }
13 |
14 | around(:example) do |example|
15 | Dir.mktmpdir do |dir|
16 | Dir.chdir dir do
17 | @dir = dir
18 | example.run
19 | end
20 | end
21 | end
22 |
23 | it "creates the initializer file" do
24 | Rails::Generators.invoke(name, [write_key])
25 | expect(File.exist?(init_file)).to eq(true)
26 | end
27 |
28 | describe "configuring honeycomb" do
29 | before(:each) do
30 | honeycomb = class_double("Honeycomb")
31 | .as_stubbed_const(transfer_nested_constants: true)
32 | expect(honeycomb).to receive(:configure) do |&block|
33 | block.call config
34 | end
35 | end
36 |
37 | it "sets the writekey correctly" do
38 | Rails::Generators.invoke(name, [write_key])
39 | require init_file
40 | expect(config.write_key).to eq(write_key)
41 | end
42 |
43 | it "sets the service_name to a default" do
44 | Rails::Generators.invoke(name, [write_key])
45 | require init_file
46 | expect(config.service_name).not_to be_empty
47 | end
48 |
49 | it "sets the service_name correctly" do
50 | Rails::Generators.invoke(name, [write_key, "--service_name", service_name])
51 | require init_file
52 | expect(config.service_name).to eq(service_name)
53 | end
54 |
55 | it "sets the notification events" do
56 | Rails::Generators.invoke(name, [write_key])
57 | require init_file
58 | expect(config.notification_events).not_to be_empty
59 | end
60 |
61 | describe "the presend hook" do
62 | let(:presend_hook) { config.presend_hook }
63 |
64 | before(:each) do
65 | Rails::Generators.invoke(name, [write_key])
66 | require init_file
67 | presend_hook.call(data)
68 | end
69 |
70 | describe "redis sanitizing" do
71 | let(:data) do
72 | {
73 | "name" => "redis",
74 | "redis.command" => "SET PII",
75 | }
76 | end
77 |
78 | it "removes the PII from the redis command" do
79 | expect(data).to include("redis.command" => "SET")
80 | end
81 | end
82 |
83 | describe "redis sanitizing with unexpected data" do
84 | let(:data) do
85 | {
86 | "name" => "redis",
87 | "redis.command" => 1,
88 | }
89 | end
90 |
91 | it "removes the PII from the redis command" do
92 | expect(data).to include("redis.command" => 1)
93 | end
94 | end
95 |
96 | describe "sql.active_record sanitizing" do
97 | let(:data) do
98 | {
99 | "name" => "sql.active_record",
100 | "sql.active_record.binds" => true,
101 | "sql.active_record.type_casted_binds" => true,
102 | }
103 | end
104 |
105 | it "filters out the sql.active_record.binds" do
106 | expect(data).not_to include("sql.active_record.binds")
107 | end
108 |
109 | it "filters out the sql.active_record.binds" do
110 | expect(data).not_to include("sql.active_record.type_casted_binds")
111 | end
112 | end
113 | end
114 | end
115 | end
116 | end
117 | end
118 |
--------------------------------------------------------------------------------
/spec/honeycomb/beeline_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "libhoney"
4 |
5 | RSpec.describe Honeycomb do
6 | let(:libhoney_client) { Libhoney::TestClient.new }
7 |
8 | before do
9 | Honeycomb.configure do |config|
10 | config.write_key = "write_key"
11 | config.dataset = "a_dataset"
12 | config.service_name = "a_service_name"
13 | config.client = libhoney_client
14 | end
15 | end
16 |
17 | describe "accessing the libhoney instance" do
18 | it "contains the expected field" do
19 | expect(Honeycomb.libhoney).to eq libhoney_client
20 | end
21 | end
22 |
23 | describe "when using a block" do
24 | before do
25 | Honeycomb.start_span(name: "test") do
26 | end
27 | end
28 |
29 | it "sends the right amount of events" do
30 | expect(libhoney_client.events.size).to eq 1
31 | end
32 | end
33 |
34 | describe "manually sending" do
35 | before do
36 | span = Honeycomb.start_span(name: "test")
37 | span.send
38 | end
39 |
40 | it "sends the right amount of events" do
41 | expect(libhoney_client.events.size).to eq 1
42 | end
43 | end
44 |
45 | describe "service name configured" do
46 | before do
47 | Honeycomb.start_span(name: "test") do
48 | Honeycomb.add_field_to_trace("interesting", "banana")
49 | end
50 | end
51 |
52 | it "contains service_name field" do
53 | expect(libhoney_client.events.map(&:data))
54 | .to all(include("service_name" => "a_service_name"))
55 | end
56 |
57 | it "contains service.name field" do
58 | expect(libhoney_client.events.map(&:data))
59 | .to all(include("service.name" => "a_service_name"))
60 | end
61 | end
62 |
63 | describe "adding fields to span" do
64 | before do
65 | Honeycomb.start_span(name: "test") do
66 | Honeycomb.add_field("interesting", "banana")
67 | end
68 | end
69 |
70 | it "contains the expected field" do
71 | expect(libhoney_client.events.map(&:data))
72 | .to all(include("app.interesting" => "banana"))
73 | end
74 | end
75 |
76 | describe "adding fields to trace" do
77 | before do
78 | Honeycomb.start_span(name: "test") do
79 | Honeycomb.add_field_to_trace("interesting", "banana")
80 | end
81 | end
82 |
83 | it "contains the expected field" do
84 | expect(libhoney_client.events.map(&:data))
85 | .to all(include("app.interesting" => "banana"))
86 | end
87 | end
88 |
89 | describe "adding computed fields to span" do
90 | let(:field) do
91 | Honeycomb.start_span(name: "test") do
92 | Honeycomb.with_field("interesting") do
93 | "banana"
94 | end
95 | end
96 | end
97 |
98 | it "returns the field's value" do
99 | expect(field).to eq "banana"
100 | end
101 |
102 | it "contains the expected field" do
103 | field # send the events
104 | expect(libhoney_client.events.map(&:data))
105 | .to all(include("app.interesting" => "banana"))
106 | end
107 | end
108 |
109 | describe "adding computed fields to trace" do
110 | let(:field) do
111 | Honeycomb.start_span(name: "test") do
112 | Honeycomb.with_trace_field("interesting") do
113 | "banana"
114 | end
115 | end
116 | end
117 |
118 | it "returns the field's value" do
119 | expect(field).to eq "banana"
120 | end
121 |
122 | it "contains the expected field" do
123 | field # send the events
124 | expect(libhoney_client.events.map(&:data))
125 | .to all(include("app.interesting" => "banana"))
126 | end
127 | end
128 | end
129 |
130 | RSpec.describe Honeycomb::Beeline do
131 | it "has a version number" do
132 | expect(Honeycomb::Beeline::VERSION).not_to be nil
133 | end
134 | end
135 |
--------------------------------------------------------------------------------
/spec/honeycomb/deterministic_sampler_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "securerandom"
4 | require "honeycomb/deterministic_sampler"
5 |
6 | RSpec.shared_examples "specific sampling decision" do |rate, value, decision|
7 | it "makes the correct sampling decision" do
8 | expect(subject.should_sample(rate, value)).to eq decision
9 | end
10 | end
11 |
12 | RSpec.shared_examples "sampling distribution" do |sample_rate, margin|
13 | let(:requests) { 50_000 }
14 |
15 | def random_request_id
16 | SecureRandom.uuid
17 | end
18 |
19 | it "gives the correct distribution for sample rate of #{sample_rate}" do
20 | expected = requests * 1.fdiv(sample_rate)
21 | bounds = expected * margin
22 | samples = 0
23 | requests.times do
24 | subject.should_sample(sample_rate, random_request_id) && samples += 1
25 | end
26 | expect(samples).to be_within(bounds).of(expected)
27 | end
28 | end
29 |
30 | RSpec.describe Honeycomb::DeterministicSampler do
31 | subject { Class.new.include(described_class).new }
32 |
33 | include_examples "specific sampling decision", 17, "hello", false
34 | include_examples "specific sampling decision", 17, "hello", false
35 | include_examples "specific sampling decision", 17, "world", false
36 | include_examples "specific sampling decision", 17, "this5", true
37 |
38 | include_examples "sampling distribution", 1, 0.05
39 | include_examples "sampling distribution", 2, 0.05
40 | include_examples "sampling distribution", 10, 0.06
41 | end
42 |
43 | RSpec.describe Honeycomb::DeterministicSampler do
44 | subject { Class.new.include(described_class).new }
45 |
46 | EXPECTED_SAMPLES = [
47 | ["4YeYygWjTZ41zOBKUoYUaSVxPGm78rdU", false],
48 | ["iow4KAFBl9u6lF4EYIcsFz60rXGvu7ph", true],
49 | ["EgQMHtruEfqaqQqRs5nwaDXsegFGmB5n", true],
50 | ["UnVVepVdyGIiwkHwofyva349tVu8QSDn", true],
51 | ["rWuxi2uZmBEprBBpxLLFcKtXHA8bQkvJ", true],
52 | ["8PV5LN1IGm5T0ZVIaakb218NvTEABNZz", false],
53 | ["EMSmscnxwfrkKd1s3hOJ9bL4zqT1uud5", true],
54 | ["YiLx0WGJrQAge2cVoAcCscDDVidbH4uE", true],
55 | ["IjD0JHdQdDTwKusrbuiRO4NlFzbPotvg", false],
56 | ["ADwiQogJGOS4X8dfIcidcfdT9fY2WpHC", false],
57 | ["DyGaS7rfQsMX0E6TD9yORqx7kJgUYvNR", true],
58 | ["MjOCkn11liCYZspTAhdULMEfWJGMHvpK", false],
59 | ["wtGa41YcFMR5CBNr79lTfRAFi6Vhr6UF", true],
60 | ["3AsMjnpTBawWv2AAPDxLjdxx4QYl9XXb", false],
61 | ["sa2uMVNPiZLK52zzxlakCUXLaRNXddBz", false],
62 | ["NYH9lkdbvXsiUFKwJtjSkQ1RzpHwWloK", false],
63 | ["8AwzQeY5cudY8YUhwxm3UEP7Oos61RTY", false],
64 | ["ADKWL3p5gloRYO3ptarTCbWUHo5JZi3j", false],
65 | ["UAnMARj5x7hkh9kwBiNRfs5aYDsbHKpw", true],
66 | ["Aes1rgTLMNnlCkb9s6bH7iT5CbZTdxUw", true],
67 | ["eh1LYTOfgISrZ54B7JbldEpvqVur57tv", false],
68 | ["u5A1wEYax1kD9HBeIjwyNAoubDreCsZ6", false],
69 | ["mv70SFwpAOHRZt4dmuw5n2lAsM1lOrcx", true],
70 | ["i4nIu0VZMuh5hLrUm9w2kqNxcfYY7Y3a", true],
71 | ["UqfewK2qFZqfJ619RKkRiZeYtO21ngX1", false],
72 | ].freeze
73 |
74 | EXPECTED_SAMPLES.each do |id, sample|
75 | it "produces the expected sampling decision" do
76 | expect(subject.should_sample(2, id)).to eq sample
77 | end
78 | end
79 | end
80 |
--------------------------------------------------------------------------------
/spec/honeycomb/integrations/rack_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | if defined?(Honeycomb::Rack)
4 | # Rack::Session was moved to a separate gem in Rack v3.0
5 | if Gem::Version.new(::Rack.release) >= Gem::Version.new("3.0")
6 | require "rack/session"
7 | end
8 | require "rack/test"
9 | require "warden"
10 |
11 | class AnApp
12 | def call(_env)
13 | [200, { "content-type" => "text/plain" }, ["Hello world!"]]
14 | end
15 | end
16 |
17 | RSpec.describe Honeycomb::Rack do
18 | include Rack::Test::Methods
19 | let(:libhoney_client) { Libhoney::TestClient.new }
20 | let(:event_data) { libhoney_client.events.map(&:data) }
21 | let(:base_test_app) { AnApp.new }
22 | let(:configuration) do
23 | Honeycomb::Configuration.new.tap do |config|
24 | config.client = libhoney_client
25 | end
26 | end
27 | let(:client) { Honeycomb::Client.new(configuration: configuration) }
28 | let(:honeycomb) do
29 | Honeycomb::Rack::Middleware.new(base_test_app, client: client)
30 | end
31 | let(:auth) { Authenticate.new(honeycomb) }
32 | let(:warden) do
33 | Warden::Manager.new(auth) do |manager|
34 | manager.default_strategies :test
35 | end
36 | end
37 | let(:secret) { "honeycombsecretneedstobe64characterslongforrack3sonowitslongeryay" }
38 | let(:session) { Rack::Session::Cookie.new(warden, secret: secret) }
39 | let(:lint) { Rack::Lint.new(session) }
40 | let(:app) { lint }
41 |
42 | class User
43 | def id
44 | 1
45 | end
46 |
47 | def email
48 | "support@honeycomb.io"
49 | end
50 |
51 | def name
52 | "bee"
53 | end
54 |
55 | def first_name
56 | "bee_first"
57 | end
58 |
59 | def last_name
60 | "bee_last"
61 | end
62 |
63 | def created_at
64 | Time.new 2002, 3, 4, 5, 6, 7
65 | end
66 | end
67 |
68 | class Authenticate
69 | def initialize(app)
70 | @app = app
71 | end
72 |
73 | def call(env)
74 | env["warden"].authenticate!
75 | @app.call(env)
76 | end
77 | end
78 |
79 | class TestStrategy < ::Warden::Strategies::Base
80 | def valid?
81 | true
82 | end
83 |
84 | def authenticate!
85 | success!(User.new)
86 | end
87 | end
88 |
89 | before do
90 | Warden::Strategies.add(:test, TestStrategy)
91 | header("Http-Version", "HTTP/1.0")
92 | header("User-Agent", "RackSpec")
93 | header("Content-Type", "text/html; charset=UTF-8")
94 | header("Accept", "*/*")
95 | header("Accept-Encoding", "gzip")
96 | header("Accept-Language", "*")
97 | header("X-Forwarded-For", "1.2.3.4")
98 | header("X-Forwarded-Proto", "https")
99 | header("X-Forwarded-Port", "8000")
100 | header("Referer", "https://forever.misspelled.referer.example.com/")
101 | end
102 |
103 | describe "standard request" do
104 | before do
105 | get "/?honey=bee"
106 | end
107 |
108 | it "returns ok" do
109 | expect(last_response).to be_ok
110 | end
111 |
112 | it "sends a single event" do
113 | expect(libhoney_client.events.size).to eq 1
114 | end
115 |
116 | it "includes package information" do
117 | libhoney_client.events.first.tap do |event|
118 | expect(event.data).to include(
119 | "meta.package" => "rack",
120 | "meta.package_version" => ::Rack.release,
121 | )
122 | end
123 | end
124 |
125 | USER_FIELDS = [
126 | "user.id",
127 | "user.email",
128 | "user.name",
129 | "user.first_name",
130 | "user.last_name",
131 | "user.created_at",
132 | ].freeze
133 | it_behaves_like "event data",
134 | http_fields: true, additional_fields: USER_FIELDS
135 | end
136 |
137 | describe "trace header request" do
138 | let(:trace_id) { "trace_id" }
139 | let(:parent_id) { "parent_id" }
140 | let(:dataset) { "test_datatset" }
141 |
142 | let(:serialized_trace) do
143 | "1;trace_id=#{trace_id},parent_id=#{parent_id},dataset=#{dataset}"
144 | end
145 |
146 | before do
147 | header("X-Honeycomb-Trace", serialized_trace)
148 | get "/?honey=bee"
149 | end
150 |
151 | it "returns ok" do
152 | expect(last_response).to be_ok
153 | end
154 |
155 | it "sends a single event" do
156 | expect(libhoney_client.events.size).to eq 1
157 | end
158 |
159 | it "has the expected dataset" do
160 | expect(libhoney_client.events.first.dataset).to eq(dataset)
161 | end
162 |
163 | it "has the expected fields from the header" do
164 | libhoney_client.events.first.tap do |event|
165 | expect(event.data).to include(
166 | "trace.trace_id" => trace_id,
167 | "trace.parent_id" => parent_id,
168 | )
169 | end
170 | end
171 |
172 | it_behaves_like "event data", http_fields: true
173 | end
174 | end
175 | end
176 |
--------------------------------------------------------------------------------
/spec/honeycomb/integrations/sequel_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | if defined?(Honeycomb::Sequel)
4 | RSpec.describe Honeycomb::Sequel do
5 | let(:libhoney_client) { Libhoney::TestClient.new }
6 | let(:configuration) do
7 | Honeycomb::Configuration.new.tap do |config|
8 | config.client = libhoney_client
9 | end
10 | end
11 | let(:client) { Honeycomb::Client.new(configuration: configuration) }
12 |
13 | let(:db) do
14 | Sequel.mock.tap do |db|
15 | db.extension :honeycomb
16 | db.honeycomb_client = client
17 | end
18 | end
19 |
20 | before do
21 | exec_sql("SELECT * FROM items")
22 | end
23 |
24 | def exec_sql(sql)
25 | db[sql].all
26 | end
27 |
28 | it "sends the right number of events" do
29 | expect(libhoney_client.events.size).to eq 1
30 | end
31 |
32 | let(:event_data) { libhoney_client.events.map(&:data) }
33 |
34 | it_behaves_like "event data"
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/spec/honeycomb/integrations/sinatra_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | if defined?(Honeycomb::Sinatra)
4 | require "rack/test"
5 | require "sinatra/base"
6 |
7 | RSpec.describe Honeycomb::Sinatra do
8 | include Rack::Test::Methods
9 |
10 | class App < Sinatra::Application
11 | set :host_authorization, permitted_hosts: []
12 |
13 | get "/" do
14 | "Hello world"
15 | end
16 | end
17 |
18 | let(:libhoney_client) { Libhoney::TestClient.new }
19 | let(:configuration) do
20 | Honeycomb::Configuration.new.tap do |config|
21 | config.client = libhoney_client
22 | end
23 | end
24 | let(:client) { Honeycomb::Client.new(configuration: configuration) }
25 |
26 | let(:app) { App }
27 |
28 | before do
29 | app.use Honeycomb::Sinatra::Middleware,
30 | client: client
31 | end
32 |
33 | before do
34 | get "/"
35 | end
36 |
37 | it "returns ok" do
38 | expect(last_response).to be_ok
39 | end
40 |
41 | it "sends a single event" do
42 | expect(libhoney_client.events.size).to eq 1
43 | end
44 |
45 | let(:event_data) { libhoney_client.events.map(&:data) }
46 |
47 | it_behaves_like "event data", additional_fields: ["request.route"]
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/spec/honeycomb/propagation/aws_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "securerandom"
4 | require "honeycomb/propagation/context"
5 | require "honeycomb/propagation/aws"
6 |
7 | RSpec.shared_examples "aws_propagation_parse" do
8 | it "handles a nil trace" do
9 | expect(aws_propagation.parse(nil)).to eq [nil, nil, nil, nil]
10 | end
11 |
12 | it "handles invalid string" do
13 | expect(aws_propagation.parse("test")).to eq [nil, nil, nil, nil]
14 | end
15 |
16 | it "handles only having trace id being specified" do
17 | expect(aws_propagation.parse("Root=root")).to eq ["root", "root", nil, nil]
18 | end
19 |
20 | it "handles no trace id being specified" do
21 | expect(aws_propagation.parse("Parent=1")).to eq [nil, nil, nil, nil]
22 | end
23 |
24 | it "handles having root and parent specified" do
25 | serialized_trace =
26 | "Root=root;Parent=parent"
27 | expect(aws_propagation.parse(serialized_trace)).to eq [
28 | "root",
29 | "parent",
30 | nil,
31 | nil,
32 | ]
33 | end
34 |
35 | it "handles having root and self specified" do
36 | serialized_trace =
37 | "Root=root;Self=self"
38 | expect(aws_propagation.parse(serialized_trace)).to eq [
39 | "root",
40 | "self",
41 | nil,
42 | nil,
43 | ]
44 | end
45 |
46 | it "handles having root, self, and parent specified, self should win" do
47 | serialized_trace =
48 | "Root=root;Parent=parent;Self=self"
49 | expect(aws_propagation.parse(serialized_trace)).to eq [
50 | "root",
51 | "self",
52 | nil,
53 | nil,
54 | ]
55 | end
56 |
57 | it "handles having root, self, and parent specified, unordered, self wins" do
58 | serialized_trace =
59 | "Self=self;Parent=parent;Root=root"
60 | expect(aws_propagation.parse(serialized_trace)).to eq [
61 | "root",
62 | "self",
63 | nil,
64 | nil,
65 | ]
66 | end
67 |
68 | it "handles with case insensitivity" do
69 | serialized_trace =
70 | "self=self;parent=parent;root=root"
71 | expect(aws_propagation.parse(serialized_trace)).to eq [
72 | "root",
73 | "self",
74 | nil,
75 | nil,
76 | ]
77 | end
78 |
79 | it "handles parsing a context" do
80 | serialized_trace =
81 | "Root=root;Self=self;userID=1;test=true"
82 | expect(aws_propagation.parse(serialized_trace)).to eq [
83 | "root",
84 | "self",
85 | { "test" => "true", "userID" => "1" },
86 | nil,
87 | ]
88 | end
89 |
90 | it "handles bad formating in trace fields" do
91 | serialized_trace =
92 | "Root=root;Self=self;userID=1;=true"
93 | expect(aws_propagation.parse(serialized_trace)).to eq [
94 | "root",
95 | "self",
96 | { "userID" => "1" },
97 | nil,
98 | ]
99 | end
100 | end
101 |
102 | RSpec.describe Honeycomb::AWSPropagation::UnmarshalTraceContext do
103 | describe "module usage" do
104 | let(:aws_propagation) { Class.new.extend(subject) }
105 | include_examples "aws_propagation_parse"
106 | end
107 |
108 | describe "class method usage" do
109 | let(:aws_propagation) { subject }
110 | include_examples "aws_propagation_parse"
111 | end
112 | end
113 |
114 | RSpec.describe Honeycomb::AWSPropagation::MarshalTraceContext do
115 | describe "module usage" do
116 | let(:builder) { instance_double("Builder", dataset: "rails") }
117 | let(:trace) { instance_double("Trace", id: 2, fields: {}) }
118 | let(:span) do
119 | instance_double("Span", id: 1, trace: trace, builder: builder)
120 | .extend(subject)
121 | end
122 |
123 | it "can serialize a basic span" do
124 | expect(span.to_trace_header)
125 | .to eq("Root=2;Parent=1")
126 | end
127 | end
128 |
129 | describe "class method usage" do
130 | let(:context) { Honeycomb::Propagation::Context.new(2, 1, {}, "dataset") }
131 |
132 | it "can serialize a basic span" do
133 | expect(subject.to_trace_header(context))
134 | .to eq("Root=2;Parent=1")
135 | end
136 | end
137 | end
138 |
139 | RSpec.describe "Propagation" do
140 | let(:parent_id) { SecureRandom.hex(8) }
141 | let(:dataset) { "dataset" }
142 | let(:trace_id) { SecureRandom.hex(16) }
143 | let(:fields) do
144 | {
145 | "test" => "honeycomb",
146 | }
147 | end
148 | let(:builder) { instance_double("Builder", dataset: dataset) }
149 | let(:trace) { instance_double("Trace", id: trace_id, fields: fields) }
150 | let(:span) do
151 | instance_double("Span", id: parent_id, trace: trace, builder: builder)
152 | .extend(Honeycomb::AWSPropagation::MarshalTraceContext)
153 | end
154 |
155 | let(:aws_propagation) do
156 | Class.new.extend(Honeycomb::AWSPropagation::UnmarshalTraceContext)
157 | end
158 |
159 | let(:output) do
160 | aws_propagation.parse(span.to_trace_header)
161 | end
162 |
163 | it "returns nil dataset" do
164 | expect(output[3]).to eq nil
165 | end
166 |
167 | it "produces the correct trace_id" do
168 | expect(output[0]).to eq trace_id
169 | end
170 |
171 | it "produces the correct parent_span_id" do
172 | expect(output[1]).to eq parent_id
173 | end
174 |
175 | it "produces the correct fields" do
176 | expect(output[2]).to eq fields
177 | end
178 | end
179 |
--------------------------------------------------------------------------------
/spec/honeycomb/propagation/default_modern_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "honeycomb/propagation/default_modern"
4 |
5 | RSpec.describe Honeycomb::DefaultModernPropagation::UnmarshalTraceContext do
6 | let(:parent_id) { SecureRandom.hex(8) }
7 | let(:dataset) { "dataset" }
8 | let(:trace_id) { SecureRandom.hex(16) }
9 | let(:builder) { instance_double("Builder", dataset: dataset) }
10 | let(:fields) { {} }
11 | let(:trace) { instance_double("Trace", id: trace_id, fields: fields) }
12 |
13 | let(:honeycomb_span) do
14 | instance_double("Span", id: parent_id, trace: trace, builder: builder)
15 | .extend(Honeycomb::PropagationSerializer)
16 | end
17 |
18 | let(:w3c_span) do
19 | instance_double("Span", id: parent_id, trace: trace, builder: builder)
20 | .extend(Honeycomb::W3CPropagation::MarshalTraceContext)
21 | end
22 |
23 | let(:default_propagation) { Class.new.extend(described_class) }
24 |
25 | describe "handles an incoming span from a Honeycomb trace" do
26 | let(:fields) do
27 | { "test" => "honeycomb" }
28 | end
29 |
30 | let(:rack_env) do
31 | { "HTTP_X_HONEYCOMB_TRACE" => honeycomb_span.to_trace_header }
32 | end
33 |
34 | let(:output) do
35 | expect(Honeycomb::W3CPropagation::UnmarshalTraceContext)
36 | .not_to receive(:parse_rack_env)
37 |
38 | expect(Honeycomb::HoneycombModernPropagation::UnmarshalTraceContext)
39 | .to receive(:parse_rack_env)
40 | .with(rack_env)
41 | .and_call_original
42 |
43 | default_propagation.parse_rack_env(rack_env)
44 | end
45 |
46 | it "produces the correct trace_id" do
47 | expect(output[0]).to eq trace_id
48 | end
49 |
50 | it "produces the correct parent_span_id" do
51 | expect(output[1]).to eq parent_id
52 | end
53 |
54 | it "produces the correct fields" do
55 | expect(output[2]).to eq fields
56 | end
57 |
58 | it "does not include a dataset" do
59 | expect(output[3]).to be_nil
60 | end
61 | end
62 |
63 | describe "handles an incoming span from a W3C trace" do
64 | let(:rack_env) do
65 | { "HTTP_TRACEPARENT" => w3c_span.to_trace_header }
66 | end
67 | let(:output) do
68 | expect(Honeycomb::HoneycombModernPropagation::UnmarshalTraceContext)
69 | .not_to receive(:parse_rack_env)
70 |
71 | expect(Honeycomb::W3CPropagation::UnmarshalTraceContext)
72 | .to receive(:parse_rack_env)
73 | .with(rack_env)
74 | .and_call_original
75 |
76 | default_propagation.parse_rack_env(rack_env)
77 | end
78 |
79 | it "produces the correct trace_id" do
80 | expect(output[0]).to eq trace_id
81 | end
82 |
83 | it "produces the correct parent_span_id" do
84 | expect(output[1]).to eq parent_id
85 | end
86 |
87 | it "returns nil fields" do
88 | expect(output[2]).to eq nil
89 | end
90 |
91 | it "returns nil dataset" do
92 | expect(output[3]).to eq nil
93 | end
94 | end
95 |
96 | describe "prefers Honeycomb trace header over W3C when both are present" do
97 | let(:fields) do
98 | { "test" => "honeycomb" }
99 | end
100 |
101 | let(:rack_env) do
102 | { "HTTP_X_HONEYCOMB_TRACE" => honeycomb_span.to_trace_header,
103 | "HTTP_TRACEPARENT" => w3c_span.to_trace_header }
104 | end
105 | let(:output) do
106 | expect(Honeycomb::W3CPropagation::UnmarshalTraceContext)
107 | .not_to receive(:parse_rack_env)
108 |
109 | expect(Honeycomb::HoneycombModernPropagation::UnmarshalTraceContext)
110 | .to receive(:parse_rack_env)
111 | .with(rack_env)
112 | .and_call_original
113 |
114 | default_propagation.parse_rack_env(rack_env)
115 | end
116 |
117 | it "produces the correct trace_id" do
118 | expect(output[0]).to eq trace_id
119 | end
120 |
121 | it "produces the correct parent_span_id" do
122 | expect(output[1]).to eq parent_id
123 | end
124 |
125 | it "produces the correct fields" do
126 | expect(output[2]).to eq fields
127 | end
128 |
129 | it "does not include a dataset" do
130 | expect(output[3]).to be_nil
131 | end
132 | end
133 | end
134 |
--------------------------------------------------------------------------------
/spec/honeycomb/propagation/default_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "honeycomb/propagation/default"
4 |
5 | RSpec.describe Honeycomb::DefaultPropagation::UnmarshalTraceContext do
6 | let(:parent_id) { SecureRandom.hex(8) }
7 | let(:dataset) { "dataset" }
8 | let(:trace_id) { SecureRandom.hex(16) }
9 | let(:builder) { instance_double("Builder", dataset: dataset) }
10 | let(:fields) { {} }
11 | let(:trace) { instance_double("Trace", id: trace_id, fields: fields) }
12 |
13 | let(:honeycomb_span) do
14 | instance_double("Span", id: parent_id, trace: trace, builder: builder)
15 | .extend(Honeycomb::PropagationSerializer)
16 | end
17 |
18 | let(:w3c_span) do
19 | instance_double("Span", id: parent_id, trace: trace, builder: builder)
20 | .extend(Honeycomb::W3CPropagation::MarshalTraceContext)
21 | end
22 |
23 | let(:default_propagation) { Class.new.extend(described_class) }
24 |
25 | describe "handles an incoming span from a Honeycomb trace" do
26 | let(:fields) do
27 | { "test" => "honeycomb" }
28 | end
29 |
30 | let(:rack_env) do
31 | { "HTTP_X_HONEYCOMB_TRACE" => honeycomb_span.to_trace_header }
32 | end
33 |
34 | let(:output) do
35 | expect(Honeycomb::W3CPropagation::UnmarshalTraceContext)
36 | .not_to receive(:parse_rack_env)
37 |
38 | expect(Honeycomb::HoneycombPropagation::UnmarshalTraceContext)
39 | .to receive(:parse_rack_env)
40 | .with(rack_env)
41 | .and_call_original
42 |
43 | default_propagation.parse_rack_env(rack_env)
44 | end
45 |
46 | it "produces the correct trace_id" do
47 | expect(output[0]).to eq trace_id
48 | end
49 |
50 | it "produces the correct parent_span_id" do
51 | expect(output[1]).to eq parent_id
52 | end
53 |
54 | it "produces the correct fields" do
55 | expect(output[2]).to eq fields
56 | end
57 |
58 | it "produces the correct dataset" do
59 | expect(output[3]).to eq dataset
60 | end
61 | end
62 |
63 | describe "handles an incoming span from a W3C trace" do
64 | let(:rack_env) do
65 | { "HTTP_TRACEPARENT" => w3c_span.to_trace_header }
66 | end
67 | let(:output) do
68 | expect(Honeycomb::HoneycombPropagation::UnmarshalTraceContext)
69 | .not_to receive(:parse_rack_env)
70 |
71 | expect(Honeycomb::W3CPropagation::UnmarshalTraceContext)
72 | .to receive(:parse_rack_env)
73 | .with(rack_env)
74 | .and_call_original
75 |
76 | default_propagation.parse_rack_env(rack_env)
77 | end
78 |
79 | it "produces the correct trace_id" do
80 | expect(output[0]).to eq trace_id
81 | end
82 |
83 | it "produces the correct parent_span_id" do
84 | expect(output[1]).to eq parent_id
85 | end
86 |
87 | it "returns nil fields" do
88 | expect(output[2]).to eq nil
89 | end
90 |
91 | it "returns nil dataset" do
92 | expect(output[3]).to eq nil
93 | end
94 | end
95 |
96 | describe "prefers Honeycomb trace header over W3C when both are present" do
97 | let(:fields) do
98 | { "test" => "honeycomb" }
99 | end
100 |
101 | let(:rack_env) do
102 | { "HTTP_X_HONEYCOMB_TRACE" => honeycomb_span.to_trace_header,
103 | "HTTP_TRACEPARENT" => w3c_span.to_trace_header }
104 | end
105 | let(:output) do
106 | expect(Honeycomb::W3CPropagation::UnmarshalTraceContext)
107 | .not_to receive(:parse_rack_env)
108 |
109 | expect(Honeycomb::HoneycombPropagation::UnmarshalTraceContext)
110 | .to receive(:parse_rack_env)
111 | .with(rack_env)
112 | .and_call_original
113 |
114 | default_propagation.parse_rack_env(rack_env)
115 | end
116 |
117 | it "produces the correct trace_id" do
118 | expect(output[0]).to eq trace_id
119 | end
120 |
121 | it "produces the correct parent_span_id" do
122 | expect(output[1]).to eq parent_id
123 | end
124 |
125 | it "produces the correct fields" do
126 | expect(output[2]).to eq fields
127 | end
128 |
129 | it "produces the correct dataset" do
130 | expect(output[3]).to eq dataset
131 | end
132 | end
133 | end
134 |
--------------------------------------------------------------------------------
/spec/honeycomb/propagation/w3c_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "securerandom"
4 | require "honeycomb/propagation/context"
5 | require "honeycomb/propagation/w3c"
6 |
7 | RSpec.shared_examples "w3c_propagation_parse" do
8 | it "handles a nil trace" do
9 | expect(w3c_propagation.parse(nil)).to eq [nil, nil, nil, nil]
10 | end
11 |
12 | it "handles invalid string" do
13 | expect(w3c_propagation.parse("test")).to eq [nil, nil, nil, nil]
14 | end
15 |
16 | it "handles a standard w3c traceparent" do
17 | expect(w3c_propagation
18 | .parse("00-7f042f75651d9782dcff93a45fa99be0-c998e73e5420f609-01")).to eq [
19 | "7f042f75651d9782dcff93a45fa99be0",
20 | "c998e73e5420f609",
21 | nil,
22 | nil,
23 | ]
24 | end
25 |
26 | it "handles an unsupported version" do
27 | expect(w3c_propagation
28 | .parse("999-7f042f75651d9782dcff93a45fa99be0-c998e73e5420f609-01"))
29 | .to eq [
30 | nil, nil, nil, nil
31 | ]
32 | end
33 |
34 | it "handles an invalid trace id" do
35 | expect(w3c_propagation
36 | .parse("00-00000000000000000000000000000000-c998e73e5420f609-01")).to eq [
37 | nil, nil, nil, nil
38 | ]
39 | end
40 |
41 | it "handles an invalid parent span id" do
42 | expect(w3c_propagation
43 | .parse("00-7f042f75651d9782dcff93a45fa99be0-0000000000000000-01")).to eq [
44 | nil, nil, nil, nil
45 | ]
46 | end
47 |
48 | it "handles a missing trace id" do
49 | expect(w3c_propagation
50 | .parse("00-c998e73e5420f609-01")).to eq [
51 | nil, nil, nil, nil
52 | ]
53 | end
54 |
55 | it "handles a missing parent span id" do
56 | expect(w3c_propagation
57 | .parse("00-7f042f75651d9782dcff93a45fa99be0-01")).to eq [
58 | nil, nil, nil, nil
59 | ]
60 | end
61 | end
62 |
63 | RSpec.describe Honeycomb::W3CPropagation::UnmarshalTraceContext do
64 | describe "module usage" do
65 | let(:w3c_propagation) { Class.new.extend(subject) }
66 | include_examples "w3c_propagation_parse"
67 | end
68 |
69 | describe "class method usage" do
70 | let(:w3c_propagation) { subject }
71 | include_examples "w3c_propagation_parse"
72 | end
73 | end
74 |
75 | RSpec.describe Honeycomb::W3CPropagation::MarshalTraceContext do
76 | describe "module usage" do
77 | let(:parent_id) { SecureRandom.hex(8) }
78 | let(:trace_id) { SecureRandom.hex(16) }
79 | let(:builder) { instance_double("Builder", dataset: "rails") }
80 | let(:trace) { instance_double("Trace", id: trace_id, fields: {}) }
81 | let(:span) do
82 | instance_double("Span", id: parent_id, trace: trace, builder: builder)
83 | .extend(subject)
84 | end
85 |
86 | it "can serialize a basic span" do
87 | expect(span.to_trace_header)
88 | .to eq("00-#{trace_id}-#{parent_id}-01")
89 | end
90 | end
91 |
92 | describe "class method usage" do
93 | let(:parent_id) { SecureRandom.hex(8) }
94 | let(:trace_id) { SecureRandom.hex(16) }
95 | let(:context) do
96 | Honeycomb::Propagation::Context.new(trace_id, parent_id, {}, "rails")
97 | end
98 |
99 | it "can serialize a basic span" do
100 | expect(subject.to_trace_header(context))
101 | .to eq("00-#{trace_id}-#{parent_id}-01")
102 | end
103 | end
104 | end
105 |
106 | RSpec.describe "Propagation" do
107 | let(:parent_id) { SecureRandom.hex(8) }
108 | let(:dataset) { "dataset" }
109 | let(:trace_id) { SecureRandom.hex(16) }
110 | let(:builder) { instance_double("Builder", dataset: dataset) }
111 | let(:trace) { instance_double("Trace", id: trace_id, fields: {}) }
112 | let(:span) do
113 | instance_double("Span", id: parent_id, trace: trace, builder: builder)
114 | .extend(Honeycomb::W3CPropagation::MarshalTraceContext)
115 | end
116 |
117 | let(:w3c_propagation) do
118 | Class.new.extend(Honeycomb::W3CPropagation::UnmarshalTraceContext)
119 | end
120 |
121 | let(:output) do
122 | w3c_propagation.parse(span.to_trace_header)
123 | end
124 |
125 | it "returns nil dataset" do
126 | expect(output[3]).to eq nil
127 | end
128 |
129 | it "produces the correct trace_id" do
130 | expect(output[0]).to eq trace_id
131 | end
132 |
133 | it "produces the correct parent_span_id" do
134 | expect(output[1]).to eq parent_id
135 | end
136 | end
137 |
--------------------------------------------------------------------------------
/spec/honeycomb/propagation_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "securerandom"
4 | require "honeycomb/propagation"
5 |
6 | RSpec.describe Honeycomb::PropagationParser do
7 | let(:propagation) { Class.new.extend(subject) }
8 |
9 | it "handles a nil trace" do
10 | expect(propagation.parse(nil)).to eq [nil, nil, nil, nil]
11 | end
12 |
13 | it "handles invalid string" do
14 | expect(propagation.parse("test")).to eq [nil, nil, nil, nil]
15 | end
16 |
17 | it "handles only having trace id being specified" do
18 | expect(propagation.parse("1;trace_id=1")).to eq [nil, nil, nil, nil]
19 | end
20 |
21 | it "handles only having parent span id being specified" do
22 | expect(propagation.parse("1;parent_id=1")).to eq [nil, nil, nil, nil]
23 | end
24 |
25 | it "handles having trace and parent id specified" do
26 | serialized_trace =
27 | "1;trace_id=trace_id,parent_id=parent_id"
28 | expect(propagation.parse(serialized_trace)).to eq [
29 | "trace_id",
30 | "parent_id",
31 | nil,
32 | nil,
33 | ]
34 | end
35 |
36 | it "handles a dataset" do
37 | serialized_trace =
38 | "1;trace_id=trace_id,parent_id=parent_id,dataset=dataset"
39 | expect(propagation.parse(serialized_trace)).to eq [
40 | "trace_id",
41 | "parent_id",
42 | nil,
43 | "dataset",
44 | ]
45 | end
46 |
47 | it "handles parsing a context" do
48 | serialized_trace =
49 | "1;trace_id=trace_id,parent_id=parent_id,context=eyJ0ZXN0IjoxfQ=="
50 | expect(propagation.parse(serialized_trace)).to eq [
51 | "trace_id",
52 | "parent_id",
53 | { "test" => 1 },
54 | nil,
55 | ]
56 | end
57 |
58 | it "handles invalid json" do
59 | serialized_trace =
60 | "1;trace_id=trace_id,parent_id=parent_id,context=dGVzdA=="
61 | expect(propagation.parse(serialized_trace)).to eq [
62 | "trace_id",
63 | "parent_id",
64 | {},
65 | nil,
66 | ]
67 | end
68 | end
69 |
70 | RSpec.describe Honeycomb::PropagationSerializer do
71 | let(:builder) { instance_double("Builder", dataset: "rails") }
72 | let(:trace) { instance_double("Trace", id: 2, fields: {}) }
73 | let(:span) do
74 | instance_double("Span", id: 1, trace: trace, builder: builder)
75 | .extend(subject)
76 | end
77 |
78 | it "can serialize a basic span" do
79 | expect(span.to_trace_header)
80 | .to eq("1;dataset=rails,trace_id=2,parent_id=1,context=e30=")
81 | end
82 | end
83 |
84 | RSpec.describe "Propagation" do
85 | let(:parent_id) { SecureRandom.hex(8) }
86 | let(:dataset) { "rails,tesing/with-%characters%" }
87 | let(:trace_id) { SecureRandom.hex(16) }
88 | let(:fields) do
89 | {
90 | "test" => "honeycomb",
91 | }
92 | end
93 | let(:builder) { instance_double("Builder", dataset: dataset) }
94 | let(:trace) { instance_double("Trace", id: trace_id, fields: fields) }
95 | let(:span) do
96 | instance_double("Span", id: parent_id, trace: trace, builder: builder)
97 | .extend(Honeycomb::PropagationSerializer)
98 | end
99 |
100 | let(:propagation) { Class.new.extend(Honeycomb::PropagationParser) }
101 |
102 | let(:output) do
103 | propagation.parse(span.to_trace_header)
104 | end
105 |
106 | it "produces the correct dataset" do
107 | expect(output[3]).to eq dataset
108 | end
109 |
110 | it "produces the correct trace_id" do
111 | expect(output[0]).to eq trace_id
112 | end
113 |
114 | it "produces the correct parent_span_id" do
115 | expect(output[1]).to eq parent_id
116 | end
117 |
118 | it "produces the correct fields" do
119 | expect(output[2]).to eq fields
120 | end
121 | end
122 |
--------------------------------------------------------------------------------
/spec/honeycomb/trace_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "libhoney"
4 |
5 | RSpec.describe Honeycomb::Trace do
6 | let(:libhoney_client) { Libhoney::TestClient.new }
7 | let(:context) { Honeycomb::Context.new }
8 | let(:builder) { libhoney_client.builder }
9 | let(:presend_hook) { proc {} }
10 | let(:sample_hook) { proc {} }
11 |
12 | it "passes the hooks to the root span" do
13 | expect(Honeycomb::Span)
14 | .to receive(:new)
15 | .with(hash_including(
16 | presend_hook: presend_hook,
17 | sample_hook: sample_hook,
18 | ))
19 | .and_call_original
20 |
21 | Honeycomb::Trace.new(
22 | builder: builder,
23 | context: context,
24 | presend_hook: presend_hook,
25 | sample_hook: sample_hook,
26 | )
27 | end
28 | end
29 |
30 | RSpec.describe Honeycomb::Trace do
31 | let(:libhoney_client) { Libhoney::TestClient.new(dataset: "awesome") }
32 | let(:builder) { libhoney_client.builder }
33 |
34 | subject(:trace) do
35 | Honeycomb::Trace.new(builder: builder,
36 | context: Honeycomb::Context.new)
37 | end
38 |
39 | let(:trace_fields) { { "wow" => 420 } }
40 | let(:upstream_trace_header) { trace.root_span.to_trace_header }
41 |
42 | let(:distributed_trace) do
43 | Honeycomb::Trace.new(builder: Libhoney::TestClient.new(dataset: "awesome squared").builder,
44 | context: context,
45 | serialized_trace: upstream_trace_header)
46 | end
47 |
48 | before do
49 | trace_fields.each do |key, value|
50 | trace.add_field key, value
51 | end
52 | end
53 |
54 | it_behaves_like "a tracing object"
55 |
56 | describe "distributed tracing" do
57 | describe "with a classic key" do
58 | let(:context) { Honeycomb::Context.new.tap { |c| c.classic = true } }
59 |
60 | it "context should be classic" do
61 | expect(context.classic?).to be true
62 | end
63 |
64 | it "preserves the trace_id" do
65 | expect(distributed_trace.id).to eq trace.id
66 | end
67 |
68 | it "preserves the parent_id" do
69 | root_span = distributed_trace.root_span
70 | parent_id = root_span.instance_variable_get("@parent_id")
71 | expect(parent_id).to eq trace.root_span.id
72 | end
73 |
74 | it "preserves the trace_fields" do
75 | expect(distributed_trace.fields).to eq trace_fields
76 | end
77 |
78 | it "uses the dataset specified by the trace header" do
79 | root_span = distributed_trace.root_span
80 | builder = root_span.instance_variable_get("@builder")
81 | expect(builder.dataset).to eq "awesome"
82 | end
83 | end
84 |
85 | describe "with a modern key" do
86 | let(:context) { Honeycomb::Context.new.tap { |c| c.classic = false } }
87 | it "preserves the trace_id" do
88 | expect(distributed_trace.id).to eq trace.id
89 | end
90 |
91 | it "preserves the parent_id" do
92 | root_span = distributed_trace.root_span
93 | parent_id = root_span.instance_variable_get("@parent_id")
94 | expect(parent_id).to eq trace.root_span.id
95 | end
96 |
97 | it "preserves the trace_fields" do
98 | expect(distributed_trace.fields).to eq trace_fields
99 | end
100 |
101 | it "ignores the dataset in the trace header and uses the dataset configured for the client" do
102 | root_span = distributed_trace.root_span
103 | builder = root_span.instance_variable_get("@builder")
104 | expect(builder.dataset).to eq "awesome squared"
105 | end
106 | end
107 | end
108 | end
109 |
110 | RSpec.describe Honeycomb::Trace do
111 | let(:libhoney_client) { Libhoney::TestClient.new }
112 | let(:context) { Honeycomb::Context.new }
113 | let(:builder) { libhoney_client.builder }
114 | let(:trace) { Honeycomb::Trace.new(builder: builder, context: context) }
115 | let(:serialized_trace) { trace.root_span.to_trace_header }
116 | let(:parser_hook) do
117 | double("parser_hook").tap do |hook|
118 | allow(hook).to receive(:call)
119 | end
120 | end
121 | subject(:distributed_trace) do
122 | Honeycomb::Trace.new(builder: builder,
123 | context: context,
124 | parser_hook: parser_hook,
125 | serialized_trace: serialized_trace)
126 | end
127 |
128 | it "should have the attributes provided by the serialized_trace" do
129 | expect(distributed_trace).to have_attributes(id: trace.id)
130 | end
131 | end
132 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "bundler/setup"
4 | require "simplecov"
5 | require "simplecov-console"
6 | require "webmock/rspec"
7 | require "pry"
8 |
9 | WebMock.disable_net_connect!
10 |
11 | Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
12 |
13 | SimpleCov.start do
14 | add_filter "/spec/"
15 | add_filter "Rakefile"
16 |
17 | add_group "Integrations", "lib/honeycomb/integrations"
18 | add_group "Propagation", "lib/honeycomb/propagation"
19 | # Make coverage work with Appraisals
20 | current_gemfile = ENV.fetch("BUNDLE_GEMFILE", "").split("/").last
21 | command_name current_gemfile if current_gemfile
22 |
23 | if ENV["CI"]
24 | coverage_dir("coverage/#{ENV['CIRCLE_JOB']}")
25 | formatter SimpleCov::Formatter::SimpleFormatter
26 | else
27 | formatter SimpleCov::Formatter::MultiFormatter.new(
28 | [
29 | SimpleCov::Formatter::SimpleFormatter,
30 | SimpleCov::Formatter::HTMLFormatter,
31 | ],
32 | )
33 | end
34 | end
35 |
36 | require "honeycomb-beeline"
37 |
38 | RSpec.configure do |config|
39 | # Enable flags like --only-failures and --next-failure
40 | config.example_status_persistence_file_path = ".rspec_status"
41 |
42 | # Disable RSpec exposing methods globally on `Module` and `main`
43 | config.disable_monkey_patching!
44 |
45 | config.expect_with :rspec do |c|
46 | c.syntax = :expect
47 | end
48 |
49 | # allow :focus to be applied to specific tests for debugging
50 | config.filter_run_when_matching :focus
51 | end
52 |
--------------------------------------------------------------------------------
/spec/support/event_data_shared_examples.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | BASE_FIELDS = %w[
4 | duration_ms
5 | meta.beeline_version
6 | meta.local_hostname
7 | meta.span_type
8 | meta.instrumentations
9 | meta.instrumentations_count
10 | trace.span_id
11 | trace.trace_id
12 | ].freeze
13 |
14 | PACKAGE_FIELDS = %w[
15 | meta.package
16 | meta.package_version
17 | ].freeze
18 |
19 | HTTP_FIELDS = %w[
20 | response.status_code
21 | request.method
22 | request.path
23 | request.query_string
24 | request.host
25 | request.remote_addr
26 | request.header.accept
27 | request.header.accept_encoding
28 | request.header.accept_language
29 | request.header.content_type
30 | request.header.referer
31 | request.header.user_agent
32 | request.header.x_forwarded_for
33 | request.header.x_forwarded_proto
34 | request.header.x_forwarded_port
35 | request.secure
36 | request.xhr
37 | request.scheme
38 | ].freeze
39 |
40 | RSpec.shared_examples "event data" do |package_fields: true, http_fields: false, additional_fields: []|
41 | describe "data" do
42 | it "is present" do
43 | # .all? on an Enumerable like event_data will return true when the collection is empty.
44 | # Confirm here that there are events to test.
45 | expect(event_data).not_to be_empty
46 | end
47 |
48 | BASE_FIELDS.each do |field|
49 | it "includes #{field}" do
50 | expect(event_data).to all(include field)
51 | end
52 | end
53 |
54 | if package_fields
55 | PACKAGE_FIELDS.each do |field|
56 | it "includes #{field}" do
57 | # the package fields will only be on the root span which should be
58 | # the last event to be sent in each test
59 | event = event_data.last
60 | expect(event).to include field
61 | end
62 | end
63 | end
64 |
65 | if http_fields
66 | HTTP_FIELDS.each do |field|
67 | it "includes #{field}" do
68 | expect(event_data).to all(include field)
69 | end
70 | end
71 | end
72 |
73 | additional_fields.each do |field|
74 | it "includes #{field}" do
75 | expect(event_data).to all(include field)
76 | end
77 | end
78 | end
79 | end
80 |
--------------------------------------------------------------------------------
/spec/support/test_tasks.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | namespace :test do
4 | task :event_data
5 |
6 | task :name
7 |
8 | desc "this is a description"
9 | task :description
10 |
11 | task :arguments, %i[a b c]
12 |
13 | namespace :client do
14 | task :access do |t|
15 | t.honeycomb_client.start_span(name: "inner task span") { :ok }
16 | end
17 |
18 | task :enabled
19 |
20 | task disabled: :enabled do
21 | Honeycomb.start_span(name: "global honeycomb client is still enabled") { :ok }
22 | end
23 |
24 | task :default do
25 | Honeycomb.start_span(name: "global honeycomb client") { :ok } if Honeycomb.client
26 | end
27 |
28 | task custom: :default
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/spec/support/tracing_object_shared_examples.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.shared_examples "a tracing object" do
4 | it "can add fields" do
5 | subject.add_field("key", "value")
6 | end
7 |
8 | it "can add rollup fields" do
9 | subject.add_rollup_field("key", 1)
10 | end
11 |
12 | it "can be sent" do
13 | subject.send
14 | end
15 | end
16 |
--------------------------------------------------------------------------------