bugwatch-ruby 0.1.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 +7 -0
- data/README.md +96 -0
- data/lib/bugwatch/backtrace_cleaner.rb +63 -0
- data/lib/bugwatch/breadcrumb_collector.rb +25 -0
- data/lib/bugwatch/configuration.rb +32 -0
- data/lib/bugwatch/error_builder.rb +75 -0
- data/lib/bugwatch/middleware.rb +28 -0
- data/lib/bugwatch/notification.rb +49 -0
- data/lib/bugwatch/railtie.rb +32 -0
- data/lib/bugwatch/user_context.rb +17 -0
- data/lib/bugwatch/version.rb +3 -0
- data/lib/bugwatch.rb +41 -0
- metadata +80 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d791f28771211d583f75d081bab1ac2a669f8cd5e023f8ec4a54045588955781
|
|
4
|
+
data.tar.gz: 5194c786bf49b7549fdf52c3b32fa3a8b2e8b285000f7339828e49bf2f6972d9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e7100cf66b34e6fbf8851d36851e9b9aab6376b8a9eebfd9d08a3f701144a23f056bf17b972e61ca4af00430d1e26c57a24a4fd2b2811530b07ae83ba6e19c01
|
|
7
|
+
data.tar.gz: 915b85cd3c4ab144027c9ab7d6c8098e38dfbf52429adb6c27070045eca8dd9889e82c71613c82653c0ee679182a8c4105421aaf90866c3e5d0babe890ce011c
|
data/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# bugwatch-ruby
|
|
2
|
+
|
|
3
|
+
Official Ruby/Rails SDK for [BugWatch](https://bugwatch.io) — open-source error monitoring.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
# Gemfile
|
|
9
|
+
gem "bugwatch-ruby"
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
bundle install
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Configuration
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
# config/initializers/bugwatch.rb
|
|
20
|
+
Bugwatch.configure do |c|
|
|
21
|
+
c.api_key = ENV["BUGWATCH_API_KEY"]
|
|
22
|
+
c.endpoint = ENV["BUGWATCH_ENDPOINT"] # e.g. "https://your-app.herokuapp.com"
|
|
23
|
+
c.release_stage = Rails.env.to_s
|
|
24
|
+
c.notify_release_stages = ["production", "staging"]
|
|
25
|
+
c.ignore_classes = ["ActionController::RoutingError"]
|
|
26
|
+
c.app_version = ENV["GIT_REV"] # optional git SHA
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## User context
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
# app/controllers/application_controller.rb
|
|
34
|
+
before_action :set_bugwatch_user
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def set_bugwatch_user
|
|
39
|
+
return unless current_user
|
|
40
|
+
Bugwatch.set_user(
|
|
41
|
+
id: current_user.id,
|
|
42
|
+
email: current_user.email,
|
|
43
|
+
name: current_user.name
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Breadcrumbs
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
Bugwatch.leave_breadcrumb("User clicked checkout", type: "ui", metadata: { cart_id: 42 })
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Manual notification
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
begin
|
|
58
|
+
risky_operation
|
|
59
|
+
rescue => e
|
|
60
|
+
Bugwatch.notify(e)
|
|
61
|
+
raise
|
|
62
|
+
end
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## How it works
|
|
66
|
+
|
|
67
|
+
1. `Bugwatch::Middleware` wraps your entire Rack stack.
|
|
68
|
+
2. When an unhandled exception propagates out of a request, the middleware:
|
|
69
|
+
- Captures the exception, full backtrace (with 3 lines of source context per in-app frame), request details, user context, and breadcrumbs.
|
|
70
|
+
- POSTs the payload to your BugWatch instance in a background thread (fire-and-forget, 3s timeout).
|
|
71
|
+
- Re-raises the exception so Rails error handling fires normally.
|
|
72
|
+
3. Thread-local user context and breadcrumbs are cleared automatically after each request by the Rails around_action hook.
|
|
73
|
+
|
|
74
|
+
## What gets sent
|
|
75
|
+
|
|
76
|
+
| Field | Description |
|
|
77
|
+
|-------|-------------|
|
|
78
|
+
| `exception.error_class` | Exception class name |
|
|
79
|
+
| `exception.message` | Exception message |
|
|
80
|
+
| `exception.backtrace` | Array of frames with `file`, `line`, `method`, `in_app`, `source_context` |
|
|
81
|
+
| `request.*` | Method, URL, params (filtered), headers (filtered), IP |
|
|
82
|
+
| `app.*` | Rails env, Ruby version, Rails version, git SHA, hostname |
|
|
83
|
+
| `user.*` | ID, email, name, any custom fields |
|
|
84
|
+
| `breadcrumbs` | Last 50 breadcrumbs with timestamp, type, message, metadata |
|
|
85
|
+
| `duration_ms` | Request duration in milliseconds |
|
|
86
|
+
|
|
87
|
+
Sensitive params (`password`, `token`, `secret`, `key`, `auth`, `credit`, `card`, `cvv`, `ssn`) are automatically filtered from request params and never sent.
|
|
88
|
+
|
|
89
|
+
## Publishing
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
cd /home/max/bugwatch-ruby
|
|
93
|
+
gem build bugwatch-ruby.gemspec
|
|
94
|
+
gem signin
|
|
95
|
+
gem push bugwatch-ruby-0.1.0.gem
|
|
96
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Bugwatch
|
|
2
|
+
class BacktraceCleaner
|
|
3
|
+
STDLIB_PATHS = [
|
|
4
|
+
RbConfig::CONFIG["rubylibdir"],
|
|
5
|
+
RbConfig::CONFIG["rubyarchdir"],
|
|
6
|
+
Gem.default_dir
|
|
7
|
+
].compact.map { |p| Regexp.escape(p) }
|
|
8
|
+
|
|
9
|
+
GEM_PATH_RE = /\/gems\//
|
|
10
|
+
STDLIB_RE = Regexp.union(*STDLIB_PATHS) unless STDLIB_PATHS.empty?
|
|
11
|
+
|
|
12
|
+
def self.clean(backtrace)
|
|
13
|
+
return [] unless backtrace
|
|
14
|
+
|
|
15
|
+
backtrace.map do |line|
|
|
16
|
+
path, lineno, label = parse_frame(line)
|
|
17
|
+
in_app = in_app?(path)
|
|
18
|
+
|
|
19
|
+
frame = {
|
|
20
|
+
file: path,
|
|
21
|
+
line: lineno,
|
|
22
|
+
method: label,
|
|
23
|
+
in_app: in_app
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if in_app && path && File.exist?(path)
|
|
27
|
+
frame[:source_context] = read_source_context(path, lineno)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
frame
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.parse_frame(line)
|
|
35
|
+
if line =~ /\A(.+?):(\d+):in `(.*)'\z/
|
|
36
|
+
[$1, $2.to_i, $3]
|
|
37
|
+
elsif line =~ /\A(.+?):(\d+)\z/
|
|
38
|
+
[$1, $2.to_i, nil]
|
|
39
|
+
else
|
|
40
|
+
[line, 0, nil]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.in_app?(path)
|
|
45
|
+
return false if path.nil?
|
|
46
|
+
return false if path.match?(GEM_PATH_RE)
|
|
47
|
+
return false if defined?(STDLIB_RE) && path.match?(STDLIB_RE)
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.read_source_context(path, lineno, context_lines: 3)
|
|
52
|
+
lines = File.readlines(path)
|
|
53
|
+
first = [lineno - context_lines - 1, 0].max
|
|
54
|
+
last = [lineno + context_lines - 1, lines.size - 1].min
|
|
55
|
+
|
|
56
|
+
lines[first..last].each_with_object({}).with_index(first + 1) do |(source_line, hash), n|
|
|
57
|
+
hash[n] = source_line.chomp
|
|
58
|
+
end
|
|
59
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
60
|
+
{}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Bugwatch
|
|
2
|
+
module BreadcrumbCollector
|
|
3
|
+
THREAD_KEY = :bugwatch_breadcrumbs
|
|
4
|
+
MAX_CRUMBS = 50
|
|
5
|
+
|
|
6
|
+
def self.add(message, type: "manual", metadata: {})
|
|
7
|
+
crumbs = Thread.current[THREAD_KEY] ||= []
|
|
8
|
+
crumbs << {
|
|
9
|
+
timestamp: Time.now.utc.iso8601(3),
|
|
10
|
+
type: type,
|
|
11
|
+
message: message.to_s,
|
|
12
|
+
metadata: metadata
|
|
13
|
+
}
|
|
14
|
+
crumbs.shift if crumbs.size > MAX_CRUMBS
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.all
|
|
18
|
+
Thread.current[THREAD_KEY] || []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.clear
|
|
22
|
+
Thread.current[THREAD_KEY] = []
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Bugwatch
|
|
2
|
+
class Configuration
|
|
3
|
+
attr_accessor :api_key,
|
|
4
|
+
:endpoint,
|
|
5
|
+
:release_stage,
|
|
6
|
+
:notify_release_stages,
|
|
7
|
+
:ignore_classes,
|
|
8
|
+
:app_version,
|
|
9
|
+
:logger
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@endpoint = nil
|
|
13
|
+
@release_stage = "production"
|
|
14
|
+
@notify_release_stages = ["production"]
|
|
15
|
+
@ignore_classes = []
|
|
16
|
+
@logger = Logger.new($stdout) if defined?(Logger)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def notify_for_release_stage?
|
|
20
|
+
notify_release_stages.include?(release_stage.to_s)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def ignore?(exception)
|
|
24
|
+
ignore_classes.any? do |klass|
|
|
25
|
+
klass = Object.const_get(klass) if klass.is_a?(String)
|
|
26
|
+
exception.is_a?(klass)
|
|
27
|
+
rescue NameError
|
|
28
|
+
false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Bugwatch
|
|
2
|
+
class ErrorBuilder
|
|
3
|
+
def initialize(exception, rack_env = nil, config: Bugwatch.configuration)
|
|
4
|
+
@exception = exception
|
|
5
|
+
@rack_env = rack_env
|
|
6
|
+
@config = config
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def build
|
|
10
|
+
{
|
|
11
|
+
exception: exception_payload,
|
|
12
|
+
request: request_payload,
|
|
13
|
+
app: app_payload,
|
|
14
|
+
user: UserContext.get,
|
|
15
|
+
breadcrumbs: BreadcrumbCollector.all
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def exception_payload
|
|
22
|
+
{
|
|
23
|
+
error_class: @exception.class.name,
|
|
24
|
+
message: @exception.message,
|
|
25
|
+
backtrace: BacktraceCleaner.clean(@exception.backtrace)
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def request_payload
|
|
30
|
+
return {} unless @rack_env
|
|
31
|
+
|
|
32
|
+
req = Rack::Request.new(@rack_env)
|
|
33
|
+
|
|
34
|
+
{
|
|
35
|
+
method: req.request_method,
|
|
36
|
+
url: req.url,
|
|
37
|
+
params: safe_params(req),
|
|
38
|
+
headers: extract_headers(@rack_env),
|
|
39
|
+
ip: req.ip
|
|
40
|
+
}
|
|
41
|
+
rescue StandardError
|
|
42
|
+
{}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def app_payload
|
|
46
|
+
payload = {
|
|
47
|
+
environment: @config.release_stage,
|
|
48
|
+
version: @config.app_version,
|
|
49
|
+
hostname: Socket.gethostname
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if defined?(RUBY_VERSION)
|
|
53
|
+
payload[:ruby_version] = RUBY_VERSION
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if defined?(Rails)
|
|
57
|
+
payload[:rails_version] = Rails.version
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
payload
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def safe_params(req)
|
|
64
|
+
req.params.reject { |k, _| k.to_s.downcase.match?(/password|secret|token|key|auth|credit|card|cvv|ssn/) }
|
|
65
|
+
rescue StandardError
|
|
66
|
+
{}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def extract_headers(env)
|
|
70
|
+
env.select { |k, _| k.start_with?("HTTP_") }
|
|
71
|
+
.transform_keys { |k| k.sub(/^HTTP_/, "").split("_").map(&:capitalize).join("-") }
|
|
72
|
+
.reject { |k, _| k.downcase.match?(/cookie|authorization/) }
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Bugwatch
|
|
2
|
+
class Middleware
|
|
3
|
+
def initialize(app)
|
|
4
|
+
@app = app
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def call(env)
|
|
8
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
9
|
+
BreadcrumbCollector.clear
|
|
10
|
+
UserContext.clear
|
|
11
|
+
|
|
12
|
+
begin
|
|
13
|
+
status, headers, body = @app.call(env)
|
|
14
|
+
[status, headers, body]
|
|
15
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
16
|
+
duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000).round
|
|
17
|
+
|
|
18
|
+
unless Bugwatch.configuration.ignore?(e)
|
|
19
|
+
payload = ErrorBuilder.new(e, env).build
|
|
20
|
+
payload[:duration_ms] = duration_ms
|
|
21
|
+
Notification.new(payload).deliver
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
raise
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require "net/http"
|
|
2
|
+
require "uri"
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Bugwatch
|
|
6
|
+
class Notification
|
|
7
|
+
TIMEOUT = 3
|
|
8
|
+
|
|
9
|
+
def initialize(payload, config: Bugwatch.configuration)
|
|
10
|
+
@payload = payload
|
|
11
|
+
@config = config
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def deliver
|
|
15
|
+
return unless @config.api_key
|
|
16
|
+
return unless @config.endpoint
|
|
17
|
+
return unless @config.notify_for_release_stage?
|
|
18
|
+
|
|
19
|
+
Thread.new do
|
|
20
|
+
post_to_api
|
|
21
|
+
rescue StandardError
|
|
22
|
+
# Fire-and-forget: swallow all errors so the gem never affects the app
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def post_to_api
|
|
29
|
+
uri = URI.parse("#{@config.endpoint.chomp("/")}/api/v1/errors")
|
|
30
|
+
|
|
31
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
32
|
+
http.use_ssl = uri.scheme == "https"
|
|
33
|
+
http.open_timeout = TIMEOUT
|
|
34
|
+
http.read_timeout = TIMEOUT
|
|
35
|
+
http.write_timeout = TIMEOUT
|
|
36
|
+
|
|
37
|
+
request = Net::HTTP::Post.new(uri.path)
|
|
38
|
+
request["Content-Type"] = "application/json"
|
|
39
|
+
request["X-Api-Key"] = @config.api_key
|
|
40
|
+
request["X-BugWatch-Ruby"] = Bugwatch::VERSION
|
|
41
|
+
|
|
42
|
+
request.body = JSON.generate(@payload)
|
|
43
|
+
|
|
44
|
+
http.request(request)
|
|
45
|
+
rescue StandardError
|
|
46
|
+
# Silently discard network errors
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Bugwatch
|
|
2
|
+
class Railtie < Rails::Railtie
|
|
3
|
+
initializer "bugwatch.middleware" do |app|
|
|
4
|
+
app.middleware.use Bugwatch::Middleware
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
initializer "bugwatch.action_controller" do
|
|
8
|
+
ActiveSupport.on_load(:action_controller) do
|
|
9
|
+
include Bugwatch::ControllerMethods
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module ControllerMethods
|
|
15
|
+
extend ActiveSupport::Concern
|
|
16
|
+
|
|
17
|
+
included do
|
|
18
|
+
around_action :bugwatch_reset_context
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def bugwatch_reset_context
|
|
24
|
+
Bugwatch::UserContext.clear
|
|
25
|
+
Bugwatch::BreadcrumbCollector.clear
|
|
26
|
+
yield
|
|
27
|
+
ensure
|
|
28
|
+
Bugwatch::UserContext.clear
|
|
29
|
+
Bugwatch::BreadcrumbCollector.clear
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Bugwatch
|
|
2
|
+
module UserContext
|
|
3
|
+
THREAD_KEY = :bugwatch_user_context
|
|
4
|
+
|
|
5
|
+
def self.set(id: nil, email: nil, name: nil, **custom)
|
|
6
|
+
Thread.current[THREAD_KEY] = { id: id, email: email, name: name }.merge(custom).compact
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.get
|
|
10
|
+
Thread.current[THREAD_KEY] || {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.clear
|
|
14
|
+
Thread.current[THREAD_KEY] = nil
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/bugwatch.rb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require "logger"
|
|
2
|
+
require "socket"
|
|
3
|
+
|
|
4
|
+
require_relative "bugwatch/version"
|
|
5
|
+
require_relative "bugwatch/configuration"
|
|
6
|
+
require_relative "bugwatch/user_context"
|
|
7
|
+
require_relative "bugwatch/breadcrumb_collector"
|
|
8
|
+
require_relative "bugwatch/backtrace_cleaner"
|
|
9
|
+
require_relative "bugwatch/error_builder"
|
|
10
|
+
require_relative "bugwatch/notification"
|
|
11
|
+
require_relative "bugwatch/middleware"
|
|
12
|
+
require_relative "bugwatch/railtie" if defined?(Rails::Railtie)
|
|
13
|
+
|
|
14
|
+
module Bugwatch
|
|
15
|
+
class << self
|
|
16
|
+
def configuration
|
|
17
|
+
@configuration ||= Configuration.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def configure
|
|
21
|
+
yield configuration
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def notify(exception, context: {})
|
|
25
|
+
return if configuration.ignore?(exception)
|
|
26
|
+
return unless configuration.notify_for_release_stage?
|
|
27
|
+
|
|
28
|
+
payload = ErrorBuilder.new(exception).build
|
|
29
|
+
payload.merge!(context)
|
|
30
|
+
Notification.new(payload).deliver
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def set_user(id: nil, email: nil, name: nil, **custom)
|
|
34
|
+
UserContext.set(id: id, email: email, name: name, **custom)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def leave_breadcrumb(message, type: "manual", metadata: {})
|
|
38
|
+
BreadcrumbCollector.add(message, type: type, metadata: metadata)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: bugwatch-ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- BugWatch
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-04-05 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: railties
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rack
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.0'
|
|
40
|
+
description: Automatically captures and reports exceptions from your Rails app to
|
|
41
|
+
BugWatch, with request context, user context, breadcrumbs, and source code snippets.
|
|
42
|
+
email:
|
|
43
|
+
- support@bugwatch.io
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- README.md
|
|
49
|
+
- lib/bugwatch.rb
|
|
50
|
+
- lib/bugwatch/backtrace_cleaner.rb
|
|
51
|
+
- lib/bugwatch/breadcrumb_collector.rb
|
|
52
|
+
- lib/bugwatch/configuration.rb
|
|
53
|
+
- lib/bugwatch/error_builder.rb
|
|
54
|
+
- lib/bugwatch/middleware.rb
|
|
55
|
+
- lib/bugwatch/notification.rb
|
|
56
|
+
- lib/bugwatch/railtie.rb
|
|
57
|
+
- lib/bugwatch/user_context.rb
|
|
58
|
+
- lib/bugwatch/version.rb
|
|
59
|
+
homepage: https://github.com/bugwatch/bugwatch-ruby
|
|
60
|
+
licenses:
|
|
61
|
+
- MIT
|
|
62
|
+
metadata: {}
|
|
63
|
+
rdoc_options: []
|
|
64
|
+
require_paths:
|
|
65
|
+
- lib
|
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '3.0'
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
requirements: []
|
|
77
|
+
rubygems_version: 3.6.2
|
|
78
|
+
specification_version: 4
|
|
79
|
+
summary: Official Ruby/Rails SDK for BugWatch error monitoring
|
|
80
|
+
test_files: []
|