exception_handling 3.1.1 → 3.2.0.pre.dc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8107c2d7f6cf04a39a6f642eb27b327d605eb2fdd2e04762d248ab577b45e02
4
- data.tar.gz: e9cfa08279d74c8a40b05ca29bd3129d621d89447c1d981e6443d23454e51822
3
+ metadata.gz: 5a1419e36b2b184e6e49ad6b943537b1f5efaf54680aef66251093c242e93168
4
+ data.tar.gz: 641de9b2e454c20cb754469fce0f781136baefb14af6913ab25fce3f0263bae4
5
5
  SHA512:
6
- metadata.gz: 2773644ac06df5431f1a7b0c523beebae6168361a807453a1727d3b2d53dacb29fc8a07f9e362dcc4bc5140dae8cbc513bec5a1d3aea3584b4c91c08301f0ce2
7
- data.tar.gz: 05eeae5bd5a88bd9388802b9351d5a3d1810ed1ce97bf2399513947e2c9ae3ca7536ffdd68ea0febc48ecdfa369ddfa44b6c98237aed46fff58bded2f33b8817
6
+ metadata.gz: d3ba9e37ec6c14e508341b604df6ffaeed7c88106ca19305f69a2990f6eefcf03a8ae41a3fb6f7245165e00cb910940ae99842f6d73d9c641d8a9f9c2293a883
7
+ data.tar.gz: a9402fb31a6cce4d2181d9b304376412c7acbf63ffb4e592615da2a2bfd77330c8e6ef1a9bb4c3e1428f6dd9bec32295c5f572860117912cb4f34c6f9e49df27
@@ -0,0 +1,26 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ registries: "*"
6
+ schedule:
7
+ interval: weekly
8
+ day: monday
9
+ time: "08:00"
10
+ timezone: PST8PDT
11
+ open-pull-requests-limit: 10
12
+ versioning-strategy: auto
13
+ commit-message:
14
+ prefix: non-production
15
+ include: scope
16
+ groups:
17
+ minor-patch-versions: # Group together minor / patch updates.
18
+ applies-to: "version-updates"
19
+ update-types:
20
+ - "minor"
21
+ - "patch"
22
+ minor-patch-security:
23
+ applies-to: "security-updates"
24
+ update-types:
25
+ - "minor"
26
+ - "patch"
@@ -0,0 +1,17 @@
1
+ name: Dependabot auto-merge
2
+ on: pull_request
3
+
4
+ permissions:
5
+ contents: write
6
+ pull-requests: write
7
+
8
+ jobs:
9
+ dependabot:
10
+ runs-on: ubuntu-latest
11
+ if: github.actor == 'dependabot[bot]'
12
+ steps:
13
+ - name: Enable auto-merge for Dependabot PRs
14
+ run: gh pr merge --auto --merge "$PR_URL"
15
+ env:
16
+ PR_URL: ${{github.event.pull_request.html_url}}
17
+ GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
@@ -1,36 +1,9 @@
1
- ---
2
1
  name: Pipeline
3
- on: [push]
2
+ on:
3
+ push:
4
+ schedule:
5
+ - cron: '0 1 * * 1' # Every Monday at 1AM UTC
4
6
  jobs:
5
7
  tests:
