logister-ruby 0.5.1 → 0.6.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 +4 -4
- data/CHANGELOG.md +21 -0
- data/README.md +103 -6
- data/lib/generators/logister/templates/logister.rb +8 -0
- data/lib/logister/active_job_reporter.rb +11 -5
- data/lib/logister/client.rb +165 -40
- data/lib/logister/configuration.rb +8 -2
- data/lib/logister/context_helpers.rb +7 -7
- data/lib/logister/context_store.rb +10 -0
- data/lib/logister/event_sanitizer.rb +210 -0
- data/lib/logister/middleware.rb +114 -21
- data/lib/logister/railtie.rb +8 -0
- data/lib/logister/redaction.rb +127 -0
- data/lib/logister/reporter.rb +28 -24
- data/lib/logister/request_subscriber.rb +3 -11
- data/lib/logister/sql_subscriber.rb +2 -2
- data/lib/logister/version.rb +1 -1
- data/lib/logister.rb +72 -25
- metadata +4 -2
data/lib/logister/reporter.rb
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require 'digest'
|
|
4
4
|
require 'time'
|
|
5
|
-
require 'set'
|
|
6
5
|
require 'securerandom'
|
|
7
6
|
require_relative 'context_helpers'
|
|
8
7
|
require_relative 'context_store'
|
|
@@ -35,12 +34,6 @@ module Logister
|
|
|
35
34
|
# always returns a new String — do it exactly once here.
|
|
36
35
|
app_root = Dir.pwd.to_s.freeze
|
|
37
36
|
@app_root_re = /\A#{Regexp.escape(app_root)}\//.freeze
|
|
38
|
-
|
|
39
|
-
# Register shutdown hook. Guard with a flag so multiple Reporter instances
|
|
40
|
-
# (created by repeated Logister.configure calls) each shut down cleanly
|
|
41
|
-
# without re-registering more handlers.
|
|
42
|
-
@shutdown_registered = false
|
|
43
|
-
register_shutdown_hook
|
|
44
37
|
end
|
|
45
38
|
|
|
46
39
|
def report_error(exception, context: {}, tags: {}, level: 'error', fingerprint: nil)
|
|
@@ -67,6 +60,8 @@ module Logister
|
|
|
67
60
|
return false unless payload
|
|
68
61
|
|
|
69
62
|
@client.publish(payload)
|
|
63
|
+
rescue StandardError => e
|
|
64
|
+
internal_failure(e)
|
|
70
65
|
end
|
|
71
66
|
|
|
72
67
|
def report_metric(message:, value: nil, unit: nil, level: 'info', context: {}, tags: {}, fingerprint: nil)
|
|
@@ -99,6 +94,8 @@ module Logister
|
|
|
99
94
|
return false unless payload
|
|
100
95
|
|
|
101
96
|
@client.publish(payload)
|
|
97
|
+
rescue StandardError => e
|
|
98
|
+
internal_failure(e)
|
|
102
99
|
end
|
|
103
100
|
|
|
104
101
|
def report_transaction(name:, duration_ms:, level: 'info', context: {}, tags: {}, fingerprint: nil, status: nil)
|
|
@@ -123,6 +120,8 @@ module Logister
|
|
|
123
120
|
return false unless payload
|
|
124
121
|
|
|
125
122
|
@client.publish(payload)
|
|
123
|
+
rescue StandardError => e
|
|
124
|
+
internal_failure(e)
|
|
126
125
|
end
|
|
127
126
|
|
|
128
127
|
def report_span(
|
|
@@ -186,6 +185,8 @@ module Logister
|
|
|
186
185
|
return false unless payload
|
|
187
186
|
|
|
188
187
|
@client.publish(payload)
|
|
188
|
+
rescue StandardError => e
|
|
189
|
+
internal_failure(e)
|
|
189
190
|
end
|
|
190
191
|
|
|
191
192
|
def report_log(message:, level: 'info', context: {}, tags: {}, fingerprint: nil)
|
|
@@ -205,6 +206,8 @@ module Logister
|
|
|
205
206
|
return false unless payload
|
|
206
207
|
|
|
207
208
|
@client.publish(payload)
|
|
209
|
+
rescue StandardError => e
|
|
210
|
+
internal_failure(e)
|
|
208
211
|
end
|
|
209
212
|
|
|
210
213
|
def report_check_in(
|
|
@@ -245,6 +248,8 @@ module Logister
|
|
|
245
248
|
return false unless payload
|
|
246
249
|
|
|
247
250
|
@client.publish(payload)
|
|
251
|
+
rescue StandardError => e
|
|
252
|
+
internal_failure(e)
|
|
248
253
|
end
|
|
249
254
|
|
|
250
255
|
def record_deployment(
|
|
@@ -281,20 +286,24 @@ module Logister
|
|
|
281
286
|
}.compact
|
|
282
287
|
|
|
283
288
|
@client.publish_deployment(payload)
|
|
289
|
+
rescue StandardError => e
|
|
290
|
+
internal_failure(e)
|
|
284
291
|
end
|
|
285
292
|
|
|
286
|
-
# Store user info
|
|
287
|
-
#
|
|
293
|
+
# Store user info in the current request scope so it is attached to every
|
|
294
|
+
# error reported during this request or job. The middleware and Active Job
|
|
295
|
+
# integration reset the scope, so identity never carries to the next unit of
|
|
296
|
+
# work on a reused thread.
|
|
288
297
|
#
|
|
289
298
|
# Logister.set_user(id: current_user.id, email: current_user.email, name: current_user.name)
|
|
290
299
|
#
|
|
291
300
|
def set_user(id: nil, email: nil, name: nil, **extra)
|
|
292
301
|
ctx = { id: id, email: email, name: name }.merge(extra).compact
|
|
293
|
-
|
|
302
|
+
Logister::ContextStore.user = ctx.empty? ? nil : ctx
|
|
294
303
|
end
|
|
295
304
|
|
|
296
305
|
def clear_user
|
|
297
|
-
|
|
306
|
+
Logister::ContextStore.user = nil
|
|
298
307
|
end
|
|
299
308
|
|
|
300
309
|
def flush(timeout: 2)
|
|
@@ -311,18 +320,13 @@ module Logister
|
|
|
311
320
|
|
|
312
321
|
private
|
|
313
322
|
|
|
314
|
-
def
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
@shutdown_registered = true
|
|
318
|
-
# Capture @client directly (not self) so the at_exit proc does not
|
|
319
|
-
# retain the entire Reporter in the finalizer chain.
|
|
320
|
-
client = @client
|
|
321
|
-
at_exit { client.shutdown }
|
|
323
|
+
def current_user_context
|
|
324
|
+
Logister::ContextStore.user
|
|
322
325
|
end
|
|
323
326
|
|
|
324
|
-
def
|
|
325
|
-
|
|
327
|
+
def internal_failure(error)
|
|
328
|
+
Logister.log_internal_error('report', error)
|
|
329
|
+
false
|
|
326
330
|
end
|
|
327
331
|
|
|
328
332
|
def default_error_context
|
|
@@ -335,7 +339,7 @@ module Logister
|
|
|
335
339
|
.merge(Logister::ContextHelpers.deployment_context)
|
|
336
340
|
)
|
|
337
341
|
rescue StandardError => e
|
|
338
|
-
|
|
342
|
+
Logister.log_internal_error('default error context', e)
|
|
339
343
|
{}
|
|
340
344
|
end
|
|
341
345
|
|
|
@@ -360,7 +364,7 @@ module Logister
|
|
|
360
364
|
# Merge static config context last so caller-supplied keys are not
|
|
361
365
|
# overwritten, then merge the static values. The static_context Hash
|
|
362
366
|
# is frozen and reused — only the new outer Hash is allocated.
|
|
363
|
-
context: @static_context.merge(Logister::ContextStore.trace_context
|
|
367
|
+
context: @static_context.merge(Logister::ContextStore.trace_context.to_h).merge(context)
|
|
364
368
|
}
|
|
365
369
|
end
|
|
366
370
|
|
|
@@ -377,7 +381,7 @@ module Logister
|
|
|
377
381
|
|
|
378
382
|
result
|
|
379
383
|
rescue StandardError => e
|
|
380
|
-
|
|
384
|
+
Logister.log_internal_error('before_notify', e)
|
|
381
385
|
nil
|
|
382
386
|
end
|
|
383
387
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require "logger"
|
|
2
|
-
|
|
3
1
|
module Logister
|
|
4
2
|
class RequestSubscriber
|
|
5
3
|
IGNORED_SQL_NAMES = %w[SCHEMA TRANSACTION].freeze
|
|
@@ -57,7 +55,7 @@ module Logister
|
|
|
57
55
|
|
|
58
56
|
report_request_span(payload, request_id) if capture_request_spans?
|
|
59
57
|
rescue StandardError => e
|
|
60
|
-
|
|
58
|
+
Logister.log_internal_error("request subscriber (process_action)", e)
|
|
61
59
|
end
|
|
62
60
|
|
|
63
61
|
def report_request_span(payload, request_id)
|
|
@@ -110,11 +108,11 @@ module Logister
|
|
|
110
108
|
message: "#{sql_name} query",
|
|
111
109
|
data: {
|
|
112
110
|
durationMs: duration_ms,
|
|
113
|
-
sql: payload[:sql]
|
|
111
|
+
sql: Logister::Redaction.strip_sql_literals(payload[:sql])[0, 250]
|
|
114
112
|
}
|
|
115
113
|
)
|
|
116
114
|
rescue StandardError => e
|
|
117
|
-
|
|
115
|
+
Logister.log_internal_error("request subscriber (sql breadcrumb)", e)
|
|
118
116
|
end
|
|
119
117
|
|
|
120
118
|
def numeric(value)
|
|
@@ -136,12 +134,6 @@ module Logister
|
|
|
136
134
|
rescue StandardError
|
|
137
135
|
false
|
|
138
136
|
end
|
|
139
|
-
|
|
140
|
-
def logger
|
|
141
|
-
configuration&.logger || Logger.new($stdout)
|
|
142
|
-
rescue StandardError
|
|
143
|
-
Logger.new($stdout)
|
|
144
|
-
end
|
|
145
137
|
end
|
|
146
138
|
end
|
|
147
139
|
end
|
|
@@ -53,14 +53,14 @@ module Logister
|
|
|
53
53
|
context: {
|
|
54
54
|
duration_ms: duration_ms.round(2),
|
|
55
55
|
name: sql_name,
|
|
56
|
-
sql: payload[:sql]
|
|
56
|
+
sql: Logister::Redaction.strip_sql_literals(payload[:sql]),
|
|
57
57
|
cached: false,
|
|
58
58
|
binds_count: (payload[:binds] || []).size
|
|
59
59
|
},
|
|
60
60
|
tags: TAGS
|
|
61
61
|
)
|
|
62
62
|
rescue StandardError => e
|
|
63
|
-
|
|
63
|
+
Logister.log_internal_error('sql subscriber', e)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def sampled_out?(sample_rate)
|
data/lib/logister/version.rb
CHANGED
data/lib/logister.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
|
1
2
|
require_relative 'logister/version'
|
|
2
3
|
require_relative 'logister/configuration'
|
|
4
|
+
require_relative 'logister/redaction'
|
|
5
|
+
require_relative 'logister/event_sanitizer'
|
|
3
6
|
require_relative 'logister/reporting_scope'
|
|
4
7
|
require_relative 'logister/client'
|
|
5
8
|
require_relative 'logister/reporter'
|
|
@@ -26,12 +29,18 @@ module Logister
|
|
|
26
29
|
@configuration ||= Configuration.new
|
|
27
30
|
end
|
|
28
31
|
|
|
32
|
+
# Reconfiguring replaces the reporter. The previous client drains what it
|
|
33
|
+
# already queued and stops, so repeated calls never leave idle workers.
|
|
29
34
|
def configure
|
|
30
35
|
yield(configuration)
|
|
36
|
+
previous = @reporter
|
|
31
37
|
@reporter = nil
|
|
38
|
+
safely(nil) { previous&.shutdown }
|
|
39
|
+
register_shutdown_hook
|
|
32
40
|
end
|
|
33
41
|
|
|
34
42
|
def reporter
|
|
43
|
+
register_shutdown_hook
|
|
35
44
|
@reporter ||= Reporter.new(configuration)
|
|
36
45
|
end
|
|
37
46
|
|
|
@@ -44,64 +53,102 @@ module Logister
|
|
|
44
53
|
end
|
|
45
54
|
|
|
46
55
|
def report_error(exception, **kwargs)
|
|
47
|
-
reporter.report_error(exception, **kwargs)
|
|
56
|
+
safely { reporter.report_error(exception, **kwargs) }
|
|
48
57
|
end
|
|
49
58
|
|
|
50
59
|
def report_metric(**kwargs)
|
|
51
|
-
reporter.report_metric(**kwargs)
|
|
60
|
+
safely { reporter.report_metric(**kwargs) }
|
|
52
61
|
end
|
|
53
62
|
|
|
54
63
|
def report_transaction(**kwargs)
|
|
55
|
-
reporter.report_transaction(**kwargs)
|
|
64
|
+
safely { reporter.report_transaction(**kwargs) }
|
|
56
65
|
end
|
|
57
66
|
|
|
58
67
|
def report_span(**kwargs)
|
|
59
|
-
reporter.report_span(**kwargs)
|
|
68
|
+
safely { reporter.report_span(**kwargs) }
|
|
60
69
|
end
|
|
61
70
|
|
|
62
71
|
def report_log(**kwargs)
|
|
63
|
-
reporter.report_log(**kwargs)
|
|
72
|
+
safely { reporter.report_log(**kwargs) }
|
|
64
73
|
end
|
|
65
74
|
|
|
66
75
|
def report_check_in(**kwargs)
|
|
67
|
-
reporter.report_check_in(**kwargs)
|
|
76
|
+
safely { reporter.report_check_in(**kwargs) }
|
|
68
77
|
end
|
|
69
78
|
|
|
70
79
|
def record_deployment(**kwargs)
|
|
71
|
-
reporter.record_deployment(**kwargs)
|
|
80
|
+
safely { reporter.record_deployment(**kwargs) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Attaches user identity to errors reported in the current request or job.
|
|
84
|
+
# The Rack middleware and Active Job integration clear it when the request
|
|
85
|
+
# or job ends; call clear_user yourself in other long-lived threads.
|
|
86
|
+
def set_user(**kwargs)
|
|
87
|
+
safely(nil) { reporter.set_user(**kwargs) }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def clear_user
|
|
91
|
+
safely(nil) { reporter.clear_user }
|
|
72
92
|
end
|
|
73
93
|
|
|
74
94
|
def flush(timeout: 2)
|
|
75
|
-
reporter.flush(timeout: timeout)
|
|
95
|
+
safely { reporter.flush(timeout: timeout) }
|
|
76
96
|
end
|
|
77
97
|
|
|
78
98
|
def delivery_stats
|
|
79
|
-
reporter.delivery_stats
|
|
99
|
+
safely({}) { reporter.delivery_stats }
|
|
80
100
|
end
|
|
81
101
|
|
|
82
102
|
def shutdown
|
|
83
|
-
reporter.shutdown
|
|
103
|
+
safely { reporter.shutdown }
|
|
84
104
|
end
|
|
85
105
|
|
|
86
106
|
def add_breadcrumb(category:, message:, data: {}, level: "info")
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
107
|
+
safely(nil) do
|
|
108
|
+
ContextStore.add_manual_breadcrumb(
|
|
109
|
+
category: category,
|
|
110
|
+
message: message,
|
|
111
|
+
data: data,
|
|
112
|
+
level: level
|
|
113
|
+
)
|
|
114
|
+
end
|
|
93
115
|
end
|
|
94
116
|
|
|
95
117
|
def add_dependency(name:, host: nil, method: nil, status: nil, duration_ms: nil, kind: nil, data: {})
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
118
|
+
safely(nil) do
|
|
119
|
+
ContextStore.add_manual_dependency(
|
|
120
|
+
name: name,
|
|
121
|
+
host: host,
|
|
122
|
+
method: method,
|
|
123
|
+
status: status,
|
|
124
|
+
duration_ms: duration_ms,
|
|
125
|
+
kind: kind,
|
|
126
|
+
data: data
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Telemetry must never raise into the host application.
|
|
132
|
+
def log_internal_error(label, error)
|
|
133
|
+
configuration.logger&.warn("logister #{label} failed: #{error.class}")
|
|
134
|
+
rescue StandardError
|
|
135
|
+
nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
def safely(fallback = false)
|
|
141
|
+
yield
|
|
142
|
+
rescue StandardError => e
|
|
143
|
+
log_internal_error('call', e)
|
|
144
|
+
fallback
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def register_shutdown_hook
|
|
148
|
+
return if @shutdown_hook_registered
|
|
149
|
+
|
|
150
|
+
@shutdown_hook_registered = true
|
|
151
|
+
at_exit { safely(nil) { @reporter&.shutdown } }
|
|
105
152
|
end
|
|
106
153
|
end
|
|
107
154
|
end
|
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
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Logister
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-10-24 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activesupport
|
|
@@ -123,8 +123,10 @@ files:
|
|
|
123
123
|
- lib/logister/configuration.rb
|
|
124
124
|
- lib/logister/context_helpers.rb
|
|
125
125
|
- lib/logister/context_store.rb
|
|
126
|
+
- lib/logister/event_sanitizer.rb
|
|
126
127
|
- lib/logister/middleware.rb
|
|
127
128
|
- lib/logister/railtie.rb
|
|
129
|
+
- lib/logister/redaction.rb
|
|
128
130
|
- lib/logister/reporter.rb
|
|
129
131
|
- lib/logister/reporting_scope.rb
|
|
130
132
|
- lib/logister/request_subscriber.rb
|