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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/bloop.rb +149 -0
  3. 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: []