6
- name: Unit Tests
7
- runs-on: ubuntu-latest
8
- strategy:
9
- fail-fast: false
10
- matrix:
11
- ruby: [2.7, '3.0', 3.1, 3.2, 3.3]
12
- gemfile:
13
- - Gemfile
14
- - gemfiles/rails_5.gemfile
15
- - gemfiles/rails_6.gemfile
16
- - gemfiles/rails_7.gemfile
17
- exclude:
18
- - gemfile: gemfiles/rails_5.gemfile
19
- ruby: '3.0'
20
- - gemfile: gemfiles/rails_5.gemfile
21
- ruby: 3.1
22
- - gemfile: gemfiles/rails_5.gemfile
23
- ruby: 3.2
24
- - gemfile: gemfiles/rails_5.gemfile
25
- ruby: 3.3
26
- env:
27
- BUNDLE_GEMFILE: ${{ matrix.gemfile }}
28
- steps:
29
- - uses: actions/checkout@v2
30
- - uses: ruby/setup-ruby@v1
31
- with:
32
- ruby-version: ${{ matrix.ruby }}
33
- bundler: 2.3.26
34
- bundler-cache: true
35
- - name: Unit tests
36
- run: bundle exec rspec
8
+ uses: Invoca/ruby-test-matrix-workflow/.github/workflows/ruby-test-matrix.yml@main
9
+ secrets: inherit
data/.gitignore CHANGED
@@ -4,3 +4,4 @@
4
4
  spec/reports/*
5
5
  gemfiles/*.lock
6
6
  pkg/
7
+ coverage/
data/Appraisals CHANGED
@@ -1,13 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- appraise "rails-5" do
4
- gem 'activesupport', '~> 5.2'
5
- end
6
-
7
- appraise "rails-6" do
8
- gem 'activesupport', '~> 6.0'
9
- end
10
-
11
- appraise "rails-7" do
12
- gem 'activesupport', '~> 7.0'
13
- end
3
+ require "appraisal/matrix"
4
+ appraisal_matrix(rails: "7.0")
data/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4
4
 
5
5
  **Note:** this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [3.2.0] - 2026-08-04
8
+ ### Added
9
+ - Optional Sentry notifications from `send_external_notifications` when the `Sentry` constant is defined
10
+ - `send_to_sentry` exception filter flag (default `false`); filters with `send_to_honeybadger: true` also send to Sentry to ease migration
11
+
12
+ ## [3.1.2] - 2024-05-08
13
+ ### Fixed
14
+ - Fixed deprecation warnings that were coming from usage of `silence` when using this gem with Rails 7.1
15
+
7
16
  ## [3.1.1] - 2024-03-19
8
17
  ### Changed
9
18
  - Changed how the `honeybadger_tag_from_log_context` to now combine the tag name with the value using a colon `:` instead of two hyphens `--`.
data/Gemfile CHANGED
@@ -5,7 +5,8 @@ source 'https://rubygems.org'
5
5
  gemspec
6
6
 
7
7
  gem 'activesupport'
8
- gem 'appraisal', '~> 2.2'
8
+ gem 'appraisal'
9
+ gem 'appraisal-matrix'
9
10
  gem 'gem-release'
10
11
  gem 'honeybadger', '~> 4.11'
11
12
  gem 'pry'
@@ -14,4 +15,13 @@ gem 'rake'
14
15
  gem 'rspec'
15
16
  gem 'rspec_junit_formatter'
16
17
  gem 'rubocop'
18
+ gem 'simplecov', '~> 0.22'
19
+ gem 'simplecov-lcov', '~> 0.8'
17
20
  gem 'test-unit'
21
+
22
+
23
+ gem "concurrent-ruby", "~> 1.3", "< 1.3.5"
24
+
25
+ gem "base64", ">= 0.2.0"
26
+ gem "bigdecimal", ">= 3.1"
27
+ gem "mutex_m", ">= 0.2.0"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- exception_handling (3.1.1)
4
+ exception_handling (3.2.0.pre.dc.0)
5
5
  activesupport (>= 5.2)
6
6
  contextual_logger (~> 1.0)
7
7
  escalate (~> 0.3)
@@ -11,102 +11,133 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- activesupport (6.0.4.7)
14
+ activesupport (7.1.3.2)
15
+ base64
16
+ bigdecimal
15
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
16
- i18n (>= 0.7, < 2)
17
- minitest (~> 5.1)
18
- tzinfo (~> 1.1)
19
- zeitwerk (~> 2.2, >= 2.2.2)
18
+ connection_pool (>= 2.2.5)
19
+ drb
20
+ i18n (>= 1.6, < 2)
21
+ minitest (>= 5.1)
22
+ mutex_m
23
+ tzinfo (~> 2.0)
20
24
  appraisal (2.5.0)
21
25
  bundler
22
26
  rake
23
27
  thor (>= 0.14.0)
24
- ast (2.4.0)
28
+ appraisal-matrix (0.3.0)
29
+ appraisal (~> 2.2)
30
+ ast (2.4.2)
31
+ base64 (0.2.0)
32
+ bigdecimal (3.1.8)
25
33
  byebug (11.1.3)
26
34
  coderay (1.1.3)
27
- concurrent-ruby (1.1.10)
28
- contextual_logger (1.3.0)
29
- activesupport (< 7.1)
35
+ concurrent-ruby (1.3.4)
36
+ connection_pool (2.4.1)
37
+ contextual_logger (1.5.0)
38
+ activesupport (>= 6.0)
30
39
  json
31
- diff-lcs (1.5.0)
40
+ date (3.5.1)
41
+ diff-lcs (1.5.1)
42
+ docile (1.4.1)
43
+ drb (2.2.1)
32
44
  escalate (0.3.0)
33
45
  gem-release (2.2.2)
34
- honeybadger (4.11.0)
35
- i18n (1.10.0)
46
+ honeybadger (4.12.2)
47
+ i18n (1.14.5)
36
48
  concurrent-ruby (~> 1.0)
37
- invoca-utils (0.5.1)
38
- activesupport (>= 5.0)
39
- json (2.7.1)
40
- method_source (1.0.0)
41
- minitest (5.15.0)
42
- parallel (1.17.0)
43
- parser (2.7.1.3)
44
- ast (~> 2.4.0)
49
+ invoca-utils (0.7.0)
50
+ activesupport (>= 6.0)
51
+ json (2.7.2)
52
+ language_server-protocol (3.17.0.3)
53
+ method_source (1.1.0)
54
+ minitest (5.22.3)
55
+ mutex_m (0.2.0)
56
+ parallel (1.24.0)
57
+ parser (3.3.1.0)
58
+ ast (~> 2.4.1)
59
+ racc
45
60
  power_assert (2.0.3)
46
61
  pry (0.14.2)
47
62
  coderay (~> 1.1)
48
63
  method_source (~> 1.0)
49
- pry-byebug (3.8.0)
64
+ pry-byebug (3.10.1)
50
65
  byebug (~> 11.0)
51
- pry (~> 0.10)
52
- psych (5.1.2)
66
+ pry (>= 0.13, < 0.15)
67
+ psych (5.4.0)
68
+ date
53
69
  stringio
54
- rainbow (3.0.0)
55
- rake (13.0.1)
70
+ racc (1.7.3)
71
+ rainbow (3.1.1)
72
+ rake (13.2.1)
56
73
  regexp_parser (2.9.0)
57
74
  rexml (3.2.6)
58
- rspec (3.9.0)
59
- rspec-core (~> 3.9.0)
60
- rspec-expectations (~> 3.9.0)
61
- rspec-mocks (~> 3.9.0)
62
- rspec-core (3.9.3)
63
- rspec-support (~> 3.9.3)
64
- rspec-expectations (3.9.2)
75
+ rspec (3.13.0)
76
+ rspec-core (~> 3.13.0)
77
+ rspec-expectations (~> 3.13.0)
78
+ rspec-mocks (~> 3.13.0)
79
+ rspec-core (3.13.0)
80
+ rspec-support (~> 3.13.0)
81
+ rspec-expectations (3.13.0)
65
82
  diff-lcs (>= 1.2.0, < 2.0)
66
- rspec-support (~> 3.9.0)
67
- rspec-mocks (3.9.1)
83
+ rspec-support (~> 3.13.0)
84
+ rspec-mocks (3.13.0)
68
85
  diff-lcs (>= 1.2.0, < 2.0)
69
- rspec-support (~> 3.9.0)
70
- rspec-support (3.9.4)
86
+ rspec-support (~> 3.13.0)
87
+ rspec-support (3.13.1)
71
88
  rspec_junit_formatter (0.6.0)
72
89
  rspec-core (>= 2, < 4, != 2.12.0)
73
- rubocop (0.89.0)
90
+ rubocop (1.63.4)
91
+ json (~> 2.3)
92
+ language_server-protocol (>= 3.17.0)
74
93
  parallel (~> 1.10)
75
- parser (>= 2.7.1.1)
94
+ parser (>= 3.3.0.2)
76
95
  rainbow (>= 2.2.2, < 4.0)
77
- regexp_parser (>= 1.7)
78
- rexml
79
- rubocop-ast (>= 0.1.0, < 1.0)
96
+ regexp_parser (>= 1.8, < 3.0)
97
+ rexml (>= 3.2.5, < 4.0)
98
+ rubocop-ast (>= 1.31.1, < 2.0)
80
99
  ruby-progressbar (~> 1.7)
81
- unicode-display_width (>= 1.4.0, < 2.0)
82
- rubocop-ast (0.2.0)
83
- parser (>= 2.7.0.1)
100
+ unicode-display_width (>= 2.4.0, < 3.0)
101
+ rubocop-ast (1.31.3)
102
+ parser (>= 3.3.1.0)
84
103
  ruby-progressbar (1.13.0)
85
- stringio (3.1.0)
86
- test-unit (3.6.1)
104
+ simplecov (0.22.0)
105
+ docile (~> 1.1)
106
+ simplecov-html (~> 0.11)
107
+ simplecov_json_formatter (~> 0.1)
108
+ simplecov-html (0.13.2)
109
+ simplecov-lcov (0.9.0)
110
+ simplecov_json_formatter (0.1.4)
111
+ stringio (3.2.0)
112
+ test-unit (3.6.2)
87
113
  power_assert
88
- thor (1.0.1)
89
- thread_safe (0.3.6)
90
- tzinfo (1.2.9)
91
- thread_safe (~> 0.1)
92
- unicode-display_width (1.6.0)
93
- zeitwerk (2.5.4)
114
+ thor (1.3.1)
115
+ tzinfo (2.0.6)
116
+ concurrent-ruby (~> 1.0)
117
+ unicode-display_width (2.5.0)
94
118
 
95
119
  PLATFORMS
96
120
  ruby
97
121
 
98
122
  DEPENDENCIES
99
123
  activesupport
100
- appraisal (~> 2.2)
124
+ appraisal
125
+ appraisal-matrix
126
+ base64 (>= 0.2.0)
127
+ bigdecimal (>= 3.1)
128
+ concurrent-ruby (~> 1.3, < 1.3.5)
101
129
  exception_handling!
102
130
  gem-release
103
131
  honeybadger (~> 4.11)
132
+ mutex_m (>= 0.2.0)
104
133
  pry
105
134
  pry-byebug
106
135
  rake
107
136
  rspec
108
137
  rspec_junit_formatter
109
138
  rubocop
139
+ simplecov (~> 0.22)
140
+ simplecov-lcov (~> 0.8)
110
141
  test-unit
111
142
 
112
143
  BUNDLED WITH
data/README.md CHANGED
@@ -39,6 +39,22 @@ ExceptionHandling.honeybadger_auto_tagger = ->(exception) { [] } # See "Automati
39
39
  ExceptionHandling.add_honeybadger_tag_from_log_context("tag-name", path: ["path", "in", "log", "context"])
40
40
  ```
41
41
 
42
+ ### External error trackers (Honeybadger / Sentry)
43
+
44
+ ExceptionHandling notifies configured external services from `log_error` (and related paths):
45
+
46
+ - **Honeybadger** — when the `Honeybadger` constant is defined
47
+ - **Sentry** — when the `Sentry` constant is defined (for example after initializing the Sentry Ruby SDK in your app)
48
+
49
+ Both can run at the same time. This gem does not depend on `sentry-ruby`; initialize Sentry in the host application.
50
+
51
+ Matched exception filters in `exception_filters.yml` control delivery:
52
+
53
+ - `send_to_honeybadger: true` — send to Honeybadger (and also to Sentry during migration)
54
+ - `send_to_sentry: true` — send to Sentry
55
+ - Unmatched exceptions are sent when the corresponding service is defined
56
+ - Matched filters with both flags false skip both services
57
+
42
58
  ## Usage
43
59
 
44
60
  Mixin the `ExceptionHandling::Methods` module into your controllers, models or classes. The example below adds it to all the controllers in you rails application:
data/catalog-info.yaml ADDED
@@ -0,0 +1,39 @@
1
+ # This file is partially auto-generated by the invoca-backstage-tools gem
2
+ # The following fields should not be edited manually as they are auto-generated
3
+ # based on the contents of the repo:
4
+ # - metadata.name
5
+ # - metadata.title
6
+ # - metadata.description
7
+ # - annotations.github.com/project-slug
8
+ # - invoca.com/version-repository-location
9
+ # - invoca.com/version-repository-name
10
+ # - spec.type
11
+ # - spec.owner
12
+ ---
13
+ apiVersion: backstage.io/v1alpha1
14
+ kind: Component
15
+ metadata:
16
+ name: exception_handling-gem
17
+ title: ExceptionHandling
18
+ annotations:
19
+ buildkite.com/project-slug: Invoca/exception_handling
20
+ github.com/project-slug: Invoca/exception_handling
21
+ invoca.com/version-repository-location: rubygems
22
+ invoca.com/version-repository-name: exception_handling
23
+ endoflife.date/products: ruby@3.3
24
+ tags:
25
+ - ruby
26
+ - open-source
27
+ - gem
28
+ - public
29
+ - rails-dependent
30
+ description: Exception handling logger
31
+ spec:
32
+ type: library
33
+ lifecycle: production
34
+ owner: octothorpe
35
+ system: platform-observability
36
+ dependsOn:
37
+ - component:escalate-gem
38
+ - component:invoca-utils-gem
39
+ - component:contextual_logger-gem
@@ -0,0 +1,25 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport"
6
+ gem "appraisal"
7
+ gem "appraisal-matrix"
8
+ gem "gem-release"
9
+ gem "honeybadger", "~> 4.11"
10
+ gem "pry"
11
+ gem "pry-byebug"
12
+ gem "rake"
13
+ gem "rspec"
14
+ gem "rspec_junit_formatter"
15
+ gem "rubocop"
16
+ gem "test-unit"
17
+ gem "concurrent-ruby", "~> 1.3", "< 1.3.5"
18
+ gem "base64", ">= 0.2.0"
19
+ gem "bigdecimal", ">= 3.1"
20
+ gem "mutex_m", ">= 0.2.0"
21
+ gem "rails", "~> 7.0.0"
22
+ gem "simplecov", "~> 0.22"
23
+ gem "simplecov-lcov", "~> 0.8"
24
+
25
+ gemspec path: "../"
@@ -0,0 +1,25 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport"
6
+ gem "appraisal"
7
+ gem "appraisal-matrix"
8
+ gem "gem-release"
9
+ gem "honeybadger", "~> 4.11"
10
+ gem "pry"
11
+ gem "pry-byebug"
12
+ gem "rake"
13
+ gem "rspec"
14
+ gem "rspec_junit_formatter"
15
+ gem "rubocop"
16
+ gem "test-unit"
17
+ gem "concurrent-ruby", "~> 1.3", "< 1.3.5"
18
+ gem "base64", ">= 0.2.0"
19
+ gem "bigdecimal", ">= 3.1"
20
+ gem "mutex_m", ">= 0.2.0"
21
+ gem "rails", "~> 7.1.0"
22
+ gem "simplecov", "~> 0.22"
23
+ gem "simplecov-lcov", "~> 0.8"
24
+
25
+ gemspec path: "../"
@@ -0,0 +1,25 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport"
6
+ gem "appraisal"
7
+ gem "appraisal-matrix"
8
+ gem "gem-release"
9
+ gem "honeybadger", "~> 4.11"
10
+ gem "pry"
11
+ gem "pry-byebug"
12
+ gem "rake"
13
+ gem "rspec"
14
+ gem "rspec_junit_formatter"
15
+ gem "rubocop"
16
+ gem "test-unit"
17
+ gem "concurrent-ruby", "~> 1.3", "< 1.3.5"
18
+ gem "base64", ">= 0.2.0"
19
+ gem "bigdecimal", ">= 3.1"
20
+ gem "mutex_m", ">= 0.2.0"
21
+ gem "rails", "~> 7.2.0"
22
+ gem "simplecov", "~> 0.22"
23
+ gem "simplecov-lcov", "~> 0.8"
24
+
25
+ gemspec path: "../"
@@ -0,0 +1,25 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport"
6
+ gem "appraisal"
7
+ gem "appraisal-matrix"
8
+ gem "gem-release"
9
+ gem "honeybadger", "~> 4.11"
10
+ gem "pry"
11
+ gem "pry-byebug"
12
+ gem "rake"
13
+ gem "rspec"
14
+ gem "rspec_junit_formatter"
15
+ gem "rubocop"
16
+ gem "test-unit"
17
+ gem "concurrent-ruby", "~> 1.3", "< 1.3.5"
18
+ gem "base64", ">= 0.2.0"
19
+ gem "bigdecimal", ">= 3.1"
20
+ gem "mutex_m", ">= 0.2.0"
21
+ gem "rails", "~> 8.0.0"
22
+ gem "simplecov", "~> 0.22"
23
+ gem "simplecov-lcov", "~> 0.8"
24
+
25
+ gemspec path: "../"
@@ -6,12 +6,13 @@ module ExceptionHandling
6
6
 
7
7
  CONFIGURATION_SECTIONS = {
8
8
  send_to_honeybadger: false, # should be sent to honeybadger?
9
+ send_to_sentry: false, # should be sent to sentry?
9
10
  send_metric: true, # should the metric be sent.
10
11
  metric_name: nil, # Will be derived from section name if not passed
11
12
  notes: nil # Will be included in exception email if set, used to keep notes and relevant links
12
13
  }.freeze
13
14
 
14
- attr_reader :filter_name, :send_to_honeybadger, :send_metric, :metric_name, :notes
15
+ attr_reader :filter_name, :send_to_honeybadger, :send_to_sentry, :send_metric, :metric_name, :notes
15
16
 
16
17
  def initialize(filter_name, configuration)
17
18
  @filter_name = filter_name
@@ -21,6 +22,7 @@ module ExceptionHandling
21
22
 
22
23
  @configuration = CONFIGURATION_SECTIONS.merge(configuration)
23
24
  @send_to_honeybadger = @configuration[:send_to_honeybadger]
25
+ @send_to_sentry = @configuration[:send_to_sentry]
24
26
  @send_metric = @configuration[:send_metric]
25
27
  @metric_name = (@configuration[:metric_name] || @filter_name).to_s.gsub(" ", "_")
26
28
  @notes = @configuration[:notes]
@@ -78,6 +78,14 @@ module ExceptionHandling
78
78
  ExceptionHandling.honeybadger_defined? && (!exception_description || exception_description.send_to_honeybadger)
79
79
  end
80
80
 
81
+ def send_to_sentry?
82
+ ExceptionHandling.sentry_defined? && (
83
+ !exception_description ||
84
+ exception_description.send_to_sentry ||
85
+ exception_description.send_to_honeybadger
86
+ )
87
+ end
88
+
81
89
  def honeybadger_context_data
82
90
  @honeybadger_context_data ||= enhanced_data_to_honeybadger_context
83
91
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ExceptionHandling
4
- VERSION = '3.1.1'
4
+ VERSION = '3.2.0.pre.dc.0'
5
5
  end
@@ -192,7 +192,7 @@ module ExceptionHandling # never included
192
192
  # Write an exception out to the log file using our own custom format.
193
193
  #
194
194
  def write_exception_to_log(ex, exception_context, timestamp, log_context = {})
195
- ActiveSupport::Deprecation.silence do
195
+ with_deprecations_silenced do
196
196
  log_message = "#{exception_context}\n#{ex.class}: (#{encode_utf8(ex.message.to_s)}):\n " + clean_backtrace(ex).join("\n ") + "\n\n"
197
197
 
198
198
  if ex.is_a?(Warning)
@@ -211,6 +211,9 @@ module ExceptionHandling # never included
211
211
  if honeybadger_defined?
212
212
  results[:honeybadger_status] = send_exception_to_honeybadger_unless_filtered(exception_info)
213
213
  end
214
+ if sentry_defined?
215
+ results[:sentry_status] = send_exception_to_sentry_unless_filtered(exception_info)
216
+ end
214
217
  results
215
218
  end
216
219
 
@@ -249,6 +252,31 @@ module ExceptionHandling # never included
249
252
  :failure
250
253
  end
251
254
 
255
+ # Returns :success or :failure or :skipped
256
+ def send_exception_to_sentry_unless_filtered(exception_info)
257
+ if exception_info.send_to_sentry?
258
+ send_exception_to_sentry(exception_info)
259
+ else
260
+ log_info("Filtered exception using '#{exception_info.exception_description.filter_name}'; not sending notification to Sentry")
261
+ :skipped
262
+ end
263
+ end
264
+
265
+ #
266
+ # Log exception to Sentry.
267
+ #
268
+ # Returns :success or :failure
269
+ #
270
+ def send_exception_to_sentry(exception_info)
271
+ exception = exception_info.exception
272
+ response = Sentry.capture_exception(exception)
273
+ response ? :success : :failure
274
+ rescue Exception => ex
275
+ warn("ExceptionHandling.send_exception_to_sentry rescued exception while logging #{exception_info.exception_context}:\n#{exception.class}: #{exception.message}:\n#{ex.class}: #{ex.message}\n#{ex.backtrace.join("\n")}")
276
+ write_exception_to_log(ex, "ExceptionHandling.send_exception_to_sentry rescued exception while logging #{exception_info.exception_context}:\n#{exception.class}: #{exception.message}", exception_info.timestamp)
277
+ :failure
278
+ end
279
+
252
280
  #
253
281
  # Check if Honeybadger defined.
254
282
  #
@@ -256,6 +284,13 @@ module ExceptionHandling # never included
256
284
  Object.const_defined?("Honeybadger")
257
285
  end
258
286
 
287
+ #
288
+ # Check if Sentry defined.
289
+ #
290
+ def sentry_defined?
291
+ Object.const_defined?("Sentry")
292
+ end
293
+
259
294
  #
260
295
  # Expects passed in hash to only include keys which be directly set on the Honeybadger config
261
296
  #
@@ -352,6 +387,14 @@ module ExceptionHandling # never included
352
387
 
353
388
  private
354
389
 
390
+ def with_deprecations_silenced(&block)
391
+ if ActiveSupport.version >= Gem::Version.new('7.1.0')
392
+ ActiveSupport.deprecator.silence(&block)
393
+ else
394
+ ActiveSupport::Deprecation.silence(&block)
395
+ end
396
+ end
397
+
355
398
  # @param exception_info [ExceptionInfo]
356
399
  #
357
400
  # @return [Array<String>]
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ unless ENV["NO_COVERAGE"] == "true"
4
+ require "simplecov"
5
+
6
+ SimpleCov.start do
7
+ add_filter "/spec/"
8
+ track_files "lib/**/*.rb"
9
+
10
+ if ENV["GITHUB_ACTIONS"]
11
+ require "simplecov-lcov"
12
+
13
+ SimpleCov::Formatter::LcovFormatter.config do |config|
14
+ config.report_with_single_file = true
15
+ config.single_report_path = "coverage/lcov.info"
16
+ end
17
+
18
+ formatter SimpleCov::Formatter::LcovFormatter
19
+ else
20
+ formatter SimpleCov::Formatter::HTMLFormatter
21
+ end
22
+ end
23
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "simplecov_helper"
4
+
3
5
  require 'rspec'
