bugwatch-sdk 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/LICENSE +21 -0
- data/README.md +74 -0
- data/lib/bugwatch/client.rb +84 -0
- data/lib/bugwatch/dsn.rb +69 -0
- data/lib/bugwatch/integration.rb +28 -0
- data/lib/bugwatch/version.rb +4 -0
- data/lib/bugwatch.rb +121 -0
- metadata +64 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 94f9c2ad3792e673573fa9fe2d517d4b9eb591fb2f1bacb18d231c41c7c46d9c
|
|
4
|
+
data.tar.gz: b1d6325a16b22c9b4770be9d9e1d49f2df71e1f7ce5462fbe94856f859467dbc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a2aec0365f97d36f3a4abce0ec72097b0f0b5ae5773e803cf950868885c9d5c7eb271555a0ebaf17151128ede1b67f708521ba7cfeddaeea15f6968402d8499e
|
|
7
|
+
data.tar.gz: 7547ca8e2cf31b869463bbdbafd75408a932bd5df955379ee5fe7c247f9ab06e4b7e38a14e87a4ba4cd68c1bf94f3fd9fe8e1d2019430b780f3a66a9f624a422
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ROOMY AI LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Bugwatch Ruby SDK
|
|
2
|
+
|
|
3
|
+
Official Ruby SDK for Bugwatch error tracking. Stdlib only — zero runtime dependencies.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
gem 'bugwatch-sdk', '~> 0.1'
|
|
9
|
+
# or from source:
|
|
10
|
+
gem 'bugwatch-sdk', github: 'roomylabs/bugwatch-ruby'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
require "bugwatch"
|
|
17
|
+
|
|
18
|
+
Bugwatch.init(
|
|
19
|
+
"https://bw-YOUR_KEY@bugwatch-api.loadmindx.com/api/22",
|
|
20
|
+
environment: "production",
|
|
21
|
+
release: "1.0.0",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Capture the active exception.
|
|
25
|
+
begin
|
|
26
|
+
1 / 0
|
|
27
|
+
rescue ZeroDivisionError => e
|
|
28
|
+
Bugwatch.capture_exception(e)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Capture a message.
|
|
32
|
+
Bugwatch.capture_message("Payment confirmed", level: "info")
|
|
33
|
+
|
|
34
|
+
# Wait for async sends (short-lived scripts).
|
|
35
|
+
Bugwatch.flush
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
|
|
40
|
+
| Method | Description |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `init(dsn, environment:, release:, debug:, send_default_pii:, allow_http:)` | Initialize the SDK (HTTPS enforced; `allow_http` is dev-only) |
|
|
43
|
+
| `capture_exception(exc = $!, level:, tags:, user:)` | Report an exception (defaults to `$!` inside rescue) |
|
|
44
|
+
| `capture_message(message, level:, tags:, user:)` | Report a message (`debug info warning error fatal`) |
|
|
45
|
+
| `configure_scope { \|scope\| ... }` | Set global tags / user / breadcrumbs / context |
|
|
46
|
+
| `flush(timeout: 5.0)` | Wait for in-flight sends; returns `true` when done |
|
|
47
|
+
|
|
48
|
+
## Scope
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
Bugwatch.configure_scope do |scope|
|
|
52
|
+
scope.set_tag("tenant", "acme")
|
|
53
|
+
scope.set_user("id" => 42, "name" => "Grace") # email/ip stripped (PII)
|
|
54
|
+
scope.add_breadcrumb("clicked checkout", category: "ui")
|
|
55
|
+
end
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Rack middleware (Rails, Sinatra…)
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
# config/application.rb (Rails) or `use Bugwatch::RackMiddleware` (Sinatra)
|
|
62
|
+
require "bugwatch"
|
|
63
|
+
config.middleware.insert_before 0, Bugwatch::RackMiddleware
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Captures unhandled exceptions with request context (URL, method, headers — sensitive headers stripped).
|
|
67
|
+
|
|
68
|
+
## Behavior
|
|
69
|
+
|
|
70
|
+
- Sends are **async** (background threads, max 50 in flight); call `flush` in short-lived scripts
|
|
71
|
+
- **HTTPS enforced** — `http://` DSNs raise unless `allow_http: true`
|
|
72
|
+
- **PII stripped by default** — `email`, `username`, `ip_address` removed from user; `Cookie`/`Authorization` headers removed from request context
|
|
73
|
+
- Breadcrumbs capped at 100; failures never crash the host app
|
|
74
|
+
- DSN format: `https://<public_key>@<host>/api/<project_id>` (the project's ingest key — Settings → API keys)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require "time"
|
|
2
|
+
|
|
3
|
+
module Bugwatch
|
|
4
|
+
module Client
|
|
5
|
+
MAX_THREADS = 50
|
|
6
|
+
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def payload(event_id:, level:, message:, culprit:, exception:, user:, tags:)
|
|
10
|
+
cfg = Bugwatch.config
|
|
11
|
+
scope = cfg[:scope].snapshot
|
|
12
|
+
p = {
|
|
13
|
+
"event_id" => event_id,
|
|
14
|
+
"timestamp" => Time.now.utc.iso8601,
|
|
15
|
+
"level" => level.to_s,
|
|
16
|
+
"message" => message,
|
|
17
|
+
"culprit" => culprit,
|
|
18
|
+
"environment" => cfg[:environment],
|
|
19
|
+
"release" => cfg[:release],
|
|
20
|
+
"platform" => "ruby",
|
|
21
|
+
}
|
|
22
|
+
p["exception"] = exception if exception
|
|
23
|
+
merged_user = user || scope[:user]
|
|
24
|
+
p["user"] = Dsn.strip_pii_user(merged_user) if merged_user
|
|
25
|
+
merged_tags = scope[:tags].merge(tags || {})
|
|
26
|
+
p["tags"] = merged_tags unless merged_tags.empty?
|
|
27
|
+
p["breadcrumbs"] = scope[:breadcrumbs] unless scope[:breadcrumbs].empty?
|
|
28
|
+
req = scope[:context]["request"]
|
|
29
|
+
if req
|
|
30
|
+
req = cfg[:send_default_pii] ? req : Dsn.strip_pii_request(req)
|
|
31
|
+
p["request"] = req
|
|
32
|
+
end
|
|
33
|
+
p
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def send(payload)
|
|
37
|
+
cfg = Bugwatch.config
|
|
38
|
+
thread = Thread.new do
|
|
39
|
+
post(cfg[:store_url], cfg[:public_key], payload)
|
|
40
|
+
end
|
|
41
|
+
thread.report_on_exception = false
|
|
42
|
+
pool << thread
|
|
43
|
+
prune_pool
|
|
44
|
+
true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def flush(timeout = 5.0)
|
|
48
|
+
deadline = Time.now + timeout
|
|
49
|
+
pool.dup.each { |t| t.join([0.01, deadline - Time.now].max) while Time.now < deadline && !t.join(0.05) }
|
|
50
|
+
pool.dup.each(&:join) if Time.now < deadline
|
|
51
|
+
pool.clear
|
|
52
|
+
true
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def pool
|
|
56
|
+
@pool ||= []
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def prune_pool
|
|
60
|
+
pool.reject! { |t| !t.alive? }
|
|
61
|
+
pool.shift while pool.size > MAX_THREADS
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def post(store_url, public_key, payload)
|
|
65
|
+
uri = URI.parse(store_url)
|
|
66
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
67
|
+
http.use_ssl = uri.scheme == "https"
|
|
68
|
+
http.open_timeout = 5
|
|
69
|
+
http.read_timeout = 5
|
|
70
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
|
71
|
+
req["Content-Type"] = "application/json"
|
|
72
|
+
req["X-Bugwatch-Key"] = public_key
|
|
73
|
+
req["User-Agent"] = USER_AGENT
|
|
74
|
+
req.body = JSON.generate(payload)
|
|
75
|
+
http.request(req)
|
|
76
|
+
rescue StandardError => e
|
|
77
|
+
warn("bugwatch: send failed: #{e.message}") if Bugwatch.config[:debug]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def warn(msg)
|
|
81
|
+
$stderr.puts(msg)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
data/lib/bugwatch/dsn.rb
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module Bugwatch
|
|
2
|
+
module Dsn
|
|
3
|
+
PII_KEYS = %w[email username ip_address ip].freeze
|
|
4
|
+
SENSITIVE_HEADERS = %w[cookie authorization x-bugwatch-key set-cookie].freeze
|
|
5
|
+
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def parse(dsn, allow_http: false)
|
|
9
|
+
raise Error, "DSN must be a non-empty string" unless dsn.is_a?(String) && !dsn.strip.empty?
|
|
10
|
+
uri = URI.parse(dsn.strip)
|
|
11
|
+
unless %w[http https].include?(uri.scheme)
|
|
12
|
+
raise Error, "Invalid DSN scheme: expected https (got #{uri.scheme.inspect})"
|
|
13
|
+
end
|
|
14
|
+
if uri.scheme == "http" && !allow_http
|
|
15
|
+
raise Error, "HTTP DSN refused. Use https:// (allow_http=True is dev-only)"
|
|
16
|
+
end
|
|
17
|
+
raise Error, "DSN missing public key before '@'" unless uri.userinfo && !uri.userinfo.empty?
|
|
18
|
+
raise Error, "DSN missing host" unless uri.host
|
|
19
|
+
segments = uri.path.split("/").reject(&:empty?)
|
|
20
|
+
project_id =
|
|
21
|
+
if segments.size >= 2 && segments[0].downcase == "api"
|
|
22
|
+
segments[1]
|
|
23
|
+
else
|
|
24
|
+
segments[0]
|
|
25
|
+
end
|
|
26
|
+
raise Error, "DSN missing project_id in path" if project_id.nil? || project_id.empty?
|
|
27
|
+
base = "#{uri.scheme}://#{uri.host}#{uri.port == uri.default_port ? '' : ":#{uri.port}"}"
|
|
28
|
+
{ public_key: uri.userinfo, base_url: base, project_id: project_id,
|
|
29
|
+
store_url: "#{base}/api/#{project_id}/store/" }
|
|
30
|
+
rescue URI::InvalidURIError, URI::Error
|
|
31
|
+
raise Error, "Invalid DSN: #{dsn.inspect}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def exception_payload(exc)
|
|
35
|
+
return nil unless exc
|
|
36
|
+
frames = (exc.backtrace || []).first(50).map do |line|
|
|
37
|
+
file, lineno, fn = line.split(":", 3)
|
|
38
|
+
{ "filename" => file.to_s, "lineno" => (lineno.to_i rescue 0),
|
|
39
|
+
"function" => (fn ? fn.split(":in ").last.to_s : nil) }.compact
|
|
40
|
+
end
|
|
41
|
+
{ "type" => exc.class.name.to_s, "value" => exc.message.to_s,
|
|
42
|
+
"stacktrace" => { "frames" => frames } }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def culprit_from(exc)
|
|
46
|
+
first = (exc.backtrace || []).first
|
|
47
|
+
return nil unless first
|
|
48
|
+
file, lineno, = first.split(":", 3)
|
|
49
|
+
"#{File.basename(file.to_s)}:#{lineno}"
|
|
50
|
+
rescue StandardError
|
|
51
|
+
nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def strip_pii_user(user)
|
|
55
|
+
return nil unless user.is_a?(Hash)
|
|
56
|
+
user.reject { |k, _| PII_KEYS.include?(k.to_s.downcase) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def strip_pii_request(req)
|
|
60
|
+
return req unless req.is_a?(Hash)
|
|
61
|
+
out = req.dup
|
|
62
|
+
headers = out["headers"]
|
|
63
|
+
if headers.is_a?(Hash)
|
|
64
|
+
out["headers"] = headers.reject { |k, _| SENSITIVE_HEADERS.include?(k.to_s.downcase) }
|
|
65
|
+
end
|
|
66
|
+
out
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Bugwatch
|
|
2
|
+
# Rack middleware: captures unhandled exceptions from any Rack app
|
|
3
|
+
# (Rails, Sinatra, etc.) and reports them to Bugwatch.
|
|
4
|
+
class RackMiddleware
|
|
5
|
+
def initialize(app)
|
|
6
|
+
@app = app
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(env)
|
|
10
|
+
@app.call(env)
|
|
11
|
+
rescue Exception => e
|
|
12
|
+
begin
|
|
13
|
+
scope = Bugwatch.initialized? ? Bugwatch.config[:scope] : nil
|
|
14
|
+
if scope
|
|
15
|
+
scope.set_context("request" => {
|
|
16
|
+
"url" => "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}#{env['REQUEST_PATH']}",
|
|
17
|
+
"method" => env["REQUEST_METHOD"],
|
|
18
|
+
"headers" => env.select { |k, _| k.to_s.start_with?("HTTP_") },
|
|
19
|
+
})
|
|
20
|
+
end
|
|
21
|
+
Bugwatch.capture_exception(e)
|
|
22
|
+
ensure
|
|
23
|
+
scope&.set_context("request" => nil)
|
|
24
|
+
raise e
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/bugwatch.rb
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "net/http"
|
|
3
|
+
require "uri"
|
|
4
|
+
require "securerandom"
|
|
5
|
+
require "thread"
|
|
6
|
+
|
|
7
|
+
require_relative "bugwatch/version"
|
|
8
|
+
require_relative "bugwatch/dsn"
|
|
9
|
+
require_relative "bugwatch/client"
|
|
10
|
+
require_relative "bugwatch/integration"
|
|
11
|
+
|
|
12
|
+
module Bugwatch
|
|
13
|
+
class Error < StandardError; end
|
|
14
|
+
|
|
15
|
+
LEVELS = %w[debug info warning error fatal].freeze
|
|
16
|
+
DEFAULT_LEVEL = "error"
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
def init(dsn, environment: "production", release: nil, debug: false,
|
|
20
|
+
send_default_pii: false, allow_http: false)
|
|
21
|
+
cfg = Dsn.parse(dsn, allow_http: allow_http)
|
|
22
|
+
@config = {
|
|
23
|
+
store_url: cfg[:store_url], public_key: cfg[:public_key],
|
|
24
|
+
project_id: cfg[:project_id], environment: environment.to_s,
|
|
25
|
+
release: release, debug: !!debug, send_default_pii: !!send_default_pii,
|
|
26
|
+
scope: Scope.new
|
|
27
|
+
}
|
|
28
|
+
true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def config
|
|
32
|
+
@config or raise Error, "Bugwatch.init must be called before use"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def initialized?
|
|
36
|
+
!!@config
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def capture_exception(exc = $!, level: DEFAULT_LEVEL, tags: nil, user: nil)
|
|
40
|
+
return nil unless initialized?
|
|
41
|
+
exc ||= $ERROR_INFO
|
|
42
|
+
return nil unless exc.is_a?(Exception)
|
|
43
|
+
event_id = SecureRandom.uuid.delete("-")
|
|
44
|
+
payload = Client.payload(
|
|
45
|
+
event_id: event_id, level: level, message: "#{exc.class}: #{exc.message}",
|
|
46
|
+
culprit: Dsn.culprit_from(exc),
|
|
47
|
+
exception: Dsn.exception_payload(exc),
|
|
48
|
+
user: user, tags: tags
|
|
49
|
+
)
|
|
50
|
+
Client.send(payload)
|
|
51
|
+
event_id
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def capture_message(message, level: "info", tags: nil, user: nil)
|
|
55
|
+
return nil unless initialized?
|
|
56
|
+
event_id = SecureRandom.uuid.delete("-")
|
|
57
|
+
payload = Client.payload(
|
|
58
|
+
event_id: event_id, level: level, message: message.to_s,
|
|
59
|
+
culprit: nil, exception: nil, user: user, tags: tags
|
|
60
|
+
)
|
|
61
|
+
Client.send(payload)
|
|
62
|
+
event_id
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def configure_scope
|
|
66
|
+
yield config[:scope] if block_given?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def flush(timeout: 5.0)
|
|
70
|
+
Client.flush(timeout)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class Scope
|
|
75
|
+
attr_reader :tags, :user, :breadcrumbs, :context
|
|
76
|
+
|
|
77
|
+
def initialize
|
|
78
|
+
@tags = {}
|
|
79
|
+
@user = nil
|
|
80
|
+
@breadcrumbs = []
|
|
81
|
+
@context = {}
|
|
82
|
+
@lock = Mutex.new
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def set_user(user)
|
|
86
|
+
@lock.synchronize { @user = Dsn.strip_pii_user(user) }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def set_tag(key, value)
|
|
90
|
+
@lock.synchronize { @tags[key.to_s] = value.to_s }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def set_context(context)
|
|
94
|
+
@lock.synchronize { @context.merge!(context) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def add_breadcrumb(message, category: nil, level: "info", data: nil)
|
|
98
|
+
crumb = { "timestamp" => Time.now.utc.iso8601, "message" => message.to_s,
|
|
99
|
+
"level" => level.to_s }
|
|
100
|
+
crumb["category"] = category if category
|
|
101
|
+
crumb["data"] = data if data
|
|
102
|
+
@lock.synchronize do
|
|
103
|
+
@breadcrumbs << crumb
|
|
104
|
+
@breadcrumbs.shift if @breadcrumbs.size > 100
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def snapshot
|
|
109
|
+
@lock.synchronize do
|
|
110
|
+
{ tags: @tags.dup, user: @user && @user.dup,
|
|
111
|
+
breadcrumbs: @breadcrumbs.map(&:dup), context: deep_dup(@context) }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
def deep_dup(h)
|
|
118
|
+
h.each_with_object({}) { |(k, v), acc| acc[k] = v.is_a?(Hash) ? deep_dup(v) : v }
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: bugwatch-sdk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- ROOMY AI LLC
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: minitest
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '5.0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '5.0'
|
|
27
|
+
description: Capture exceptions and messages from Ruby apps to Bugwatch. Stdlib only.
|
|
28
|
+
email:
|
|
29
|
+
- billing@roomylabs.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- LICENSE
|
|
35
|
+
- README.md
|
|
36
|
+
- lib/bugwatch.rb
|
|
37
|
+
- lib/bugwatch/client.rb
|
|
38
|
+
- lib/bugwatch/dsn.rb
|
|
39
|
+
- lib/bugwatch/integration.rb
|
|
40
|
+
- lib/bugwatch/version.rb
|
|
41
|
+
homepage: https://bugwatch.roomylabs.com
|
|
42
|
+
licenses:
|
|
43
|
+
- MIT
|
|
44
|
+
metadata: {}
|
|
45
|
+
post_install_message:
|
|
46
|
+
rdoc_options: []
|
|
47
|
+
require_paths:
|
|
48
|
+
- lib
|
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.0'
|
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '0'
|
|
59
|
+
requirements: []
|
|
60
|
+
rubygems_version: 3.4.20
|
|
61
|
+
signing_key:
|
|
62
|
+
specification_version: 4
|
|
63
|
+
summary: Official Ruby SDK for Bugwatch error tracking
|
|
64
|
+
test_files: []
|