bloop-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/lib/bloop.rb +149 -0
- metadata +45 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5596c4ce2d5c135c37489bc42e8f5198e08747db4d2f042b4b0d92c4da922505
|
|
4
|
+
data.tar.gz: 83611d60a780e9b16d67b43199998984a5673174ea08b79d29b20962a29d7859
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f16a1f358c7fd2406156c4a771b252fa186eb65cedfbf4dc5184aed896f7923805c119c5f317a914d9a0ca45d9b2f1cd1510a478bf08ef54f70132e03db1ebad
|
|
7
|
+
data.tar.gz: 933331f141544fa8f041e883094624cae9d9dbdbfcde3f1efbeb29d5e3e9056330e33f9bb4c8b31fe5a43dcc20c18b6fd64a4e14c45dc7c063451b0ee4fe4658
|
data/lib/bloop.rb
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
require "net/http"
|
|
5
|
+
require "json"
|
|
6
|
+
require "uri"
|
|
7
|
+
|
|
8
|
+
module Bloop
|
|
9
|
+
VERSION = "0.1.0"
|
|
10
|
+
|
|
11
|
+
class Client
|
|
12
|
+
attr_reader :endpoint, :project_key
|
|
13
|
+
|
|
14
|
+
# @param endpoint [String] Bloop server URL
|
|
15
|
+
# @param project_key [String] Project API key for HMAC signing
|
|
16
|
+
# @param environment [String] Environment tag (default: "production")
|
|
17
|
+
# @param release [String] Release version tag
|
|
18
|
+
# @param flush_interval [Numeric] Seconds between auto-flushes (default: 5)
|
|
19
|
+
# @param max_buffer_size [Integer] Flush when buffer reaches this size (default: 100)
|
|
20
|
+
def initialize(endpoint:, project_key:, environment: "production", release: "", flush_interval: 5, max_buffer_size: 100)
|
|
21
|
+
@endpoint = endpoint.chomp("/")
|
|
22
|
+
@project_key = project_key
|
|
23
|
+
@environment = environment
|
|
24
|
+
@release = release
|
|
25
|
+
@flush_interval = flush_interval
|
|
26
|
+
@max_buffer_size = max_buffer_size
|
|
27
|
+
|
|
28
|
+
@buffer = []
|
|
29
|
+
@mutex = Mutex.new
|
|
30
|
+
@closed = false
|
|
31
|
+
|
|
32
|
+
start_flush_thread
|
|
33
|
+
install_at_exit
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Capture an error event.
|
|
37
|
+
#
|
|
38
|
+
# @param error_type [String] The error class name
|
|
39
|
+
# @param message [String] Human-readable error message
|
|
40
|
+
# @param source [String] Source platform (default: "ruby")
|
|
41
|
+
# @param stack [String] Stack trace
|
|
42
|
+
# @param route_or_procedure [String] Route or method
|
|
43
|
+
# @param screen [String] Screen name (mobile)
|
|
44
|
+
# @param metadata [Hash] Arbitrary metadata
|
|
45
|
+
def capture(error_type:, message:, source: "ruby", stack: "", route_or_procedure: "", screen: "", metadata: nil, **kwargs)
|
|
46
|
+
return if @closed
|
|
47
|
+
|
|
48
|
+
event = {
|
|
49
|
+
timestamp: (Time.now.to_f * 1000).to_i,
|
|
50
|
+
source: source,
|
|
51
|
+
environment: @environment,
|
|
52
|
+
error_type: error_type,
|
|
53
|
+
message: message,
|
|
54
|
+
}
|
|
55
|
+
event[:release] = @release unless @release.empty?
|
|
56
|
+
event[:stack] = stack unless stack.empty?
|
|
57
|
+
event[:route_or_procedure] = route_or_procedure unless route_or_procedure.empty?
|
|
58
|
+
event[:screen] = screen unless screen.empty?
|
|
59
|
+
event[:metadata] = metadata if metadata
|
|
60
|
+
|
|
61
|
+
kwargs.each { |k, v| event[k] = v unless event.key?(k) }
|
|
62
|
+
|
|
63
|
+
@mutex.synchronize do
|
|
64
|
+
@buffer << event
|
|
65
|
+
flush_locked if @buffer.size >= @max_buffer_size
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Capture a Ruby exception.
|
|
70
|
+
#
|
|
71
|
+
# @param exception [Exception] The exception to capture
|
|
72
|
+
def capture_exception(exception, **kwargs)
|
|
73
|
+
capture(
|
|
74
|
+
error_type: exception.class.name,
|
|
75
|
+
message: exception.message,
|
|
76
|
+
stack: (exception.backtrace || []).join("\n"),
|
|
77
|
+
**kwargs
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Flush buffered events immediately.
|
|
82
|
+
def flush
|
|
83
|
+
@mutex.synchronize { flush_locked }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Flush and stop the background thread.
|
|
87
|
+
def close
|
|
88
|
+
@closed = true
|
|
89
|
+
flush
|
|
90
|
+
@flush_thread&.kill
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def flush_locked
|
|
96
|
+
return if @buffer.empty?
|
|
97
|
+
|
|
98
|
+
events = @buffer.dup
|
|
99
|
+
@buffer.clear
|
|
100
|
+
|
|
101
|
+
Thread.new { send_events(events) }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def send_events(events)
|
|
105
|
+
if events.size == 1
|
|
106
|
+
path = "/v1/ingest"
|
|
107
|
+
body = JSON.generate(events.first)
|
|
108
|
+
else
|
|
109
|
+
path = "/v1/ingest/batch"
|
|
110
|
+
body = JSON.generate({ events: events })
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
signature = OpenSSL::HMAC.hexdigest("SHA256", @project_key, body)
|
|
114
|
+
|
|
115
|
+
uri = URI("#{@endpoint}#{path}")
|
|
116
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
117
|
+
http.use_ssl = (uri.scheme == "https")
|
|
118
|
+
http.open_timeout = 5
|
|
119
|
+
http.read_timeout = 10
|
|
120
|
+
|
|
121
|
+
req = Net::HTTP::Post.new(uri.path)
|
|
122
|
+
req["Content-Type"] = "application/json"
|
|
123
|
+
req["X-Signature"] = signature
|
|
124
|
+
req["X-Project-Key"] = @project_key
|
|
125
|
+
req.body = body
|
|
126
|
+
|
|
127
|
+
http.request(req)
|
|
128
|
+
rescue StandardError
|
|
129
|
+
# Fire and forget — don't crash the host app
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def start_flush_thread
|
|
133
|
+
@flush_thread = Thread.new do
|
|
134
|
+
loop do
|
|
135
|
+
sleep @flush_interval
|
|
136
|
+
flush unless @closed
|
|
137
|
+
rescue StandardError
|
|
138
|
+
# Ignore flush errors
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
@flush_thread.abort_on_exception = false
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def install_at_exit
|
|
145
|
+
client = self
|
|
146
|
+
at_exit { client.close }
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: bloop-sdk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- bloop
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-02-13 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Capture and send error events to a bloop server. Zero external dependencies.
|
|
14
|
+
email:
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- lib/bloop.rb
|
|
20
|
+
homepage: https://github.com/your-org/bloop
|
|
21
|
+
licenses:
|
|
22
|
+
- MIT
|
|
23
|
+
metadata:
|
|
24
|
+
homepage_uri: https://github.com/your-org/bloop
|
|
25
|
+
source_code_uri: https://github.com/your-org/bloop/tree/main/sdks/ruby
|
|
26
|
+
post_install_message:
|
|
27
|
+
rdoc_options: []
|
|
28
|
+
require_paths:
|
|
29
|
+
- lib
|
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - ">="
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '3.0'
|
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
requirements: []
|
|
41
|
+
rubygems_version: 3.5.22
|
|
42
|
+
signing_key:
|
|
43
|
+
specification_version: 4
|
|
44
|
+
summary: Ruby SDK for bloop error observability
|
|
45
|
+
test_files: []
|