4
6
  require 'rspec/mocks'
5
7
  require 'rspec_junit_formatter'
@@ -35,6 +35,12 @@ module ExceptionHandling
35
35
  expect(!ExceptionDescription.new(:filter1, error: "my error message").send_to_honeybadger).to be_truthy
36
36
  end
37
37
 
38
+ it "allow send_to_sentry to be specified and have it disabled by default" do
39
+ expect(ExceptionDescription.new(:filter1, error: "my error message", send_to_sentry: false).send_to_sentry).to eq(false)
40
+ expect(ExceptionDescription.new(:filter1, error: "my error message", send_to_sentry: true).send_to_sentry).to eq(true)
41
+ expect(ExceptionDescription.new(:filter1, error: "my error message").send_to_sentry).to eq(false)
42
+ end
43
+
38
44
  it "allow send_metric to be configured" do
39
45
  expect(!ExceptionDescription.new(:filter1, error: "my error message", send_metric: false).send_metric).to be_truthy
40
46
  expect(ExceptionDescription.new(:filter1, error: "my error message").send_metric).to be_truthy
@@ -452,6 +452,48 @@ module ExceptionHandling
452
452
  end
453
453
  end
454
454
 
455
+ context "send_to_sentry?" do
456
+ it "be enabled when Sentry is defined and exception is not in the filter list" do
457
+ allow(ExceptionHandling).to receive(:sentry_defined?).and_return(true)
458
+ exception_info = ExceptionInfo.new(StandardError.new("something went wrong"), nil, Time.now)
459
+ expect(exception_info.exception_description).to be_nil
460
+ expect(exception_info.send_to_sentry?).to eq(true)
461
+ end
462
+
463
+ it "be enabled when Sentry is defined and filter has send_to_sentry true" do
464
+ allow(ExceptionHandling).to receive(:sentry_defined?).and_return(true)
465
+ exception = StandardError.new("No route matches")
466
+ exception_info = ExceptionInfo.new(exception, nil, Time.now)
467
+ allow(exception_info.exception_description).to receive(:send_to_sentry).and_return(true)
468
+ allow(exception_info.exception_description).to receive(:send_to_honeybadger).and_return(false)
469
+ expect(exception_info.send_to_sentry?).to eq(true)
470
+ end
471
+
472
+ it "be enabled when Sentry is defined and filter has send_to_honeybadger true only" do
473
+ allow(ExceptionHandling).to receive(:sentry_defined?).and_return(true)
474
+ exception = StandardError.new("No route matches")
475
+ exception_info = ExceptionInfo.new(exception, nil, Time.now)
476
+ allow(exception_info.exception_description).to receive(:send_to_sentry).and_return(false)
477
+ allow(exception_info.exception_description).to receive(:send_to_honeybadger).and_return(true)
478
+ expect(exception_info.send_to_sentry?).to eq(true)
479
+ end
480
+
481
+ it "be disabled when Sentry is defined and both filter flags are false" do
482
+ allow(ExceptionHandling).to receive(:sentry_defined?).and_return(true)
483
+ exception = StandardError.new("No route matches")
484
+ exception_info = ExceptionInfo.new(exception, nil, Time.now)
485
+ allow(exception_info.exception_description).to receive(:send_to_sentry).and_return(false)
486
+ allow(exception_info.exception_description).to receive(:send_to_honeybadger).and_return(false)
487
+ expect(exception_info.send_to_sentry?).to eq(false)
488
+ end
489
+
490
+ it "be disabled when Sentry is not defined" do
491
+ allow(ExceptionHandling).to receive(:sentry_defined?).and_return(false)
492
+ exception_info = ExceptionInfo.new(StandardError.new("something went wrong"), nil, Time.now)
493
+ expect(exception_info.send_to_sentry?).to eq(false)
494
+ end
495
+ end
496
+
455
497
  context "honeybadger_context_data" do
