├── .circleci ├── bundler_version.sh ├── config.yml └── setup-rubygems.sh ├── .editorconfig ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ ├── question-discussion.md │ └── security-vulnerability-report.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── release.yml └── workflows │ ├── add-to-project-v2.yml │ ├── apply-labels.yml │ ├── stale.yml │ └── validate-pr-title.yml ├── .gitignore ├── .overcommit.yml ├── .rspec ├── .rubocop.yml ├── .ruby-version ├── .tool-versions ├── Appraisals ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── DEVELOPING.md ├── Gemfile ├── LICENSE ├── NOTICE ├── OSSMETADATA ├── README.md ├── RELEASING.md ├── Rakefile ├── SECURITY.md ├── SUPPORT.md ├── UPGRADING.md ├── bin ├── console └── setup ├── examples ├── hooks │ ├── Gemfile │ └── main.rb ├── propagation │ ├── Gemfile │ └── main.rb ├── rack │ ├── Gemfile │ └── main.rb ├── rails52 │ ├── .dockerignore │ ├── .gitignore │ ├── .node-version │ ├── .proxyconfig │ │ ├── htpasswd │ │ └── squid.conf │ ├── .ruby-version │ ├── Dockerfile │ ├── Gemfile │ ├── README.md │ ├── Rakefile │ ├── app │ │ ├── assets │ │ │ ├── config │ │ │ │ └── manifest.js │ │ │ ├── images │ │ │ │ └── .keep │ │ │ ├── javascripts │ │ │ │ ├── application.js │ │ │ │ ├── bees.coffee │ │ │ │ ├── cable.js │ │ │ │ └── channels │ │ │ │ │ └── .keep │ │ │ └── stylesheets │ │ │ │ ├── application.css │ │ │ │ ├── bees.scss │ │ │ │ └── scaffolds.scss │ │ ├── channels │ │ │ └── application_cable │ │ │ │ ├── channel.rb │ │ │ │ └── connection.rb │ │ ├── controllers │ │ │ ├── application_controller.rb │ │ │ ├── bees_controller.rb │ │ │ └── concerns │ │ │ │ └── .keep │ │ ├── helpers │ │ │ ├── application_helper.rb │ │ │ └── bees_helper.rb │ │ ├── jobs │ │ │ ├── application_job.rb │ │ │ ├── poke_job.rb │ │ │ ├── schedule_pokes_job.rb │ │ │ └── sidekiq_report_job.rb │ │ ├── mailers │ │ │ └── application_mailer.rb │ │ ├── models │ │ │ ├── application_record.rb │ │ │ ├── bee.rb │ │ │ └── concerns │ │ │ │ └── .keep │ │ └── views │ │ │ ├── bees │ │ │ ├── _bee.json.jbuilder │ │ │ ├── _form.html.erb │ │ │ ├── edit.html.erb │ │ │ ├── index.html.erb │ │ │ ├── index.json.jbuilder │ │ │ ├── new.html.erb │ │ │ ├── show.html.erb │ │ │ └── show.json.jbuilder │ │ │ └── layouts │ │ │ ├── application.html.erb │ │ │ ├── mailer.html.erb │ │ │ └── mailer.text.erb │ ├── bin │ │ ├── bundle │ │ ├── rails │ │ ├── rake │ │ ├── setup │ │ ├── spring │ │ ├── update │ │ └── yarn │ ├── config.ru │ ├── config │ │ ├── application.rb │ │ ├── boot.rb │ │ ├── cable.yml │ │ ├── credentials.yml.enc │ │ ├── database.yml │ │ ├── environment.rb │ │ ├── environments │ │ │ ├── development.rb │ │ │ ├── production.rb │ │ │ └── test.rb │ │ ├── initializers │ │ │ ├── application_controller_renderer.rb │ │ │ ├── assets.rb │ │ │ ├── backtrace_silencers.rb │ │ │ ├── content_security_policy.rb │ │ │ ├── cookies_serializer.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── honeycomb.rb │ │ │ ├── inflections.rb │ │ │ ├── mime_types.rb │ │ │ ├── sidekiq.rb │ │ │ └── wrap_parameters.rb │ │ ├── job_schedule.yml │ │ ├── locales │ │ │ └── en.yml │ │ ├── puma.rb │ │ ├── routes.rb │ │ ├── spring.rb │ │ └── storage.yml │ ├── db │ │ ├── migrate │ │ │ └── 20190319182235_create_bees.rb │ │ ├── schema.rb │ │ └── seeds.rb │ ├── docker-compose.yml │ ├── lib │ │ ├── assets │ │ │ └── .keep │ │ └── tasks │ │ │ └── .keep │ ├── log │ │ └── .keep │ ├── package.json │ ├── public │ │ ├── 404.html │ │ ├── 422.html │ │ ├── 500.html │ │ ├── apple-touch-icon-precomposed.png │ │ ├── apple-touch-icon.png │ │ ├── favicon.ico │ │ └── robots.txt │ ├── storage │ │ └── .keep │ ├── test │ │ ├── application_system_test_case.rb │ │ ├── controllers │ │ │ ├── .keep │ │ │ └── bees_controller_test.rb │ │ ├── fixtures │ │ │ ├── .keep │ │ │ ├── bees.yml │ │ │ └── files │ │ │ │ └── .keep │ │ ├── helpers │ │ │ └── .keep │ │ ├── integration │ │ │ └── .keep │ │ ├── mailers │ │ │ └── .keep │ │ ├── models │ │ │ ├── .keep │ │ │ └── bee_test.rb │ │ ├── system │ │ │ ├── .keep │ │ │ └── bees_test.rb │ │ └── test_helper.rb │ ├── tmp │ │ └── .keep │ └── vendor │ │ └── .keep ├── sequel │ ├── Gemfile │ ├── README.md │ └── main.rb └── sinatra │ ├── Gemfile │ └── main.rb ├── gemfiles ├── .bundle │ └── config ├── .gitignore ├── aws_2.gemfile ├── aws_3.gemfile ├── faraday_0.gemfile ├── faraday_1.gemfile ├── rack_2.gemfile ├── rack_3.gemfile ├── rails_5.gemfile ├── rails_51.gemfile ├── rails_52.gemfile ├── rails_6.gemfile ├── rails_61.gemfile ├── redis_3.gemfile ├── redis_4.gemfile ├── sequel4.gemfile ├── sequel5.gemfile └── sinatra.gemfile ├── honeycomb-beeline.gemspec ├── lib ├── generators │ └── honeycomb │ │ └── honeycomb_generator.rb ├── honeycomb-beeline.rb ├── honeycomb │ ├── beeline │ │ └── version.rb │ ├── client.rb │ ├── configuration.rb │ ├── context.rb │ ├── deterministic_sampler.rb │ ├── integrations │ │ ├── active_support.rb │ │ ├── aws.rb │ │ ├── faraday.rb │ │ ├── rack.rb │ │ ├── rails.rb │ │ ├── railtie.rb │ │ ├── rake.rb │ │ ├── redis.rb │ │ ├── sequel.rb │ │ ├── sinatra.rb │ │ └── warden.rb │ ├── propagation.rb │ ├── propagation │ │ ├── aws.rb │ │ ├── context.rb │ │ ├── default.rb │ │ ├── default_modern.rb │ │ ├── honeycomb.rb │ │ ├── honeycomb_modern.rb │ │ └── w3c.rb │ ├── rollup_fields.rb │ ├── span.rb │ └── trace.rb └── sequel │ └── extensions │ └── honeycomb.rb └── spec ├── generators └── honeycomb │ └── honeycomb_generator_spec.rb ├── honeycomb ├── beeline_spec.rb ├── client_spec.rb ├── configuration_spec.rb ├── deterministic_sampler_spec.rb ├── integrations │ ├── active_support_spec.rb │ ├── aws_spec.rb │ ├── faraday_spec.rb │ ├── rack_spec.rb │ ├── rails_spec.rb │ ├── rake_spec.rb │ ├── redis_spec.rb │ ├── sequel_spec.rb │ └── sinatra_spec.rb ├── propagation │ ├── aws_spec.rb │ ├── default_modern_spec.rb │ ├── default_spec.rb │ ├── honeycomb_modern_spec.rb │ ├── honeycomb_spec.rb │ └── w3c_spec.rb ├── propagation_spec.rb ├── span_spec.rb └── trace_spec.rb ├── spec_helper.rb └── support ├── event_data_shared_examples.rb ├── test_tasks.rake └── tracing_object_shared_examples.rb /.circleci/bundler_version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ux 4 | 5 | if [[ "$BUNDLE_GEMFILE" =~ (rails_41.gemfile|rails_42.gemfile)$ ]]; then 6 | echo "Rails 4.1 and 4.2 require an old Bundler" 7 | gem uninstall -v '>= 2' -ax bundler 8 | gem install bundler -v '< 2' 9 | else 10 | echo "Get the latest Bundler" 11 | gem update bundler 12 | fi 13 | -------------------------------------------------------------------------------- /.circleci/setup-rubygems.sh: -------------------------------------------------------------------------------- 1 | mkdir ~/.gem 2 | echo -e "---\r\n:rubygems_api_key: $RUBYGEMS_API_KEY" > ~/.gem/credentials 3 | chmod 0600 /home/circleci/.gem/credentials 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | indent_size = 4 12 | trim_trailing_whitespace = true 13 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Code owners file. 2 | # This file controls who is tagged for review for any given pull request. 3 | 4 | # For anything not explicitly taken by someone else: 5 | * @honeycombio/pipeline-team 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Let us know if something is not working as expected 4 | title: '' 5 | labels: 'type: bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 17 | 18 | **Versions** 19 | 20 | - Ruby: 21 | - Beeline: 22 | 23 | **Steps to reproduce** 24 | 25 | 1. 26 | 27 | **Additional context** 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'type: enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 15 | 16 | **Is your feature request related to a problem? Please describe.** 17 | 18 | 19 | **Describe the solution you'd like** 20 | 21 | 22 | **Describe alternatives you've considered** 23 | 24 | 25 | **Additional context** 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question-discussion.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question/Discussion 3 | about: General question about how things work or a discussion 4 | title: '' 5 | labels: 'type: discussion' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/security-vulnerability-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Security vulnerability report 3 | about: Let us know if you discover a security vulnerability 4 | title: '' 5 | labels: 'type: security' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 15 | **Versions** 16 | 17 | - Ruby: 18 | - Beeline: 19 | 20 | **Description** 21 | 22 | (Please include any relevant CVE advisory links) 23 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | ## Which problem is this PR solving? 14 | 15 | - 16 | 17 | ## Short description of the changes 18 | 19 | - 20 | 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "bundler" # See documentation for possible values 9 | directory: "/gemfiles/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | labels: 13 | - "type: dependencies" 14 | reviewers: 15 | - "honeycombio/telemetry-team" 16 | commit-message: 17 | prefix: "maint" 18 | include: "scope" 19 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | # .github/release.yml 2 | 3 | changelog: 4 | exclude: 5 | labels: 6 | - no-changelog 7 | categories: 8 | - title: 💥 Breaking Changes 💥 9 | labels: 10 | - "version: bump major" 11 | - breaking-change 12 | - title: 💡 Enhancements 13 | labels: 14 | - "type: enhancement" 15 | - title: 🐛 Fixes 16 | labels: 17 | - "type: bug" 18 | - title: 🛠 Maintenance 19 | labels: 20 | - "type: maintenance" 21 | - "type: dependencies" 22 | - "type: documentation" 23 | - title: 🤷 Other Changes 24 | labels: 25 | - "*" 26 | -------------------------------------------------------------------------------- /.github/workflows/add-to-project-v2.yml: -------------------------------------------------------------------------------- 1 | name: Add to project 2 | on: 3 | issues: 4 | types: [opened] 5 | pull_request_target: 6 | types: [opened] 7 | jobs: 8 | add-to-project: 9 | runs-on: ubuntu-latest 10 | name: Add issues and PRs to project 11 | steps: 12 | - uses: actions/add-to-project@main 13 | with: 14 | project-url: https://github.com/orgs/honeycombio/projects/27 15 | github-token: ${{ secrets.GHPROJECTS_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/apply-labels.yml: -------------------------------------------------------------------------------- 1 | name: Apply project labels 2 | on: [issues, pull_request_target, label] 3 | jobs: 4 | apply-labels: 5 | runs-on: ubuntu-latest 6 | name: Apply common project labels 7 | steps: 8 | - uses: honeycombio/oss-management-actions/labels@v1 9 | with: 10 | github-token: ${{ secrets.GITHUB_TOKEN }} 11 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '30 1 * * *' 5 | 6 | jobs: 7 | stale: 8 | name: 'Close stale issues and PRs' 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | pull-requests: write 13 | 14 | steps: 15 | - uses: actions/stale@v4 16 | with: 17 | start-date: '2021-09-01T00:00:00Z' 18 | stale-issue-message: 'Marking this issue as stale because it has been open 14 days with no activity. Please add a comment if this is still an ongoing issue; otherwise this issue will be automatically closed in 7 days.' 19 | stale-pr-message: 'Marking this PR as stale because it has been open 30 days with no activity. Please add a comment if this PR is still relevant; otherwise this PR will be automatically closed in 7 days.' 20 | close-issue-message: 'Closing this issue due to inactivity. Please see our [Honeycomb OSS Lifecyle and Practices](https://github.com/honeycombio/home/blob/main/honeycomb-oss-lifecycle-and-practices.md).' 21 | close-pr-message: 'Closing this PR due to inactivity. Please see our [Honeycomb OSS Lifecyle and Practices](https://github.com/honeycombio/home/blob/main/honeycomb-oss-lifecycle-and-practices.md).' 22 | days-before-issue-stale: 14 23 | days-before-pr-stale: 30 24 | days-before-issue-close: 7 25 | days-before-pr-close: 7 26 | any-of-labels: 'status: info needed,status: revision needed' 27 | -------------------------------------------------------------------------------- /.github/workflows/validate-pr-title.yml: -------------------------------------------------------------------------------- 1 | name: "Validate PR Title" 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | main: 12 | name: Validate PR title 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: amannn/action-semantic-pull-request@v5 16 | id: lint_pr_title 17 | name: "🤖 Check PR title follows conventional commit spec" 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | # Have to specify all types because `maint` and `rel` aren't defaults 22 | types: | 23 | maint 24 | rel 25 | fix 26 | feat 27 | chore 28 | ci 29 | docs 30 | style 31 | refactor 32 | perf 33 | test 34 | ignoreLabels: | 35 | "type: dependencies" 36 | # When the previous steps fails, the workflow would stop. By adding this 37 | # condition you can continue the execution with the populated error message. 38 | - if: always() && (steps.lint_pr_title.outputs.error_message != null) 39 | name: "📝 Add PR comment about using conventional commit spec" 40 | uses: marocchino/sticky-pull-request-comment@v2 41 | with: 42 | header: pr-title-lint-error 43 | message: | 44 | Thank you for contributing to the project! 🎉 45 | 46 | We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted. 47 | 48 | Make sure to prepend with `feat:`, `fix:`, or another option in the list below. 49 | 50 | Once you update the title, this workflow will re-run automatically and validate the updated title. 51 | 52 | Details: 53 | 54 | ``` 55 | ${{ steps.lint_pr_title.outputs.error_message }} 56 | ``` 57 | 58 | # Delete a previous comment when the issue has been resolved 59 | - if: ${{ steps.lint_pr_title.outputs.error_message == null }} 60 | name: "❌ Delete PR comment after title has been updated" 61 | uses: marocchino/sticky-pull-request-comment@v2 62 | with: 63 | header: pr-title-lint-error 64 | delete: true 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /test/reports/ 9 | /tmp/ 10 | .idea/ 11 | 12 | # rspec failure tracking 13 | .rspec_status 14 | 15 | /examples/**/*.lock 16 | 17 | # intentionally omitting lock files 18 | Gemfile.lock 19 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | PreCommit: 2 | RuboCop: 3 | enabled: true 4 | command: ['bundle', 'exec', 'rubocop'] 5 | on_warn: fail # Treat all warnings as failures 6 | RSpec: 7 | enabled: true 8 | command: ['bundle', 'exec', 'rspec'] 9 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | <% if ENV['CI'] %> 2 | --format RspecJunitFormatter 3 | --out test/reports/result.xml 4 | <% end %> 5 | --format documentation 6 | --color 7 | --require spec_helper 8 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: rubocop-performance 2 | 3 | AllCops: 4 | TargetRubyVersion: 2.5 5 | Exclude: 6 | - "examples/**/*" 7 | - "vendor/**/*" 8 | 9 | Style/StringLiterals: 10 | EnforcedStyle: double_quotes 11 | 12 | Metrics/BlockLength: 13 | Exclude: 14 | - honeycomb-beeline.gemspec 15 | - spec/**/*.rb 16 | - spec/**/*.rake 17 | 18 | Metrics/AbcSize: 19 | Max: 50 20 | 21 | Metrics/ClassLength: 22 | Max: 200 23 | 24 | Metrics/MethodLength: 25 | Max: 25 26 | Exclude: 27 | - lib/generators/honeycomb/honeycomb_generator.rb 28 | 29 | Metrics/LineLength: 30 | Max: 100 31 | Exclude: 32 | - spec/honeycomb/integrations/active_support_spec.rb 33 | - spec/support/event_data_shared_examples.rb 34 | 35 | Metrics/ParameterLists: 36 | Max: 6 37 | 38 | Style/AccessModifierDeclarations: 39 | Enabled: false 40 | 41 | Style/FrozenStringLiteralComment: 42 | EnforcedStyle: always 43 | Exclude: 44 | - gemfiles/**/* 45 | 46 | Naming/FileName: 47 | Exclude: 48 | - lib/honeycomb-beeline.rb 49 | 50 | Style/SymbolProc: 51 | Exclude: 52 | - lib/honeycomb/span.rb 53 | 54 | Lint/HandleExceptions: 55 | Exclude: 56 | - lib/honeycomb-beeline.rb 57 | 58 | Style/TrailingCommaInArguments: 59 | EnforcedStyleForMultiline: comma 60 | 61 | Style/TrailingCommaInArrayLiteral: 62 | EnforcedStyleForMultiline: comma 63 | 64 | Style/TrailingCommaInHashLiteral: 65 | EnforcedStyleForMultiline: comma 66 | 67 | Style/AsciiComments: 68 | Enabled: false 69 | 70 | Style/Alias: 71 | EnforcedStyle: prefer_alias_method 72 | 73 | Style/IfUnlessModifier: 74 | Enabled: false 75 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.6.3 2 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | ruby 2.7.2 2 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | appraise "aws-2" do 4 | gem "aws-sdk", "~> 2" 5 | gem "webrick" 6 | end 7 | 8 | appraise "aws-3" do 9 | gem "aws-sdk", "~> 3" 10 | end 11 | 12 | appraise "faraday-0" do 13 | gem "faraday", "~> 0" 14 | end 15 | 16 | appraise "faraday-1" do 17 | gem "faraday", "~> 1" 18 | end 19 | 20 | appraise "sequel4" do 21 | gem "sequel", "~> 4" 22 | end 23 | 24 | appraise "sequel5" do 25 | gem "sequel", "~> 5" 26 | end 27 | 28 | appraise "sinatra" do 29 | gem "rack-test" 30 | gem "sinatra" 31 | gem "warden" 32 | end 33 | 34 | appraise "rack-2" do 35 | gem "rack", "~>2.0" 36 | gem "rack-test" 37 | gem "warden" 38 | end 39 | 40 | appraise "rack-3" do 41 | gem "rack", "~>3.0" 42 | gem "rack-session" 43 | gem "rack-test" 44 | gem "warden" 45 | end 46 | 47 | appraise "rails-5" do 48 | gem "rack-test" 49 | gem "rails", "~> 5.0.0" 50 | gem "sprockets", "~> 3" 51 | gem "warden" 52 | end 53 | 54 | appraise "rails-51" do 55 | gem "rack-test" 56 | gem "rails", "~> 5.1.0" 57 | gem "sprockets", "~> 3" 58 | gem "warden" 59 | end 60 | 61 | appraise "rails-52" do 62 | gem "rack-test" 63 | gem "rails", "~> 5.2.0" 64 | gem "sprockets", "~> 3" 65 | gem "warden" 66 | end 67 | 68 | appraise "rails-6" do 69 | gem "rack-test" 70 | gem "rails", "~> 6.0.0" 71 | gem "warden" 72 | end 73 | 74 | appraise "rails-61" do 75 | gem "rack-test" 76 | gem "rails", "~> 6.1.0" 77 | gem "warden" 78 | end 79 | 80 | appraise "redis-3" do 81 | gem "redis", "~> 3" 82 | end 83 | 84 | appraise "redis-4" do 85 | gem "redis", "~> 4" 86 | end 87 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the Honeycomb User Community Code of Conduct to clarify expected behavior in our community. 4 | 5 | https://www.honeycomb.io/honeycomb-user-community-code-of-conduct/ -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | Please see our [general guide for OSS lifecycle and practices.](https://github.com/honeycombio/home/blob/main/honeycomb-oss-lifecycle-and-practices.md) 4 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | * Sergio Mira 4 | * Alex Vondrak - [@ajvondrak](https://github.com/ajvondrak) 5 | -------------------------------------------------------------------------------- /DEVELOPING.md: -------------------------------------------------------------------------------- 1 | # Local Development 2 | 3 | ## Requirements 4 | 5 | Ruby: 6 | 7 | Rake: 8 | 9 | ```shell 10 | gem install rake 11 | ``` 12 | 13 | ## Install dependencies 14 | 15 | ```shell 16 | bundle install 17 | ``` 18 | 19 | ## Run Tests 20 | 21 | To run all tests: 22 | 23 | ```shell 24 | bundle exec rake test 25 | ``` 26 | 27 | To run individual tests: 28 | 29 | ```shell 30 | bundle exec rake test TEST=spec/honeycomb/trace_spec.rb 31 | ``` 32 | -------------------------------------------------------------------------------- /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 | # Specify your gem's dependencies in honeycomb-beeline.gemspec 8 | gemspec 9 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-Present Honeycomb, Hound Technology, Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /OSSMETADATA: -------------------------------------------------------------------------------- 1 | osslifecycle=beingsunset 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Honeycomb Beeline for Ruby 2 | 3 | [![OSS Lifecycle](https://img.shields.io/osslifecycle/honeycombio/beeline-ruby?color=pink)](https://github.com/honeycombio/home/blob/main/honeycomb-oss-lifecycle-and-practices.md) 4 | [![Build Status](https://circleci.com/gh/honeycombio/beeline-ruby.svg?style=svg)](https://circleci.com/gh/honeycombio/beeline-ruby) 5 | [![Gem Version](https://badge.fury.io/rb/honeycomb-beeline.svg)](https://badge.fury.io/rb/honeycomb-beeline) 6 | 7 | ⚠️**STATUS**: This project is being Sunset. See [this issue](https://github.com/honeycombio/beeline-ruby/issues/244) for more details. 8 | 9 | ⚠️**Note**: Beelines are Honeycomb's legacy instrumentation libraries. We embrace OpenTelemetry as the effective way to instrument applications. For any new observability efforts, we recommend [instrumenting with OpenTelemetry](https://docs.honeycomb.io/getting-data-in/opentelemetry/ruby/). 10 | 11 | This package makes it easy to instrument your Ruby web app to send useful events to [Honeycomb](https://www.honeycomb.io), a service for debugging your software in production. 12 | - [Usage and Examples](https://docs.honeycomb.io/getting-data-in/beelines/ruby-beeline/) 13 | 14 | Sign up for a [Honeycomb 15 | trial](https://ui.honeycomb.io/signup) to obtain an API key before starting. 16 | 17 | ## Compatible with 18 | 19 | Requires Ruby version 2.5 or later 20 | 21 | Built in instrumentation for: 22 | 23 | - Active Support 24 | - AWS (v2 and v3) 25 | - Faraday 26 | - Rack 27 | - Rails (tested on versions 4.1 and up) 28 | - Redis (tested on versions 3.x and 4.x) 29 | - Sequel 30 | - Sinatra 31 | 32 | ## Testing 33 | Find `rspec` test files in the `spec` directory. 34 | 35 | To run tests on gem-specific instrumentations or across various dependency versions, use [appraisal](https://github.com/thoughtbot/appraisal) (further instructions in the readme for that gem). Find gem sets in the `Appraisals` config. 36 | 37 | To run a specific file: `bundle exec appraisal rspec ` 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 | 9 | 10 | 11 | 12 | 13 | 14 | <% @bees.each do |bee| %> 15 | 16 | 17 | 18 | 19 | 20 | 21 | <% end %> 22 | 23 |
Name
<%= bee.name %><%= link_to 'Show', bee %><%= link_to 'Edit', edit_bee_path(bee) %><%= link_to 'Destroy', bee, method: :delete, data: { confirm: 'Are you sure?' } %>
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 | --------------------------------------------------------------------------------