logister-ruby 0.4.1 → 0.4.2
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 +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +18 -0
- data/lib/logister/client.rb +75 -16
- data/lib/logister/configuration.rb +2 -1
- data/lib/logister/reporter.rb +4 -0
- data/lib/logister/version.rb +1 -1
- data/lib/logister.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 381dffab29b34b6beffe54e105fcf8dc5151f59bde1e44cfbba1749d21bff763
|
|
4
|
+
data.tar.gz: 5fe2ef7923ebeb4aa440d4aff35b246b2d407da0fe11dd26e26633e491105657
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a5380bdc6bc4c7b4c1e4cb9e2c0d9d4cc4825f87d3517b57c22db7e63fff519071e46e9ad3555d3035819f4e1a64819d26305a1646f18cb32c02977cff925da2
|
|
7
|
+
data.tar.gz: b3d7c3a5932f560be7ec3b1dc1a88a2319799e2b166e91d8a6d6084e8d6d7eaf262c047c5d7f9ba3e095d44dc857d1ed701605ced120a2253a168d3568cc5687
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.4.2 - 2026-09-16
|
|
4
|
+
|
|
5
|
+
- Added process-local delivery counters and an optional recursion-safe observer for queued, acknowledged, unconfirmed, queue-full, and retry outcomes without exporting event contents.
|
|
6
|
+
- Clarified that a drained queue or successful `flush` does not prove every event was acknowledged; timed-out requests may already have reached the server.
|
|
7
|
+
- Removed exception messages from terminal event-publish warnings to avoid exposing transport details.
|
|
8
|
+
- Bounded async shutdown even when the queue is full; retained the worker until it finishes and report incomplete shutdown honestly.
|
|
9
|
+
|
|
3
10
|
## v0.4.1 - 2026-09-11
|
|
4
11
|
|
|
5
12
|
- Constrained JSON to the compatible 2.x line while supported ActiveSupport 8 releases use positional JSON.parse options; verified real Rails request decoding.
|
data/README.md
CHANGED
|
@@ -162,6 +162,24 @@ safe against a Logister server that supports the batch endpoint. Older servers a
|
|
|
162
162
|
detected automatically and receive the same stable events through the single-event
|
|
163
163
|
endpoint. Call `Logister.flush` before a short-lived process exits.
|
|
164
164
|
|
|
165
|
+
### Delivery outcomes
|
|
166
|
+
|
|
167
|
+
`flush` waits for queued and in-flight attempts to finish; it does not guarantee
|
|
168
|
+
server acknowledgement. Inspect `Logister.delivery_stats` for process-local
|
|
169
|
+
`queued_events`, `acknowledged_events`, `unconfirmed_events`, `queue_full_events`,
|
|
170
|
+
`retry_attempts`, and `pending_events`. An unconfirmed event may already have
|
|
171
|
+
reached the server when its response timed out. Counts are not durable and reset
|
|
172
|
+
with the client/process; they cover events, not deployment reports.
|
|
173
|
+
|
|
174
|
+
For local delivery diagnostics, set `config.delivery_observer` to a fast callback
|
|
175
|
+
accepting `{ outcome:, count:, reason: }`. It receives no payload, identifiers,
|
|
176
|
+
credentials, or exception messages. Reporting is suppressed inside the callback
|
|
177
|
+
to prevent feedback loops; callback failure cannot change delivery. Write these
|
|
178
|
+
diagnostics to a local log or counter rather than publishing them through Logister.
|
|
179
|
+
`shutdown` waits at most one second for the worker and returns false if attempts
|
|
180
|
+
are still running. It stops accepting asynchronous events and does not kill an
|
|
181
|
+
in-flight request or block while adding a sentinel to a full queue.
|
|
182
|
+
|
|
165
183
|
Every HTTP attempt applies `timeout_seconds` to connect, read, and write operations.
|
|
166
184
|
Retryable responses honor `Retry-After` when present, cap any individual wait at
|
|
167
185
|
`max_retry_delay`, and add bounded positive jitter controlled by `retry_jitter`.
|
data/lib/logister/client.rb
CHANGED
|
@@ -32,9 +32,13 @@ module Logister
|
|
|
32
32
|
@queue = SizedQueue.new(@configuration.queue_size)
|
|
33
33
|
@worker = nil
|
|
34
34
|
@running = false
|
|
35
|
+
@stopping = false
|
|
35
36
|
@pending_mutex = Mutex.new
|
|
36
37
|
@pending_condition = ConditionVariable.new
|
|
37
38
|
@pending_count = 0
|
|
39
|
+
@delivery_mutex = Mutex.new
|
|
40
|
+
@delivery_counts = { queued_events: 0, acknowledged_events: 0, unconfirmed_events: 0,
|
|
41
|
+
queue_full_events: 0, retry_attempts: 0 }
|
|
38
42
|
|
|
39
43
|
# Cache values that are static for the lifetime of this client so we
|
|
40
44
|
# don't allocate on every send_request call.
|
|
@@ -48,6 +52,7 @@ module Logister
|
|
|
48
52
|
end
|
|
49
53
|
|
|
50
54
|
def publish(payload)
|
|
55
|
+
return false if ReportingScope.suppressed?
|
|
51
56
|
return false unless ready?
|
|
52
57
|
|
|
53
58
|
payload = with_stable_uuid(payload)
|
|
@@ -83,25 +88,40 @@ module Logister
|
|
|
83
88
|
def shutdown
|
|
84
89
|
return true unless @configuration.async
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
worker = @worker_mutex.synchronize do
|
|
92
|
+
@stopping = true
|
|
93
|
+
begin
|
|
94
|
+
@queue.push(nil, true)
|
|
95
|
+
rescue ThreadError
|
|
96
|
+
# A full queue will be drained by the worker, which also checks the
|
|
97
|
+
# stopping flag. Never block the caller waiting to enqueue a sentinel.
|
|
98
|
+
end
|
|
99
|
+
@worker
|
|
91
100
|
end
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
101
|
+
worker&.join(1)
|
|
102
|
+
!worker&.alive?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Process-local counters, never an assertion of exactly-once server receipt.
|
|
106
|
+
def delivery_stats
|
|
107
|
+
@delivery_mutex.synchronize { @delivery_counts.dup }
|
|
108
|
+
.merge(pending_events: @pending_mutex.synchronize { @pending_count })
|
|
95
109
|
end
|
|
96
110
|
|
|
97
111
|
private
|
|
98
112
|
|
|
99
113
|
def enqueue(payload)
|
|
100
|
-
|
|
101
|
-
|
|
114
|
+
@worker_mutex.synchronize do
|
|
115
|
+
return false if @stopping
|
|
116
|
+
|
|
117
|
+
increment_pending
|
|
118
|
+
@queue.push(payload, true)
|
|
119
|
+
end
|
|
120
|
+
record_delivery(:queued_events, 1)
|
|
102
121
|
true
|
|
103
122
|
rescue ThreadError
|
|
104
123
|
complete_pending(1)
|
|
124
|
+
record_delivery(:queue_full_events, 1, reason: 'queue_full')
|
|
105
125
|
@configuration.logger.warn('logister queue full; dropping event')
|
|
106
126
|
false
|
|
107
127
|
end
|
|
@@ -111,6 +131,7 @@ module Logister
|
|
|
111
131
|
return if @running && @worker&.alive?
|
|
112
132
|
|
|
113
133
|
@worker_mutex.synchronize do
|
|
134
|
+
return if @stopping
|
|
114
135
|
return if @running && @worker&.alive?
|
|
115
136
|
|
|
116
137
|
@running = true
|
|
@@ -144,7 +165,7 @@ module Logister
|
|
|
144
165
|
|
|
145
166
|
publish_batch_sync(batch)
|
|
146
167
|
complete_pending(batch.length)
|
|
147
|
-
break if stop_after_batch
|
|
168
|
+
break if stop_after_batch || (@stopping && @queue.empty?)
|
|
148
169
|
end
|
|
149
170
|
rescue StandardError => e
|
|
150
171
|
@configuration.logger.warn("logister worker crashed: #{e.class} #{e.message}")
|
|
@@ -158,14 +179,22 @@ module Logister
|
|
|
158
179
|
attempts = 0
|
|
159
180
|
begin
|
|
160
181
|
attempts += 1
|
|
161
|
-
send_request(payload)
|
|
182
|
+
delivered = send_request(payload)
|
|
183
|
+
unless delivered
|
|
184
|
+
record_delivery(:unconfirmed_events, 1, reason: 'no_acknowledgment')
|
|
185
|
+
return false
|
|
186
|
+
end
|
|
187
|
+
record_delivery(:acknowledged_events, 1)
|
|
188
|
+
true
|
|
162
189
|
rescue StandardError => e
|
|
163
190
|
if attempts <= @configuration.max_retries && retryable_error?(e)
|
|
191
|
+
record_delivery(:retry_attempts, 1, reason: failure_reason(e))
|
|
164
192
|
sleep(retry_delay(e, attempts))
|
|
165
193
|
retry
|
|
166
194
|
end
|
|
167
195
|
|
|
168
|
-
|
|
196
|
+
record_delivery(:unconfirmed_events, 1, reason: failure_reason(e))
|
|
197
|
+
@configuration.logger.warn("logister publish failed: #{e.class}")
|
|
169
198
|
false
|
|
170
199
|
end
|
|
171
200
|
end
|
|
@@ -174,7 +203,13 @@ module Logister
|
|
|
174
203
|
attempts = 0
|
|
175
204
|
begin
|
|
176
205
|
attempts += 1
|
|
177
|
-
send_batch_request(payloads)
|
|
206
|
+
delivered = send_batch_request(payloads)
|
|
207
|
+
unless delivered
|
|
208
|
+
record_delivery(:unconfirmed_events, payloads.length, reason: 'no_acknowledgment')
|
|
209
|
+
return false
|
|
210
|
+
end
|
|
211
|
+
record_delivery(:acknowledged_events, payloads.length)
|
|
212
|
+
true
|
|
178
213
|
rescue UnsupportedBatchEndpoint
|
|
179
214
|
payloads.map { |payload| publish_sync(payload) }.all?
|
|
180
215
|
rescue RequestError => e
|
|
@@ -185,19 +220,23 @@ module Logister
|
|
|
185
220
|
return first_half_delivered && second_half_delivered
|
|
186
221
|
end
|
|
187
222
|
if attempts <= @configuration.max_retries && retryable_error?(e)
|
|
223
|
+
record_delivery(:retry_attempts, 1, reason: failure_reason(e))
|
|
188
224
|
sleep(retry_delay(e, attempts))
|
|
189
225
|
retry
|
|
190
226
|
end
|
|
191
227
|
|
|
192
|
-
|
|
228
|
+
record_delivery(:unconfirmed_events, payloads.length, reason: failure_reason(e))
|
|
229
|
+
@configuration.logger.warn("logister batch publish failed: #{e.class}")
|
|
193
230
|
false
|
|
194
231
|
rescue StandardError => e
|
|
195
232
|
if attempts <= @configuration.max_retries && retryable_error?(e)
|
|
233
|
+
record_delivery(:retry_attempts, 1, reason: failure_reason(e))
|
|
196
234
|
sleep(retry_delay(e, attempts))
|
|
197
235
|
retry
|
|
198
236
|
end
|
|
199
237
|
|
|
200
|
-
|
|
238
|
+
record_delivery(:unconfirmed_events, payloads.length, reason: failure_reason(e))
|
|
239
|
+
@configuration.logger.warn("logister batch publish failed: #{e.class}")
|
|
201
240
|
false
|
|
202
241
|
end
|
|
203
242
|
end
|
|
@@ -290,6 +329,26 @@ module Logister
|
|
|
290
329
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
291
330
|
end
|
|
292
331
|
|
|
332
|
+
def failure_reason(error)
|
|
333
|
+
return "http_#{error.status}" if error.is_a?(RequestError)
|
|
334
|
+
return 'timeout' if error.is_a?(Timeout::Error)
|
|
335
|
+
|
|
336
|
+
'transport_error'
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def record_delivery(counter, count, reason: nil)
|
|
340
|
+
@delivery_mutex.synchronize { @delivery_counts[counter] += count }
|
|
341
|
+
observer = @configuration.delivery_observer
|
|
342
|
+
return unless observer
|
|
343
|
+
|
|
344
|
+
ReportingScope.suppress do
|
|
345
|
+
observer.call({ outcome: counter.to_s, count: count, reason: reason }.compact.freeze)
|
|
346
|
+
end
|
|
347
|
+
rescue StandardError
|
|
348
|
+
# Counters survive observer failure; never report this through telemetry.
|
|
349
|
+
nil
|
|
350
|
+
end
|
|
351
|
+
|
|
293
352
|
def with_stable_uuid(payload)
|
|
294
353
|
attributes = payload.to_h.dup
|
|
295
354
|
key = attributes.keys.any? { |candidate| candidate.is_a?(String) } ? 'uuid' : :uuid
|
|
@@ -10,7 +10,7 @@ module Logister
|
|
|
10
10
|
:capture_db_metrics, :db_metric_min_duration_ms, :db_metric_sample_rate,
|
|
11
11
|
:feature_flags_resolver, :dependency_resolver, :anonymize_ip,
|
|
12
12
|
:max_breadcrumbs, :max_dependencies, :capture_request_spans,
|
|
13
|
-
:capture_sql_breadcrumbs, :sql_breadcrumb_min_duration_ms
|
|
13
|
+
:capture_sql_breadcrumbs, :sql_breadcrumb_min_duration_ms, :delivery_observer
|
|
14
14
|
attr_writer :deployment_endpoint, :batch_endpoint
|
|
15
15
|
|
|
16
16
|
def initialize
|
|
@@ -32,6 +32,7 @@ module Logister
|
|
|
32
32
|
@ignore_environments = []
|
|
33
33
|
@ignore_paths = []
|
|
34
34
|
@before_notify = nil
|
|
35
|
+
@delivery_observer = nil
|
|
35
36
|
|
|
36
37
|
@async = true
|
|
37
38
|
@queue_size = 1000
|
data/lib/logister/reporter.rb
CHANGED
data/lib/logister/version.rb
CHANGED
data/lib/logister.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: logister-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Logister
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-09-
|
|
10
|
+
date: 2026-09-16 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activesupport
|