456
498
  before do
457
499
  allow(ExceptionHandling.logger).to receive(:current_context_for_thread).and_return({ cuid: 'ABCD' })
@@ -690,6 +690,139 @@ describe ExceptionHandling do
690
690
  end
691
691
  end
692
692
 
693
+ context "Sentry integration" do
694
+ context "with Sentry not defined" do
695
+ before do
696
+ allow(ExceptionHandling).to receive(:sentry_defined?).and_return(false)
697
+ end
698
+
699
+ it "not invoke send_exception_to_sentry when log_error is executed" do
700
+ expect(ExceptionHandling).to_not receive(:send_exception_to_sentry)
701
+ ExceptionHandling.log_error(exception_1)
702
+ end
703
+
704
+ it "not invoke send_exception_to_sentry when ensure_safe is executed" do
705
+ expect(ExceptionHandling).to_not receive(:send_exception_to_sentry)
706
+ ExceptionHandling.ensure_safe { raise exception_1 }
707
+ end
708
+ end
709
+
710
+ context "with Sentry defined" do
711
+ let(:sentry_module) do
712
+ Module.new do
713
+ def self.capture_exception(_exception); end
714
+ end
715
+ end
716
+
717
+ before do
718
+ stub_const("Sentry", sentry_module)
719
+ allow(Sentry).to receive(:capture_exception).and_return(Object.new)
720
+ end
721
+
722
+ it "not send_exception_to_sentry when log_warning is executed" do
723
+ expect(ExceptionHandling).to_not receive(:send_exception_to_sentry)
724
+ ExceptionHandling.log_warning("This should not go to sentry")
725
+ end
726
+
727
+ it "not send_exception_to_sentry when log_error is called with a Warning" do
728
+ expect(ExceptionHandling).to_not receive(:send_exception_to_sentry)
729
+ ExceptionHandling.log_error(ExceptionHandling::Warning.new("This should not go to sentry"))
730
+ end
731
+
732
+ it "invoke send_exception_to_sentry when log_error is executed" do
733
+ expect(ExceptionHandling).to receive(:send_exception_to_sentry).with(any_args).and_call_original
734
+ ExceptionHandling.log_error(exception_1)
735
+ end
736
+
737
+ it "invoke send_exception_to_sentry when log_error_rack is executed" do
738
+ expect(ExceptionHandling).to receive(:send_exception_to_sentry).with(any_args).and_call_original
739
+ ExceptionHandling.log_error_rack(exception_1, {}, nil)
740
+ end
741
+
742
+ it "invoke send_exception_to_sentry when ensure_safe is executed" do
743
+ expect(ExceptionHandling).to receive(:send_exception_to_sentry).with(any_args).and_call_original
744
+ ExceptionHandling.ensure_safe { raise exception_1 }
745
+ end
746
+
747
+ it "call Sentry.capture_exception with the exception" do
748
+ expect(Sentry).to receive(:capture_exception).with(exception_1)
749
+ ExceptionHandling.log_error(exception_1)
750
+ end
751
+
752
+ it "also notify Honeybadger when both are defined" do
753
+ expect(Honeybadger).to receive(:notify).with(any_args).and_return("hb-id")
754
+ expect(Sentry).to receive(:capture_exception).with(exception_1).and_return(Object.new)
755
+ ExceptionHandling.log_error(exception_1)
756
+ end
757
+
758
+ it "not send to Sentry when filter has both flags false" do
759
+ filter_list = {
760
+ NoSentry: {
761
+ error: "suppress Sentry notification",
762
+ send_to_honeybadger: false,
763
+ send_to_sentry: false
764
+ }
765
+ }
766
+ allow(File).to receive(:mtime) { incrementing_mtime }
767
+ expect(YAML).to receive(:load_file).with(any_args) { ActiveSupport::HashWithIndifferentAccess.new(filter_list) }.at_least(1)
768
+
769
+ expect(ExceptionHandling).to receive(:send_exception_to_sentry_unless_filtered).with(any_args).exactly(1).and_call_original
770
+ expect(Sentry).to_not receive(:capture_exception)
771
+ ExceptionHandling.log_error(StandardError.new("suppress Sentry notification"))
772
+ end
773
+
774
+ it "send to Sentry when filter has send_to_honeybadger true only" do
775
+ filter_list = {
776
+ HoneybadgerOnly: {
777
+ error: "honeybadger only filter",
778
+ send_to_honeybadger: true,
779
+ send_to_sentry: false
780
+ }
781
+ }
782
+ allow(File).to receive(:mtime) { incrementing_mtime }
783
+ expect(YAML).to receive(:load_file).with(any_args) { ActiveSupport::HashWithIndifferentAccess.new(filter_list) }.at_least(1)
784
+
785
+ expect(Sentry).to receive(:capture_exception).with(instance_of(StandardError))
786
+ ExceptionHandling.log_error(StandardError.new("honeybadger only filter"))
787
+ end
788
+
789
+ it "send to Sentry when filter has send_to_sentry true only" do
790
+ filter_list = {
791
+ SentryOnly: {
792
+ error: "sentry only filter",
793
+ send_to_honeybadger: false,
794
+ send_to_sentry: true
795
+ }
796
+ }
797
+ allow(File).to receive(:mtime) { incrementing_mtime }
798
+ expect(YAML).to receive(:load_file).with(any_args) { ActiveSupport::HashWithIndifferentAccess.new(filter_list) }.at_least(1)
799
+
800
+ expect(Sentry).to receive(:capture_exception).with(instance_of(StandardError))
801
+ ExceptionHandling.log_error(StandardError.new("sentry only filter"))
802
+ end
803
+
804
+ it "still complete log_error and notify Honeybadger if Sentry.capture_exception raises" do
805
+ expect(Honeybadger).to receive(:notify).with(any_args).and_return("hb-id")
806
+ expect(Sentry).to receive(:capture_exception).and_raise(StandardError, "Sentry Notification Failure")
807
+ expect { ExceptionHandling.log_error(exception_1) }.not_to raise_error
808
+ end
809
+
810
+ it "return :failure when Sentry.capture_exception returns nil" do
811
+ expect(Sentry).to receive(:capture_exception).and_return(nil)
812
+ expect(ExceptionHandling.send_exception_to_sentry(
813
+ ExceptionHandling::ExceptionInfo.new(exception_1, "", Time.now)
814
+ )).to eq(:failure)
815
+ end
816
+
817
+ it "return :success when Sentry.capture_exception returns an event" do
818
+ expect(Sentry).to receive(:capture_exception).and_return(Object.new)
819
+ expect(ExceptionHandling.send_exception_to_sentry(
820
+ ExceptionHandling::ExceptionInfo.new(exception_1, "", Time.now)
821
+ )).to eq(:success)
822
+ end
823
+ end
824
+ end
825
+
693
826
  class EventResponse
