action_reporter 2.0.2 → 3.0.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 +30 -0
- data/lib/action_reporter/current.rb +57 -18
- data/lib/action_reporter/plugin_discovery.rb +25 -22
- data/lib/action_reporter/utils.rb +9 -10
- data/lib/action_reporter/version.rb +1 -1
- data/lib/action_reporter.rb +8 -22
- 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: 883155825258f4bf10900efaa4f5486fa651ddda415e21282ceba8063909fa1b
|
|
4
|
+
data.tar.gz: 7b2fe04f7a946503e516e744a9fa292b8002d6b75a30b69830f79ff272c26d7f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a252069094c66d8c5bf87204101a426c5dec49496065ba18548ab00a0c304932fc247fd32c13e08c34cc276ef1b777937c5456b895b58b6e42235b753c41b557
|
|
7
|
+
data.tar.gz: 9e596e6be356fd140c3137b90952dd8c105907fd17aac78a3e8ec21586eaa074cb760477b9b17198fec246473f3d32cd8abe0eb4691a596562ef742deb847f25
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 3.0.0 (2026-03-13)
|
|
4
|
+
|
|
5
|
+
- BREAKING: Remove backward-compat reporter loading
|
|
6
|
+
- Remove `AVAILABLE_REPORTERS` constant
|
|
7
|
+
- Stop eager requiring built-in reporters at boot
|
|
8
|
+
- Built-in reporters are now loaded lazily through `ActionReporter.available_reporters`
|
|
9
|
+
- Fix built-in reporter constant autoloading
|
|
10
|
+
- Add `autoload` mappings for built-in reporters (`RailsReporter`, `AuditedReporter`, `PaperTrailReporter`, `ActiveVersionReporter`, `SentryReporter`, `HoneybadgerReporter`, `ScoutApmReporter`)
|
|
11
|
+
- Fix `NameError: uninitialized constant ActionReporter::ActiveVersionReporter` when reporters are referenced directly from app initializers
|
|
12
|
+
- Fix custom reporter lazy loading regression
|
|
13
|
+
- `PluginDiscovery.load_registered_reporter` now attempts `require` before constant lookup
|
|
14
|
+
- Registered reporters can now be loaded from `require_path` when class is not preloaded
|
|
15
|
+
- Improve plugin discovery thread safety
|
|
16
|
+
- Synchronize `register` writes with discovery mutex
|
|
17
|
+
- Snapshot registered reporters under lock in `available_reporters` before iteration
|
|
18
|
+
- Remove fragile loaded-feature detection
|
|
19
|
+
- Remove substring-based `$LOADED_FEATURES` matching that could produce false positives
|
|
20
|
+
- Rely on idempotent `require` behavior instead
|
|
21
|
+
- Improve context transformation behavior
|
|
22
|
+
- `Utils.deep_transform_values` now recursively transforms all values, including objects nested directly inside arrays
|
|
23
|
+
- Add configurable current-context storage adapter
|
|
24
|
+
- New `ActionReporter::Current.storage_adapter=` with `#[]` / `#[]=` contract
|
|
25
|
+
- New `ActionReporter::Current.reset_storage_adapter!`
|
|
26
|
+
- Storage resolution order: explicit `storage_adapter` -> `ActiveSupport::IsolatedExecutionState` -> `Thread.current`
|
|
27
|
+
- Enables fiber-aware request storage customization in non-Rails environments
|
|
28
|
+
- Add regression coverage for lazy loading and storage behavior
|
|
29
|
+
- Add spec for loading registered reporter via `require_path` when class is not preloaded
|
|
30
|
+
- Add spec for transforming array-contained objects in deep context transform
|
|
31
|
+
- Add specs for `Current.storage_adapter` validation and precedence
|
|
32
|
+
|
|
3
33
|
## 2.0.2 (2026-03-13)
|
|
4
34
|
|
|
5
35
|
- Add `ActiveVersionReporter` integration for the `active_version` gem
|
|
@@ -2,57 +2,96 @@ require_relative "error"
|
|
|
2
2
|
|
|
3
3
|
module ActionReporter
|
|
4
4
|
# Thread-safe storage for ActionReporter context attributes
|
|
5
|
-
# Uses
|
|
6
|
-
#
|
|
7
|
-
# so we use Thread.current for proper thread isolation
|
|
5
|
+
# Uses ActiveSupport::IsolatedExecutionState when available (fiber/request-aware),
|
|
6
|
+
# and falls back to Thread.current for non-Rails/non-ActiveSupport environments.
|
|
8
7
|
class Current
|
|
8
|
+
STORAGE_PREFIX = :action_reporter
|
|
9
|
+
|
|
9
10
|
class << self
|
|
11
|
+
attr_reader :storage_adapter
|
|
12
|
+
|
|
13
|
+
def storage_adapter=(adapter)
|
|
14
|
+
if adapter && (!adapter.respond_to?(:[]) || !adapter.respond_to?(:[]=))
|
|
15
|
+
raise ArgumentError, "storage_adapter must respond to #[] and #[]="
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@storage_adapter = adapter
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def reset_storage_adapter!
|
|
22
|
+
@storage_adapter = nil
|
|
23
|
+
end
|
|
24
|
+
|
|
10
25
|
def current_user
|
|
11
|
-
|
|
26
|
+
read(:current_user)
|
|
12
27
|
end
|
|
13
28
|
|
|
14
29
|
def current_user=(user)
|
|
15
|
-
|
|
30
|
+
write(:current_user, user)
|
|
16
31
|
end
|
|
17
32
|
|
|
18
33
|
def current_request_uuid
|
|
19
|
-
|
|
34
|
+
read(:current_request_uuid)
|
|
20
35
|
end
|
|
21
36
|
|
|
22
37
|
def current_request_uuid=(uuid)
|
|
23
|
-
|
|
38
|
+
write(:current_request_uuid, uuid)
|
|
24
39
|
end
|
|
25
40
|
|
|
26
41
|
def current_remote_addr
|
|
27
|
-
|
|
42
|
+
read(:current_remote_addr)
|
|
28
43
|
end
|
|
29
44
|
|
|
30
45
|
def current_remote_addr=(addr)
|
|
31
|
-
|
|
46
|
+
write(:current_remote_addr, addr)
|
|
32
47
|
end
|
|
33
48
|
|
|
34
49
|
def transaction_id
|
|
35
|
-
|
|
50
|
+
read(:transaction_id)
|
|
36
51
|
end
|
|
37
52
|
|
|
38
53
|
def transaction_id=(transaction_id)
|
|
39
|
-
|
|
54
|
+
write(:transaction_id, transaction_id)
|
|
40
55
|
end
|
|
41
56
|
|
|
42
57
|
def transaction_name
|
|
43
|
-
|
|
58
|
+
read(:transaction_name)
|
|
44
59
|
end
|
|
45
60
|
|
|
46
61
|
def transaction_name=(transaction_name)
|
|
47
|
-
|
|
62
|
+
write(:transaction_name, transaction_name)
|
|
48
63
|
end
|
|
49
64
|
|
|
50
65
|
def reset
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
write(:current_user, nil)
|
|
67
|
+
write(:current_request_uuid, nil)
|
|
68
|
+
write(:current_remote_addr, nil)
|
|
69
|
+
write(:transaction_id, nil)
|
|
70
|
+
write(:transaction_name, nil)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def storage
|
|
76
|
+
return storage_adapter if storage_adapter
|
|
77
|
+
|
|
78
|
+
if defined?(ActiveSupport::IsolatedExecutionState)
|
|
79
|
+
ActiveSupport::IsolatedExecutionState
|
|
80
|
+
else
|
|
81
|
+
Thread.current
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def key(name)
|
|
86
|
+
:"#{STORAGE_PREFIX}_#{name}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def read(name)
|
|
90
|
+
storage[key(name)]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def write(name, value)
|
|
94
|
+
storage[key(name)] = value
|
|
56
95
|
end
|
|
57
96
|
end
|
|
58
97
|
end
|
|
@@ -28,10 +28,12 @@ module ActionReporter
|
|
|
28
28
|
# @param class_name [String] Fully qualified class name
|
|
29
29
|
# @param require_path [String] Path to require (e.g., "action_reporter/custom_reporter")
|
|
30
30
|
def register(name, class_name:, require_path:)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
discovery_lock.synchronize do
|
|
32
|
+
registered_reporters[name] = {
|
|
33
|
+
class_name: class_name,
|
|
34
|
+
require_path: require_path
|
|
35
|
+
}
|
|
36
|
+
end
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
# Discover reporters from the filesystem (lazy-loaded, cached)
|
|
@@ -66,7 +68,8 @@ module ActionReporter
|
|
|
66
68
|
reporters = discover.dup
|
|
67
69
|
|
|
68
70
|
# Add registered reporters (lazy-loaded)
|
|
69
|
-
registered_reporters.
|
|
71
|
+
registered_configs = discovery_lock.synchronize { registered_reporters.values.dup }
|
|
72
|
+
registered_configs.each do |config|
|
|
70
73
|
reporter_class = load_registered_reporter(config)
|
|
71
74
|
reporters << reporter_class if reporter_class
|
|
72
75
|
rescue => e
|
|
@@ -99,7 +102,15 @@ module ActionReporter
|
|
|
99
102
|
|
|
100
103
|
full_class_name = "ActionReporter::#{class_name}"
|
|
101
104
|
|
|
102
|
-
#
|
|
105
|
+
# Lazy load reporter file on discovery.
|
|
106
|
+
# Some callers (including tests) pass synthetic paths that do not exist.
|
|
107
|
+
begin
|
|
108
|
+
require file_path
|
|
109
|
+
rescue LoadError
|
|
110
|
+
# Ignore and continue with constant resolution.
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Check if class is defined after requiring.
|
|
103
114
|
return nil unless Object.const_defined?(full_class_name)
|
|
104
115
|
|
|
105
116
|
klass = Object.const_get(full_class_name)
|
|
@@ -109,30 +120,22 @@ module ActionReporter
|
|
|
109
120
|
end
|
|
110
121
|
|
|
111
122
|
def load_registered_reporter(config)
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
klass = Object.const_get(config[:class_name])
|
|
116
|
-
return nil unless klass < Base
|
|
117
|
-
|
|
118
|
-
# Only require if class is not already available and require_path is provided
|
|
119
|
-
# This allows classes defined inline (e.g., in tests) to work without requiring files
|
|
120
|
-
if config[:require_path] && !required?(config[:require_path])
|
|
123
|
+
# Try to load reporter file first; require is idempotent.
|
|
124
|
+
if config[:require_path]
|
|
121
125
|
begin
|
|
122
126
|
require config[:require_path]
|
|
123
127
|
rescue LoadError => e
|
|
124
|
-
# If file doesn't exist but class is already defined, that's okay
|
|
125
|
-
# (e.g., class defined inline in tests or already loaded)
|
|
126
128
|
warn "ActionReporter: Could not require #{config[:require_path]}: #{e.message}" if logger
|
|
127
|
-
# Continue - class might already be defined
|
|
128
129
|
end
|
|
129
130
|
end
|
|
130
131
|
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
# Check if class is defined (e.g., file loaded, inline test class, or already loaded)
|
|
133
|
+
return nil unless Object.const_defined?(config[:class_name])
|
|
133
134
|
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
klass = Object.const_get(config[:class_name])
|
|
136
|
+
return nil unless klass < Base
|
|
137
|
+
|
|
138
|
+
klass
|
|
136
139
|
end
|
|
137
140
|
|
|
138
141
|
def logger
|
|
@@ -2,17 +2,16 @@ module ActionReporter
|
|
|
2
2
|
module Utils
|
|
3
3
|
module_function
|
|
4
4
|
|
|
5
|
-
def deep_transform_values(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
v.map { |e| e.is_a?(Hash) ? deep_transform_values(e, &block) : e }
|
|
11
|
-
else
|
|
12
|
-
v
|
|
5
|
+
def deep_transform_values(value, &block)
|
|
6
|
+
case value
|
|
7
|
+
when Hash
|
|
8
|
+
value.each_with_object({}) do |(k, v), result|
|
|
9
|
+
result[k] = deep_transform_values(v, &block)
|
|
13
10
|
end
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
when Array
|
|
12
|
+
value.map { |element| deep_transform_values(element, &block) }
|
|
13
|
+
else
|
|
14
|
+
block.call(value)
|
|
16
15
|
end
|
|
17
16
|
end
|
|
18
17
|
end
|
data/lib/action_reporter.rb
CHANGED
|
@@ -4,30 +4,16 @@ require "action_reporter/base"
|
|
|
4
4
|
require "action_reporter/current"
|
|
5
5
|
require "action_reporter/plugin_discovery"
|
|
6
6
|
|
|
7
|
-
# Core reporters are still required for backward compatibility
|
|
8
|
-
# But discovery mechanism allows for lazy loading and custom reporters
|
|
9
|
-
require "action_reporter/rails_reporter"
|
|
10
|
-
require "action_reporter/honeybadger_reporter"
|
|
11
|
-
require "action_reporter/sentry_reporter"
|
|
12
|
-
require "action_reporter/scout_apm_reporter"
|
|
13
|
-
require "action_reporter/audited_reporter"
|
|
14
|
-
require "action_reporter/paper_trail_reporter"
|
|
15
|
-
require "action_reporter/active_version_reporter"
|
|
16
|
-
|
|
17
7
|
module ActionReporter
|
|
18
|
-
|
|
8
|
+
autoload :RailsReporter, "action_reporter/rails_reporter"
|
|
9
|
+
autoload :AuditedReporter, "action_reporter/audited_reporter"
|
|
10
|
+
autoload :PaperTrailReporter, "action_reporter/paper_trail_reporter"
|
|
11
|
+
autoload :ActiveVersionReporter, "action_reporter/active_version_reporter"
|
|
12
|
+
autoload :SentryReporter, "action_reporter/sentry_reporter"
|
|
13
|
+
autoload :HoneybadgerReporter, "action_reporter/honeybadger_reporter"
|
|
14
|
+
autoload :ScoutApmReporter, "action_reporter/scout_apm_reporter"
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
# Use `available_reporters` for auto-discovered reporters
|
|
22
|
-
AVAILABLE_REPORTERS = [
|
|
23
|
-
ActionReporter::RailsReporter,
|
|
24
|
-
ActionReporter::HoneybadgerReporter,
|
|
25
|
-
ActionReporter::SentryReporter,
|
|
26
|
-
ActionReporter::ScoutApmReporter,
|
|
27
|
-
ActionReporter::AuditedReporter,
|
|
28
|
-
ActionReporter::PaperTrailReporter,
|
|
29
|
-
ActionReporter::ActiveVersionReporter
|
|
30
|
-
].freeze
|
|
16
|
+
module_function
|
|
31
17
|
|
|
32
18
|
# Get available reporters (auto-discovered + registered)
|
|
33
19
|
# This is lazy-loaded and does not block application boot
|