forge_ops_tracker 0.2.1 → 0.7.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 +49 -0
- data/README.md +191 -10
- data/lib/forge_ops_tracker/client.rb +40 -10
- data/lib/forge_ops_tracker/configuration.rb +89 -3
- data/lib/forge_ops_tracker/delivery_queue.rb +1 -1
- data/lib/forge_ops_tracker/error_subscriber.rb +6 -1
- data/lib/forge_ops_tracker/event_builder.rb +64 -8
- data/lib/forge_ops_tracker/infrastructure_metric_buffer.rb +69 -0
- data/lib/forge_ops_tracker/integrations/net_http.rb +32 -0
- data/lib/forge_ops_tracker/integrations/puma.rb +55 -0
- data/lib/forge_ops_tracker/integrations/sidekiq.rb +69 -0
- data/lib/forge_ops_tracker/integrations/solid_queue.rb +37 -0
- data/lib/forge_ops_tracker/metric_buffer.rb +80 -0
- data/lib/forge_ops_tracker/middleware/session_tracking.rb +38 -0
- data/lib/forge_ops_tracker/middleware/user_context.rb +52 -0
- data/lib/forge_ops_tracker/performance_flusher.rb +101 -0
- data/lib/forge_ops_tracker/performance_instrumentation.rb +60 -0
- data/lib/forge_ops_tracker/periodic_poller.rb +50 -0
- data/lib/forge_ops_tracker/pii_scrubber.rb +2 -2
- data/lib/forge_ops_tracker/railtie.rb +123 -0
- data/lib/forge_ops_tracker/session_flusher.rb +85 -0
- data/lib/forge_ops_tracker/version.rb +1 -1
- data/lib/forge_ops_tracker.rb +52 -0
- metadata +58 -4
|
@@ -2,6 +2,20 @@ require "rails/railtie"
|
|
|
2
2
|
|
|
3
3
|
module ForgeOpsTracker
|
|
4
4
|
class Railtie < ::Rails::Railtie
|
|
5
|
+
# Shared across every performance-timing subscription/integration below (controller, query,
|
|
6
|
+
# job, raw Sidekiq, outbound Net::HTTP): one flusher, one background thread, one set of
|
|
7
|
+
# buckets, since PerformanceFlusher already buckets on [transaction_name, kind] and safely
|
|
8
|
+
# handles concurrent calls from several sources at once. Exposed as a class method, not a
|
|
9
|
+
# local variable inside one initializer, specifically so the Sidekiq/Net::HTTP integrations
|
|
10
|
+
# (each guarded by their own separate initializer, since each only loads if its own optional
|
|
11
|
+
# library is present) can record into the exact same instance rather than each starting a
|
|
12
|
+
# redundant flusher/thread of their own.
|
|
13
|
+
class << self
|
|
14
|
+
def performance_flusher
|
|
15
|
+
@performance_flusher ||= ForgeOpsTracker::PerformanceFlusher.new(ForgeOpsTracker.configuration)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
5
19
|
initializer "forge_ops_tracker.subscribe_error_reporter" do |app|
|
|
6
20
|
configuration = ForgeOpsTracker.configuration
|
|
7
21
|
configuration.app_root ||= app.root.to_s
|
|
@@ -10,5 +24,114 @@ module ForgeOpsTracker
|
|
|
10
24
|
|
|
11
25
|
Rails.error.subscribe(ForgeOpsTracker::ErrorSubscriber.new(configuration))
|
|
12
26
|
end
|
|
27
|
+
|
|
28
|
+
initializer "forge_ops_tracker.track_sessions" do |app|
|
|
29
|
+
app.middleware.use ForgeOpsTracker::Middleware::SessionTracking, configuration: ForgeOpsTracker.configuration
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Warden::Manager (mounted automatically by Devise, or directly by any other Warden-based
|
|
33
|
+
# auth setup) sets env["warden"] itself; this only ever reads it, so it works regardless of
|
|
34
|
+
# exactly where in the middleware stack this ends up relative to Warden::Manager, as long as
|
|
35
|
+
# that key exists in env by the time this middleware's own #call runs, which is true for any
|
|
36
|
+
# Warden-based auth setup: it mounts its own middleware once, for the whole app, long before
|
|
37
|
+
# any individual request begins.
|
|
38
|
+
initializer "forge_ops_tracker.track_current_user" do |app|
|
|
39
|
+
app.middleware.use ForgeOpsTracker::Middleware::UserContext, configuration: ForgeOpsTracker.configuration
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Rails already fires "process_action.action_controller" after every request with the
|
|
43
|
+
# controller/action and how long it took (event.duration, in ms) built in, so unlike session
|
|
44
|
+
# tracking above (which needed a Rack middleware because there's no single existing hook for
|
|
45
|
+
# "a request finished"), this needs no middleware of its own at all, just a subscriber.
|
|
46
|
+
# Guarded the same way the error subscriber and session-tracking middleware both are: on by
|
|
47
|
+
# default, off entirely if track_performance is turned off or the gem itself isn't enabled
|
|
48
|
+
# (no DSN configured, or the current environment isn't in enabled_environments).
|
|
49
|
+
#
|
|
50
|
+
# sql.active_record/perform.active_job are subscribed here too, not in their own separate
|
|
51
|
+
# initializers: all three (plus Sidekiq/Net::HTTP below) are genuinely the same concern
|
|
52
|
+
# (automatic performance timing), sharing the one performance_flusher above; see
|
|
53
|
+
# PerformanceInstrumentation for the actual transaction_name/skip decisions each one makes,
|
|
54
|
+
# kept out of these blocks specifically so that decision logic is unit-testable without a real
|
|
55
|
+
# Rails/ActiveJob boot (this gem's own test suite doesn't require Rails at all; see that
|
|
56
|
+
# file's own comment).
|
|
57
|
+
initializer "forge_ops_tracker.track_performance" do
|
|
58
|
+
configuration = ForgeOpsTracker.configuration
|
|
59
|
+
flusher = Railtie.performance_flusher
|
|
60
|
+
|
|
61
|
+
ActiveSupport::Notifications.subscribe("process_action.action_controller") do |*args|
|
|
62
|
+
next unless configuration.track_performance && configuration.enabled?
|
|
63
|
+
|
|
64
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
65
|
+
transaction_name = PerformanceInstrumentation.controller_transaction_name(event.payload)
|
|
66
|
+
flusher.record(transaction_name: transaction_name, duration_ms: event.duration, kind: "controller")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
|
|
70
|
+
next unless configuration.track_performance && configuration.enabled?
|
|
71
|
+
|
|
72
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
73
|
+
transaction_name = PerformanceInstrumentation.query_transaction_name(event.payload)
|
|
74
|
+
next if transaction_name.nil? # SCHEMA/cached, see PerformanceInstrumentation's own comment
|
|
75
|
+
|
|
76
|
+
flusher.record(transaction_name: transaction_name, duration_ms: event.duration, kind: "query")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
ActiveSupport::Notifications.subscribe("perform.active_job") do |*args|
|
|
80
|
+
next unless configuration.track_performance && configuration.enabled?
|
|
81
|
+
|
|
82
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
83
|
+
transaction_name = PerformanceInstrumentation.job_transaction_name(event.payload)
|
|
84
|
+
flusher.record(transaction_name: transaction_name, duration_ms: event.duration, kind: "job")
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Raw (non-ActiveJob) Sidekiq::Worker job timing, plus Sidekiq's own queue-depth/processed/
|
|
89
|
+
# failed/etc. gauges: a separate initializer, since unlike ActiveRecord/ActionController
|
|
90
|
+
# (always present in any Rails app this gem's own gemspec already implies), Sidekiq is
|
|
91
|
+
# genuinely optional and isn't a declared dependency of this gem at all.
|
|
92
|
+
initializer "forge_ops_tracker.track_sidekiq" do
|
|
93
|
+
next unless defined?(::Sidekiq)
|
|
94
|
+
|
|
95
|
+
require "forge_ops_tracker/integrations/sidekiq"
|
|
96
|
+
|
|
97
|
+
configuration = ForgeOpsTracker.configuration
|
|
98
|
+
::Sidekiq.configure_server do |config|
|
|
99
|
+
config.server_middleware do |chain|
|
|
100
|
+
# No extra args to chain.add: Sidekiq calls Middleware.new(*args) itself for every job,
|
|
101
|
+
# splatting whatever was passed here; the middleware's own keyword-argument defaults
|
|
102
|
+
# (ForgeOpsTracker.configuration, Railtie.performance_flusher) already resolve correctly
|
|
103
|
+
# when called with none, and passing a keyword hash through *args here is genuinely
|
|
104
|
+
# ambiguous (Sidekiq's own chain.add doc example passes a literal positional Hash, not
|
|
105
|
+
# keyword syntax, for exactly this reason).
|
|
106
|
+
chain.add ForgeOpsTracker::Integrations::Sidekiq::Middleware
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
ForgeOpsTracker::Integrations::Sidekiq::StatsReporter.start(configuration: configuration)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
initializer "forge_ops_tracker.track_solid_queue" do
|
|
114
|
+
next unless defined?(::SolidQueue)
|
|
115
|
+
|
|
116
|
+
require "forge_ops_tracker/integrations/solid_queue"
|
|
117
|
+
ForgeOpsTracker::Integrations::SolidQueue::StatsReporter.start(configuration: ForgeOpsTracker.configuration)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
initializer "forge_ops_tracker.track_puma" do
|
|
121
|
+
next unless defined?(::Puma)
|
|
122
|
+
|
|
123
|
+
require "forge_ops_tracker/integrations/puma"
|
|
124
|
+
ForgeOpsTracker::Integrations::Puma::StatsReporter.start(configuration: ForgeOpsTracker.configuration)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Net::HTTP is always present (Ruby's own standard library), so this always prepends; the
|
|
128
|
+
# actual on/off decision happens at call time inside Timing#request itself (checking
|
|
129
|
+
# configuration.track_performance/enabled? the same way the subscriptions above do), so
|
|
130
|
+
# toggling that flag after boot still takes effect immediately, the same as every other
|
|
131
|
+
# automatic instrumentation point in this gem.
|
|
132
|
+
initializer "forge_ops_tracker.track_net_http" do
|
|
133
|
+
require "forge_ops_tracker/integrations/net_http"
|
|
134
|
+
Net::HTTP.prepend(ForgeOpsTracker::Integrations::NetHTTP::Timing)
|
|
135
|
+
end
|
|
13
136
|
end
|
|
14
137
|
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require "thread"
|
|
2
|
+
|
|
3
|
+
module ForgeOpsTracker
|
|
4
|
+
# Counts requests and crashes in-process (see Middleware::SessionTracking) and periodically
|
|
5
|
+
# flushes the totals as one small aggregate report, rather than one network call per request.
|
|
6
|
+
# Same "lazily start a background thread on first use, not at load time" pattern DeliveryQueue
|
|
7
|
+
# already uses, so each forked Puma/Passenger worker gets its own fresh thread instead of
|
|
8
|
+
# inheriting a dead one across fork; here it's triggered by a sleep-loop timer instead of a
|
|
9
|
+
# queue push, since there's no per-event signal to wait on, just a clock.
|
|
10
|
+
class SessionFlusher
|
|
11
|
+
def initialize(configuration, client: Client.new(configuration))
|
|
12
|
+
@configuration = configuration
|
|
13
|
+
@client = client
|
|
14
|
+
@mutex = Mutex.new
|
|
15
|
+
@sessions_count = 0
|
|
16
|
+
@crashed_sessions_count = 0
|
|
17
|
+
@period_started_at = Time.now.utc
|
|
18
|
+
@thread = nil
|
|
19
|
+
|
|
20
|
+
# Flushes whatever's already tallied on a normal process exit, so the last partial window
|
|
21
|
+
# (anything shorter than a full session_flush_interval) isn't silently dropped.
|
|
22
|
+
at_exit { flush }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def record_session(crashed:)
|
|
26
|
+
ensure_worker_started
|
|
27
|
+
|
|
28
|
+
@mutex.synchronize do
|
|
29
|
+
@sessions_count += 1
|
|
30
|
+
@crashed_sessions_count += 1 if crashed
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Snapshots and resets the in-process counters, then delivers them. A failed delivery keeps
|
|
35
|
+
# the counts where they are rather than resetting, so the next flush's window just grows
|
|
36
|
+
# instead of losing what was already tallied; there's no other copy of this data anywhere.
|
|
37
|
+
def flush
|
|
38
|
+
snapshot = nil
|
|
39
|
+
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
return if @sessions_count.zero?
|
|
42
|
+
|
|
43
|
+
snapshot = {
|
|
44
|
+
release: configuration.release,
|
|
45
|
+
environment: configuration.environment.to_s,
|
|
46
|
+
period_started_at: @period_started_at.iso8601,
|
|
47
|
+
period_ended_at: Time.now.utc.iso8601,
|
|
48
|
+
sessions_count: @sessions_count,
|
|
49
|
+
crashed_sessions_count: @crashed_sessions_count
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
return unless snapshot && client.deliver_session_checkin(snapshot)
|
|
54
|
+
|
|
55
|
+
@mutex.synchronize do
|
|
56
|
+
@sessions_count = 0
|
|
57
|
+
@crashed_sessions_count = 0
|
|
58
|
+
@period_started_at = Time.now.utc
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
attr_reader :configuration, :client
|
|
64
|
+
|
|
65
|
+
def ensure_worker_started
|
|
66
|
+
return if @thread&.alive?
|
|
67
|
+
|
|
68
|
+
@mutex.synchronize do
|
|
69
|
+
return if @thread&.alive?
|
|
70
|
+
|
|
71
|
+
@thread = Thread.new { run }
|
|
72
|
+
@thread.abort_on_exception = false
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def run
|
|
77
|
+
loop do
|
|
78
|
+
sleep configuration.session_flush_interval
|
|
79
|
+
flush
|
|
80
|
+
rescue StandardError => e
|
|
81
|
+
configuration.logger&.debug { "[ForgeOpsTracker] session flush thread error: #{e.class}: #{e.message}" }
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
data/lib/forge_ops_tracker.rb
CHANGED
|
@@ -5,6 +5,14 @@ require "forge_ops_tracker/event_builder"
|
|
|
5
5
|
require "forge_ops_tracker/client"
|
|
6
6
|
require "forge_ops_tracker/delivery_queue"
|
|
7
7
|
require "forge_ops_tracker/error_subscriber"
|
|
8
|
+
require "forge_ops_tracker/session_flusher"
|
|
9
|
+
require "forge_ops_tracker/performance_flusher"
|
|
10
|
+
require "forge_ops_tracker/performance_instrumentation"
|
|
11
|
+
require "forge_ops_tracker/metric_buffer"
|
|
12
|
+
require "forge_ops_tracker/infrastructure_metric_buffer"
|
|
13
|
+
require "forge_ops_tracker/periodic_poller"
|
|
14
|
+
require "forge_ops_tracker/middleware/session_tracking"
|
|
15
|
+
require "forge_ops_tracker/middleware/user_context"
|
|
8
16
|
|
|
9
17
|
module ForgeOpsTracker
|
|
10
18
|
class << self
|
|
@@ -15,6 +23,50 @@ module ForgeOpsTracker
|
|
|
15
23
|
def configure
|
|
16
24
|
yield configuration
|
|
17
25
|
end
|
|
26
|
+
|
|
27
|
+
# Records a named business metric (a signup, a payment, anything a customer wants to name),
|
|
28
|
+
# buffered and flushed periodically as a batch rather than one network call per capture (see
|
|
29
|
+
# MetricBuffer). value defaults to 1.0 so a bare counter-style call ("a signup happened")
|
|
30
|
+
# needs no argument; pass an explicit one for a metric with a real magnitude ("a $49 payment
|
|
31
|
+
# happened"). A no-op, same as every other capture path in this gem, when the gem isn't
|
|
32
|
+
# enabled (no DSN configured, or the current environment isn't in enabled_environments).
|
|
33
|
+
def capture_metric(name, value: 1.0)
|
|
34
|
+
return unless configuration.enabled?
|
|
35
|
+
|
|
36
|
+
metric_buffer.record(metric_name: name, value: value)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Records one infrastructure reading (CPU/memory/disk, or anything else a customer's own
|
|
40
|
+
# script reads) from one of their own hosts. hostname defaults to Configuration#server_name
|
|
41
|
+
# (already derived from Socket.gethostname), so a script running on the box it's reporting
|
|
42
|
+
# about doesn't need to pass one explicitly. Same buffered-batch delivery and no-op-when-
|
|
43
|
+
# disabled contract as capture_metric above.
|
|
44
|
+
def capture_infrastructure_metric(name, value:, hostname: nil)
|
|
45
|
+
return unless configuration.enabled?
|
|
46
|
+
|
|
47
|
+
infrastructure_metric_buffer.record(metric_name: name, value: value, hostname: hostname || configuration.server_name)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Manually attaches an affected user to whatever gets reported for the rest of this request
|
|
51
|
+
# (or, outside a request entirely, in a background job or a console session, for the rest of
|
|
52
|
+
# this thread). Sets the same thread-local ForgeOpsTracker::Middleware::UserContext's own
|
|
53
|
+
# Warden auto-detection uses, so this composes with it rather than being a second, separate
|
|
54
|
+
# mechanism: call this after that middleware ran to override its guess, or call it standalone
|
|
55
|
+
# in an app with no Warden at all. id/email/username are all independently optional; a call
|
|
56
|
+
# with none of them (or all nil) clears whatever was set.
|
|
57
|
+
def set_user(id: nil, email: nil, username: nil)
|
|
58
|
+
user = { id: id, email: email, username: username }.compact
|
|
59
|
+
Thread.current[:forge_ops_tracker_current_user] = user.empty? ? nil : user
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
def metric_buffer
|
|
64
|
+
@metric_buffer ||= MetricBuffer.new(configuration)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def infrastructure_metric_buffer
|
|
68
|
+
@infrastructure_metric_buffer ||= InfrastructureMetricBuffer.new(configuration)
|
|
69
|
+
end
|
|
18
70
|
end
|
|
19
71
|
end
|
|
20
72
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: forge_ops_tracker
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ForgeOps
|
|
@@ -23,8 +23,50 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '3.13'
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: sidekiq
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '7.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '7.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: puma
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '6.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '6.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: webrick
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.8'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '1.8'
|
|
68
|
+
description: Hooks Rails' error reporter and reports exceptions to a ForgeOps exception
|
|
69
|
+
tracker instance over HTTP, without ever raising back into the host application.
|
|
28
70
|
executables: []
|
|
29
71
|
extensions: []
|
|
30
72
|
extra_rdoc_files: []
|
|
@@ -38,8 +80,20 @@ files:
|
|
|
38
80
|
- lib/forge_ops_tracker/delivery_queue.rb
|
|
39
81
|
- lib/forge_ops_tracker/error_subscriber.rb
|
|
40
82
|
- lib/forge_ops_tracker/event_builder.rb
|
|
83
|
+
- lib/forge_ops_tracker/infrastructure_metric_buffer.rb
|
|
84
|
+
- lib/forge_ops_tracker/integrations/net_http.rb
|
|
85
|
+
- lib/forge_ops_tracker/integrations/puma.rb
|
|
86
|
+
- lib/forge_ops_tracker/integrations/sidekiq.rb
|
|
87
|
+
- lib/forge_ops_tracker/integrations/solid_queue.rb
|
|
88
|
+
- lib/forge_ops_tracker/metric_buffer.rb
|
|
89
|
+
- lib/forge_ops_tracker/middleware/session_tracking.rb
|
|
90
|
+
- lib/forge_ops_tracker/middleware/user_context.rb
|
|
91
|
+
- lib/forge_ops_tracker/performance_flusher.rb
|
|
92
|
+
- lib/forge_ops_tracker/performance_instrumentation.rb
|
|
93
|
+
- lib/forge_ops_tracker/periodic_poller.rb
|
|
41
94
|
- lib/forge_ops_tracker/pii_scrubber.rb
|
|
42
95
|
- lib/forge_ops_tracker/railtie.rb
|
|
96
|
+
- lib/forge_ops_tracker/session_flusher.rb
|
|
43
97
|
- lib/forge_ops_tracker/version.rb
|
|
44
98
|
homepage: https://getforgeops.net
|
|
45
99
|
licenses:
|
|
@@ -63,5 +117,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
63
117
|
requirements: []
|
|
64
118
|
rubygems_version: 4.0.11
|
|
65
119
|
specification_version: 4
|
|
66
|
-
summary: Rails exception reporting client for a
|
|
120
|
+
summary: Rails exception reporting client for a ForgeOps tracker
|
|
67
121
|
test_files: []
|