694
827
  def to_s
695
828
  "message from to_s!"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exception_handling
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.2.0.pre.dc.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Invoca
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-19 00:00:00.000000000 Z
11
+ date: 2026-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -88,6 +88,8 @@ extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
90
  - ".github/CODEOWNERS"
91
+ - ".github/dependabot.yml"
92
+ - ".github/workflows/dependabot_automerge.yml"
91
93
  - ".github/workflows/pipeline.yml"
92
94
  - ".gitignore"
93
95
  - ".rspec"
@@ -101,11 +103,13 @@ files:
101
103
  - LICENSE
102
104
  - README.md
103
105
  - Rakefile
106
+ - catalog-info.yaml
104
107
  - config/exception_filters.yml
105
108
  - exception_handling.gemspec
106
- - gemfiles/rails_5.gemfile
107
- - gemfiles/rails_6.gemfile
108
- - gemfiles/rails_7.gemfile
109
+ - gemfiles/rails_7_0.gemfile
110
+ - gemfiles/rails_7_1.gemfile
111
+ - gemfiles/rails_7_2.gemfile
112
+ - gemfiles/rails_8_0.gemfile
109
113
  - lib/exception_handling.rb
110
114
  - lib/exception_handling/escalate_callback.rb
111
115
  - lib/exception_handling/exception_catalog.rb
