logister-ruby 0.1.2 → 0.2.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/lib/logister/middleware.rb +56 -3
- data/lib/logister/reporter.rb +52 -2
- data/lib/logister/version.rb +1 -1
- data/lib/logister.rb +8 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2f3f9f67b976e92e535974c8a243b14f011088bca57e5ab3dc0385de6ba4c003
|
|
4
|
+
data.tar.gz: d744d4996b00d27a2e51e54b5b91134f4c0ae049c76270a41c7ef93deec04189
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9a8dbbbdde7778369654c287f9076a89efaadf5a3d03bdd5cb064546bac2b2b72bd53d641b424d3b4bc3cefecfce0bfd1b0a9c50714a8c58506b21c6765b8902
|
|
7
|
+
data.tar.gz: 823a8c724d3aee752558e9936c23f924c774157c83a7b997885ba19a8dbf6aa835277506101f2eea341dde05923353f58127f6b94c0d8cbb6711823c5f43c946
|
data/lib/logister/middleware.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'socket'
|
|
2
|
+
|
|
1
3
|
module Logister
|
|
2
4
|
class Middleware
|
|
3
5
|
def initialize(app)
|
|
@@ -10,12 +12,63 @@ module Logister
|
|
|
10
12
|
Logister.report_error(
|
|
11
13
|
e,
|
|
12
14
|
context: {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
method: env['REQUEST_METHOD']
|
|
15
|
+
request: build_request_context(env),
|
|
16
|
+
app: build_app_context
|
|
16
17
|
}
|
|
17
18
|
)
|
|
18
19
|
raise
|
|
19
20
|
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def build_request_context(env)
|
|
25
|
+
ctx = {
|
|
26
|
+
id: env['action_dispatch.request_id'],
|
|
27
|
+
path: env['PATH_INFO'],
|
|
28
|
+
method: env['REQUEST_METHOD'],
|
|
29
|
+
ip: remote_ip(env),
|
|
30
|
+
user_agent: env['HTTP_USER_AGENT']
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Params — available if ActionDispatch has already parsed them
|
|
34
|
+
if (params = env['action_dispatch.request.parameters'])
|
|
35
|
+
ctx[:params] = filter_params(params)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
ctx.compact
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def build_app_context
|
|
42
|
+
ctx = {
|
|
43
|
+
ruby: RUBY_VERSION,
|
|
44
|
+
hostname: hostname
|
|
45
|
+
}
|
|
46
|
+
ctx[:rails] = Rails::VERSION::STRING if defined?(Rails::VERSION)
|
|
47
|
+
ctx
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Respect X-Forwarded-For set by proxies, fall back to REMOTE_ADDR
|
|
51
|
+
def remote_ip(env)
|
|
52
|
+
forwarded = env['HTTP_X_FORWARDED_FOR'].to_s.split(',').first&.strip
|
|
53
|
+
forwarded.nil? || forwarded.empty? ? env['REMOTE_ADDR'] : forwarded
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Remove sensitive parameter values the same way Rails does
|
|
57
|
+
SENSITIVE_PARAMS = %w[password password_confirmation token secret api_key
|
|
58
|
+
credit_card cvv ssn].freeze
|
|
59
|
+
|
|
60
|
+
def filter_params(params)
|
|
61
|
+
params.each_with_object({}) do |(k, v), h|
|
|
62
|
+
h[k] = SENSITIVE_PARAMS.any? { |s| k.to_s.downcase.include?(s) } ? '[FILTERED]' : v
|
|
63
|
+
end
|
|
64
|
+
rescue StandardError
|
|
65
|
+
{}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def hostname
|
|
69
|
+
Socket.gethostname
|
|
70
|
+
rescue StandardError
|
|
71
|
+
'unknown'
|
|
72
|
+
end
|
|
20
73
|
end
|
|
21
74
|
end
|
data/lib/logister/reporter.rb
CHANGED
|
@@ -14,12 +14,15 @@ module Logister
|
|
|
14
14
|
return false if ignored_exception?(exception)
|
|
15
15
|
return false if ignored_path?(context)
|
|
16
16
|
|
|
17
|
+
merged_context = context.dup
|
|
18
|
+
merged_context[:user] = current_user_context if current_user_context
|
|
19
|
+
|
|
17
20
|
payload = build_payload(
|
|
18
21
|
event_type: 'error',
|
|
19
22
|
level: level,
|
|
20
23
|
message: "#{exception.class}: #{exception.message}",
|
|
21
24
|
fingerprint: fingerprint || default_fingerprint(exception),
|
|
22
|
-
context:
|
|
25
|
+
context: merged_context.merge(
|
|
23
26
|
exception: {
|
|
24
27
|
class: exception.class.to_s,
|
|
25
28
|
message: exception.message.to_s,
|
|
@@ -53,6 +56,20 @@ module Logister
|
|
|
53
56
|
@client.publish(payload)
|
|
54
57
|
end
|
|
55
58
|
|
|
59
|
+
# Store user info for the current thread so it is automatically attached to
|
|
60
|
+
# every error reported during this request.
|
|
61
|
+
#
|
|
62
|
+
# Logister.set_user(id: current_user.id, email: current_user.email, name: current_user.name)
|
|
63
|
+
#
|
|
64
|
+
def set_user(id: nil, email: nil, name: nil, **extra)
|
|
65
|
+
ctx = { id: id, email: email, name: name }.merge(extra).compact
|
|
66
|
+
Thread.current[:logister_user] = ctx.empty? ? nil : ctx
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def clear_user
|
|
70
|
+
Thread.current[:logister_user] = nil
|
|
71
|
+
end
|
|
72
|
+
|
|
56
73
|
def flush(timeout: 2)
|
|
57
74
|
@client.flush(timeout: timeout)
|
|
58
75
|
end
|
|
@@ -63,6 +80,10 @@ module Logister
|
|
|
63
80
|
|
|
64
81
|
private
|
|
65
82
|
|
|
83
|
+
def current_user_context
|
|
84
|
+
Thread.current[:logister_user]
|
|
85
|
+
end
|
|
86
|
+
|
|
66
87
|
def build_payload(event_type:, level:, message:, fingerprint:, context:)
|
|
67
88
|
{
|
|
68
89
|
event_type: event_type,
|
|
@@ -118,7 +139,36 @@ module Logister
|
|
|
118
139
|
end
|
|
119
140
|
|
|
120
141
|
def default_fingerprint(exception)
|
|
121
|
-
|
|
142
|
+
# Prefer class + first backtrace location so that errors with dynamic
|
|
143
|
+
# values in their message (e.g. "Couldn't find User with 'id'=42") still
|
|
144
|
+
# group together across different IDs / UUIDs.
|
|
145
|
+
location = Array(exception.backtrace).first.to_s
|
|
146
|
+
.sub(/:in\s+.+$/, '') # strip method name
|
|
147
|
+
.sub(/\A.*\/gems\//, 'gems/') # normalise gem paths
|
|
148
|
+
.sub(/\A#{Regexp.escape(Dir.pwd.to_s)}\//, '') # strip app root
|
|
149
|
+
|
|
150
|
+
if location.empty?
|
|
151
|
+
# No backtrace available — scrub common dynamic tokens from the message
|
|
152
|
+
# before hashing so that e.g. "id=42" and "id=99" hash the same way.
|
|
153
|
+
scrubbed = scrub_dynamic_values(exception.message.to_s)
|
|
154
|
+
Digest::SHA256.hexdigest("#{exception.class}|#{scrubbed}")[0, 32]
|
|
155
|
+
else
|
|
156
|
+
Digest::SHA256.hexdigest("#{exception.class}|#{location}")[0, 32]
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Strip values that tend to vary per-occurrence but carry no grouping signal:
|
|
161
|
+
# - numeric IDs: id=42, 'id'=42, id: 42
|
|
162
|
+
# - UUIDs
|
|
163
|
+
# - hex digests (≥8 hex chars)
|
|
164
|
+
# - quoted string values in ActiveRecord-style messages
|
|
165
|
+
def scrub_dynamic_values(message)
|
|
166
|
+
message
|
|
167
|
+
.gsub(/\b(id['"]?\s*[=:]\s*)\d+/i, '\1?')
|
|
168
|
+
.gsub(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i, '?')
|
|
169
|
+
.gsub(/\b[0-9a-f]{8,}\b/, '?')
|
|
170
|
+
.gsub(/'[^']{1,64}'/, '?')
|
|
171
|
+
.gsub(/\d+/, '?')
|
|
122
172
|
end
|
|
123
173
|
end
|
|
124
174
|
end
|
data/lib/logister/version.rb
CHANGED
data/lib/logister.rb
CHANGED
|
@@ -28,6 +28,14 @@ module Logister
|
|
|
28
28
|
reporter.report_metric(**kwargs)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
def set_user(id: nil, email: nil, name: nil, **extra)
|
|
32
|
+
reporter.set_user(id: id, email: email, name: name, **extra)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def clear_user
|
|
36
|
+
reporter.clear_user
|
|
37
|
+
end
|
|
38
|
+
|
|
31
39
|
def flush(timeout: 2)
|
|
32
40
|
reporter.flush(timeout: timeout)
|
|
33
41
|
end
|