gc_reporter 0.1.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 +7 -0
- data/README.md +50 -0
- data/gc_reporter.gemspec +31 -0
- data/lib/gc_reporter/configuration.rb +13 -0
- data/lib/gc_reporter/railtie.rb +12 -0
- data/lib/gc_reporter/reporter.rb +86 -0
- data/lib/gc_reporter/sidekiq.rb +12 -0
- data/lib/gc_reporter/version.rb +3 -0
- data/lib/gc_reporter.rb +16 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c222c825b6a36d78de4bb5c0685ac53caaaaef3f55ead344ff1548a60b4cf941
|
4
|
+
data.tar.gz: dcaee860d83bb29c4eaf1a80e575e9de265f16444a2676b57b20c0998be28048
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 443044d4c0ddb10027b0b41941d72acad7e2489a78c652a4194743ff00598376b157263caccef1943cb8ef1704939556264351d99647fd6d4169fb148065e3bf
|
7
|
+
data.tar.gz: c670a8606e92d7bc64d01f93e63e06bd4f42322e65097c0df55b6ed8666e5292a5b7e46af3864b8780995cd1bf951ee9f23ea2cdb4a5cf8b66ffa54052ffc558
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# GC Reporter
|
2
|
+
|
3
|
+
A simple Rails extension to collect and report Garbage Collection (`GC.stat`) and `ObjectSpace` statistics from your Ruby applications to a central monitoring application via Action Cable.
|
4
|
+
|
5
|
+
It is designed to be lightweight and work in various Ruby process types:
|
6
|
+
* Rails servers (Puma, Unicorn, etc.)
|
7
|
+
* Sidekiq workers
|
8
|
+
* Rake tasks
|
9
|
+
|
10
|
+
## Receiving Application Setup
|
11
|
+
|
12
|
+
Before using this gem, you need a Rails application with an Action Cable channel ready to receive the data.
|
13
|
+
|
14
|
+
1. **Generate a channel:**
|
15
|
+
```bash
|
16
|
+
rails g channel GcStats
|
17
|
+
```
|
18
|
+
|
19
|
+
2. **Implement the channel:** The channel needs to define a `report` action, which is the remote procedure call (RPC) endpoint this gem uses.
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
# app/channels/gc_stats_channel.rb
|
23
|
+
class GcStatsChannel < ApplicationCable::Channel
|
24
|
+
def subscribed
|
25
|
+
stream_from "gc_stats_channel"
|
26
|
+
end
|
27
|
+
|
28
|
+
def unsubscribed
|
29
|
+
# Any cleanup needed when a client disconnects
|
30
|
+
end
|
31
|
+
|
32
|
+
# This is the action called by the gem's client.
|
33
|
+
def report(data)
|
34
|
+
# data is the payload sent from the gc_reporter gem.
|
35
|
+
# Process it here: log it, save to a DB, or broadcast to a live dashboard.
|
36
|
+
Rails.logger.info "Received GC Stats from #{data['source']} (PID: #{data['process_id']})"
|
37
|
+
|
38
|
+
# Example: Broadcast to a UI dashboard
|
39
|
+
ActionCable.server.broadcast("live_dashboard_channel", data)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
## Installation
|
45
|
+
|
46
|
+
Add `gc_reporter` to your application's Gemfile.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
# Gemfile
|
50
|
+
gem 'gc_reporter', git: '[https://github.com/example/gc_reporter](https://github.com/example/gc_reporter)' # Or from RubyGems when published
|
data/gc_reporter.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative "lib/gc_reporter/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "gc_reporter"
|
5
|
+
spec.version = GcReporter::VERSION
|
6
|
+
spec.authors = ["Gemini AI"]
|
7
|
+
spec.email = ["ai@google.com"]
|
8
|
+
|
9
|
+
spec.summary = "Reports GC stats over Action Cable."
|
10
|
+
spec.description = "A Rails extension to collect and report GC.stat and ObjectSpace counts from Rails, Sidekiq, and Rake tasks to a central Action Cable endpoint."
|
11
|
+
spec.homepage = "https://github.com/codepawpaw/gc_reporter"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = ">= 2.7.0"
|
14
|
+
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
17
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGELOG.md"
|
18
|
+
|
19
|
+
# Find all files tracked by git, excluding the spec/test directory.
|
20
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
21
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
22
|
+
end
|
23
|
+
spec.bindir = "exe"
|
24
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
|
27
|
+
# Gem Dependencies
|
28
|
+
#spec.add_dependency "actioncable-client-ruby", "~> 0.4"
|
29
|
+
spec.add_dependency 'action_cable_client', '~> 3.1'
|
30
|
+
spec.add_dependency "railties", ">= 5.0.0"
|
31
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module GcReporter
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :url, :channel, :interval, :source, :verbose
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@url = nil # Must be configured by the user, e.g., 'ws://localhost:3000/cable'
|
7
|
+
@channel = 'GcStatsChannel'
|
8
|
+
@interval = 30 # seconds
|
9
|
+
@source = "Process-#{Process.pid}" # A default identifier
|
10
|
+
@verbose = false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module GcReporter
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
config.after_initialize do
|
4
|
+
if defined?(Rails::Server)
|
5
|
+
GcReporter.configuration.source ||= "Rails-#{Process.pid}"
|
6
|
+
GcReporter::Reporter.start
|
7
|
+
|
8
|
+
at_exit { GcReporter::Reporter.stop }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'action_cable_client'
|
2
|
+
require 'time'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module GcReporter
|
6
|
+
class Reporter
|
7
|
+
def self.start
|
8
|
+
config = GcReporter.configuration
|
9
|
+
return if running?
|
10
|
+
return log("Cannot start: URL not configured.", level: :error) if config.url.nil?
|
11
|
+
|
12
|
+
@running = true
|
13
|
+
@thread = Thread.new do
|
14
|
+
client = ActionCableClient.new(config.url, channel: config.channel)
|
15
|
+
log("Reporter thread started. Sending stats every #{config.interval}s.")
|
16
|
+
|
17
|
+
client.connected { log("Successfully connected to Action Cable at #{config.url}.") }
|
18
|
+
client.disconnected { log("Disconnected from Action Cable. Client will try to reconnect.") }
|
19
|
+
|
20
|
+
loop do
|
21
|
+
break unless @running
|
22
|
+
begin
|
23
|
+
client.perform('report', collect_stats) if client.subscribed?
|
24
|
+
rescue => e
|
25
|
+
log("Error reporting stats: #{e.message}", level: :error)
|
26
|
+
end
|
27
|
+
sleep config.interval
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Stops the background reporting thread.
|
33
|
+
def self.stop
|
34
|
+
return unless running?
|
35
|
+
@running = false
|
36
|
+
@thread&.kill
|
37
|
+
@thread = nil
|
38
|
+
log("Reporter stopped.")
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sends a single, synchronous report.
|
42
|
+
# Ideal for short-lived scripts like Rake tasks.
|
43
|
+
def self.report_once!
|
44
|
+
config = GcReporter.configuration
|
45
|
+
return log("Cannot report: URL not configured.", level: :error) if config.url.nil?
|
46
|
+
|
47
|
+
log("Performing a one-off report...")
|
48
|
+
# Using a block ensures the client is created and torn down cleanly.
|
49
|
+
ActionCableClient.start(config.url, channel: config.channel) do |client|
|
50
|
+
client.subscribed do
|
51
|
+
log("One-off report client subscribed. Sending data.")
|
52
|
+
client.perform('report', collect_stats)
|
53
|
+
# Disconnect after sending to allow the parent script to exit.
|
54
|
+
client.disconnect!
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def self.running?
|
62
|
+
!!@running
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.collect_stats
|
66
|
+
{
|
67
|
+
source: GcReporter.configuration.source,
|
68
|
+
process_id: Process.pid,
|
69
|
+
reported_at: Time.now.utc.iso8601,
|
70
|
+
payload: {
|
71
|
+
gc_stat: GC.stat,
|
72
|
+
object_counts: ObjectSpace.count_objects
|
73
|
+
}
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.log(message, level: :info)
|
78
|
+
timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
79
|
+
if level == :error
|
80
|
+
warn "[GcReporter ERROR] #{timestamp}: #{message}"
|
81
|
+
elsif GcReporter.configuration.verbose
|
82
|
+
puts "[GcReporter INFO] #{timestamp}: #{message}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# This file must be explicitly required by the user in their Sidekiq initializer.
|
2
|
+
if defined?(Sidekiq)
|
3
|
+
Sidekiq.configure_server do |config|
|
4
|
+
config.on(:startup) do
|
5
|
+
GcReporter.configuration.source ||= "Sidekiq-#{Process.pid}"
|
6
|
+
GcReporter::Reporter.start
|
7
|
+
end
|
8
|
+
|
9
|
+
config.on(:quiet) { GcReporter::Reporter.stop }
|
10
|
+
config.on(:shutdown) { GcReporter::Reporter.stop }
|
11
|
+
end
|
12
|
+
end
|
data/lib/gc_reporter.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "gc_reporter/version"
|
2
|
+
require "gc_reporter/configuration"
|
3
|
+
require "gc_reporter/reporter"
|
4
|
+
require "gc_reporter/railtie" if defined?(Rails::Railtie)
|
5
|
+
|
6
|
+
module GcReporter
|
7
|
+
class Error < StandardError; end
|
8
|
+
|
9
|
+
def self.configure
|
10
|
+
yield(configuration)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.configuration
|
14
|
+
@configuration ||= Configuration.new
|
15
|
+
end
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gc_reporter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gemini AI
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: action_cable_client
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '3.1'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '3.1'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: railties
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 5.0.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 5.0.0
|
40
|
+
description: A Rails extension to collect and report GC.stat and ObjectSpace counts
|
41
|
+
from Rails, Sidekiq, and Rake tasks to a central Action Cable endpoint.
|
42
|
+
email:
|
43
|
+
- ai@google.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- README.md
|
49
|
+
- gc_reporter.gemspec
|
50
|
+
- lib/gc_reporter.rb
|
51
|
+
- lib/gc_reporter/configuration.rb
|
52
|
+
- lib/gc_reporter/railtie.rb
|
53
|
+
- lib/gc_reporter/reporter.rb
|
54
|
+
- lib/gc_reporter/sidekiq.rb
|
55
|
+
- lib/gc_reporter/version.rb
|
56
|
+
homepage: https://github.com/codepawpaw/gc_reporter
|
57
|
+
licenses:
|
58
|
+
- MIT
|
59
|
+
metadata:
|
60
|
+
homepage_uri: https://github.com/codepawpaw/gc_reporter
|
61
|
+
source_code_uri: https://github.com/codepawpaw/gc_reporter
|
62
|
+
changelog_uri: https://github.com/codepawpaw/gc_reporter/CHANGELOG.md
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 2.7.0
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubygems_version: 3.6.9
|
78
|
+
specification_version: 4
|
79
|
+
summary: Reports GC stats over Action Cable.
|
80
|
+
test_files: []
|