@@ -118,6 +122,7 @@ files:
118
122
  - spec/helpers/controller_helpers.rb
119
123
  - spec/helpers/exception_helpers.rb
120
124
  - spec/rake_test_warning_false.rb
125
+ - spec/simplecov_helper.rb
121
126
  - spec/spec_helper.rb
122
127
  - spec/unit/exception_handling/escalate_callback_spec.rb
123
128
  - spec/unit/exception_handling/exception_catalog_spec.rb
@@ -146,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
151
  - !ruby/object:Gem::Version
147
152
  version: '0'
148
153
  requirements: []
149
- rubygems_version: 3.3.7
154
+ rubygems_version: 3.5.10
150
155
  signing_key:
151
156
  specification_version: 4
152
157
  summary: Invoca's exception handling logger layer, based on exception_notifier.
@@ -154,6 +159,7 @@ test_files:
154
159
  - spec/helpers/controller_helpers.rb
155
160
  - spec/helpers/exception_helpers.rb
156
161
  - spec/rake_test_warning_false.rb
162
+ - spec/simplecov_helper.rb
157
163
  - spec/spec_helper.rb
158
164
  - spec/unit/exception_handling/escalate_callback_spec.rb
159
165
  - spec/unit/exception_handling/exception_catalog_spec.rb
@@ -1,16 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "activesupport", "~> 5.2"
6
- gem "appraisal", "~> 2.2"
7
- gem "honeybadger", "~> 4.11"
8
- gem "pry"
9
- gem "pry-byebug"
10
- gem "rake"
11
- gem "rspec"
12
- gem "rspec_junit_formatter"
13
- gem "rubocop"
14
- gem "test-unit"
15
-
16
- gemspec path: "../"
@@ -1,16 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "activesupport", "~> 6.0"
6
- gem "appraisal", "~> 2.2"
7
- gem "honeybadger", "~> 4.11"
8
- gem "pry"
9
- gem "pry-byebug"
10
- gem "rake"
11
- gem "rspec"
12
- gem "rspec_junit_formatter"
13
- gem "rubocop"
14
- gem "test-unit"
15
-
16
- gemspec path: "../"
@@ -1,16 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "activesupport", "~> 7.0"
6
- gem "appraisal", "~> 2.2"
7
- gem "honeybadger", "~> 4.11"
8
- gem "pry"
9
- gem "pry-byebug"
10
- gem "rake"
11
- gem "rspec"
12
- gem "rspec_junit_formatter"
13
- gem "rubocop"
14
- gem "test-unit"
15
-
16
- gemspec path: "../"