lantern-rails 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/LICENSE +21 -0
- data/README.md +83 -0
- data/lib/lantern/rails/collector.rb +192 -0
- data/lib/lantern/rails/configuration.rb +25 -0
- data/lib/lantern/rails/git_metadata.rb +21 -0
- data/lib/lantern/rails/railtie.rb +18 -0
- data/lib/lantern/rails/reporter.rb +48 -0
- data/lib/lantern/rails/runner.rb +44 -0
- data/lib/lantern/rails/version.rb +5 -0
- data/lib/lantern-rails.rb +43 -0
- metadata +61 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b544123b390d3e7fee816bd3f27fdd485334fb5688f45754a52551294d39ef98
|
|
4
|
+
data.tar.gz: 74b280feb07f0d4bdac527c56d206c47b0fd009d293348c7924df76d46c900f8
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a14c62f42e4b69f19ae193061a17d981f005e110c691f1f6b13200020cbb7bb1e50182d2c471814244a06b68d69bef54bab6f51230e5291226f3c3653f8bf788
|
|
7
|
+
data.tar.gz: d0f5344237d2684d8a8891a2ce9941669a26d455a3a05fdf01b6102c9c26322410f505db446c76b8043400e39b0b86491af165e5341d0a0b9366066ec92ffb48
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eric Mumbower
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# lantern-rails
|
|
2
|
+
|
|
3
|
+
Postgres health monitoring for Rails apps. Collects database metrics and sends them to [Lantern](https://uselantern.dev) — a hosted dashboard that scores your database health and surfaces actionable recommendations.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add to your Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem "lantern-rails"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then run:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
bundle install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Setup
|
|
20
|
+
|
|
21
|
+
Generate an API key at [uselantern.dev](https://uselantern.dev), then create an initializer:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
# config/initializers/lantern.rb
|
|
25
|
+
Lantern::Rails.configure do |config|
|
|
26
|
+
config.api_key = ENV["LANTERN_API_KEY"]
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
That's it. The collector starts automatically when your Rails app boots in production.
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
Lantern::Rails.configure do |config|
|
|
36
|
+
config.api_key = ENV["LANTERN_API_KEY"]
|
|
37
|
+
config.host = "https://uselantern.dev" # default
|
|
38
|
+
config.interval = 300 # seconds, default 5 min
|
|
39
|
+
config.collect_in_environments = %w[production staging] # default
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Deploy tracking
|
|
44
|
+
|
|
45
|
+
To correlate deploys with health score changes, call this from your deploy pipeline or a Rails initializer that runs after deploy:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
Lantern::Rails.report_deploy
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Or pass explicit values:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
Lantern::Rails.report_deploy(
|
|
55
|
+
git_sha: ENV["GIT_SHA"],
|
|
56
|
+
git_author: ENV["GIT_AUTHOR"],
|
|
57
|
+
deployer: "github-actions"
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## What gets collected
|
|
62
|
+
|
|
63
|
+
Every collection interval, the gem queries your Postgres instance for:
|
|
64
|
+
|
|
65
|
+
- Buffer and index cache hit ratios
|
|
66
|
+
- Unused index count and total size
|
|
67
|
+
- Table bloat ratio
|
|
68
|
+
- Long-running queries (> 30 seconds)
|
|
69
|
+
- Dead tuple counts and vacuum status
|
|
70
|
+
- Active connections vs. max_connections
|
|
71
|
+
- pg_stat_bgwriter stats_reset timestamp (to detect false positives)
|
|
72
|
+
|
|
73
|
+
No query text, no table data, no PII. Only aggregate pg_stat_* metrics.
|
|
74
|
+
|
|
75
|
+
## Requirements
|
|
76
|
+
|
|
77
|
+
- Ruby >= 3.1
|
|
78
|
+
- Rails >= 7.0
|
|
79
|
+
- PostgreSQL (any version with pg_stat_statements)
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
MIT
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
module Lantern
|
|
2
|
+
module Rails
|
|
3
|
+
class Collector
|
|
4
|
+
def collect
|
|
5
|
+
connection = ActiveRecord::Base.connection
|
|
6
|
+
|
|
7
|
+
cache = collect_cache_metrics(connection)
|
|
8
|
+
indexes = collect_index_metrics(connection)
|
|
9
|
+
bloat = collect_bloat_metrics(connection)
|
|
10
|
+
queries = collect_query_metrics(connection)
|
|
11
|
+
vacuum = collect_vacuum_metrics(connection)
|
|
12
|
+
conns = collect_connection_metrics(connection)
|
|
13
|
+
git = GitMetadata.collect
|
|
14
|
+
|
|
15
|
+
{
|
|
16
|
+
collected_at: Time.current.iso8601,
|
|
17
|
+
|
|
18
|
+
shared_buffer_hit_ratio: cache[:shared_buffer_hit_ratio],
|
|
19
|
+
index_hit_ratio: cache[:index_hit_ratio],
|
|
20
|
+
|
|
21
|
+
unused_index_count: indexes[:unused_index_count],
|
|
22
|
+
unused_index_size_bytes: indexes[:unused_index_size_bytes],
|
|
23
|
+
stats_reset_at: indexes[:stats_reset_at],
|
|
24
|
+
|
|
25
|
+
estimated_bloat_bytes: bloat[:estimated_bloat_bytes],
|
|
26
|
+
bloat_ratio: bloat[:bloat_ratio],
|
|
27
|
+
|
|
28
|
+
long_running_query_count: queries[:long_running_query_count],
|
|
29
|
+
longest_query_duration_ms: queries[:longest_query_duration_ms],
|
|
30
|
+
|
|
31
|
+
total_dead_tuples: vacuum[:total_dead_tuples],
|
|
32
|
+
total_live_tuples: vacuum[:total_live_tuples],
|
|
33
|
+
tables_needing_vacuum: vacuum[:tables_needing_vacuum],
|
|
34
|
+
tables_never_vacuumed: vacuum[:tables_never_vacuumed],
|
|
35
|
+
oldest_vacuum_age_seconds: vacuum[:oldest_vacuum_age_seconds],
|
|
36
|
+
|
|
37
|
+
active_connections: conns[:active_connections],
|
|
38
|
+
max_connections: conns[:max_connections],
|
|
39
|
+
connection_utilization: conns[:connection_utilization],
|
|
40
|
+
|
|
41
|
+
git_sha: git[:sha],
|
|
42
|
+
git_message: git[:message]
|
|
43
|
+
}
|
|
44
|
+
rescue => e
|
|
45
|
+
::Rails.logger.error("[Lantern] Collection failed: #{e.message}")
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def collect_cache_metrics(conn)
|
|
52
|
+
row = conn.select_one(<<~SQL)
|
|
53
|
+
SELECT
|
|
54
|
+
round(
|
|
55
|
+
sum(heap_blks_hit)::numeric /
|
|
56
|
+
nullif(sum(heap_blks_hit) + sum(heap_blks_read), 0) * 100,
|
|
57
|
+
4
|
|
58
|
+
) AS shared_buffer_hit_ratio,
|
|
59
|
+
round(
|
|
60
|
+
sum(idx_blks_hit)::numeric /
|
|
61
|
+
nullif(sum(idx_blks_hit) + sum(idx_blks_read), 0) * 100,
|
|
62
|
+
4
|
|
63
|
+
) AS index_hit_ratio
|
|
64
|
+
FROM pg_statio_user_tables
|
|
65
|
+
SQL
|
|
66
|
+
|
|
67
|
+
{
|
|
68
|
+
shared_buffer_hit_ratio: row["shared_buffer_hit_ratio"]&.to_f,
|
|
69
|
+
index_hit_ratio: row["index_hit_ratio"]&.to_f
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def collect_index_metrics(conn)
|
|
74
|
+
row = conn.select_one(<<~SQL)
|
|
75
|
+
SELECT
|
|
76
|
+
count(*) AS unused_index_count,
|
|
77
|
+
coalesce(sum(pg_relation_size(indexrelid)), 0)::bigint AS unused_index_size_bytes,
|
|
78
|
+
(SELECT stats_reset FROM pg_stat_bgwriter) AS stats_reset_at
|
|
79
|
+
FROM pg_stat_user_indexes
|
|
80
|
+
JOIN pg_index USING (indexrelid)
|
|
81
|
+
WHERE idx_scan = 0
|
|
82
|
+
AND NOT indisprimary
|
|
83
|
+
AND NOT indisunique
|
|
84
|
+
SQL
|
|
85
|
+
|
|
86
|
+
{
|
|
87
|
+
unused_index_count: row["unused_index_count"].to_i,
|
|
88
|
+
unused_index_size_bytes: row["unused_index_size_bytes"].to_i,
|
|
89
|
+
stats_reset_at: row["stats_reset_at"]
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def collect_bloat_metrics(conn)
|
|
94
|
+
row = conn.select_one(<<~SQL)
|
|
95
|
+
SELECT
|
|
96
|
+
coalesce(
|
|
97
|
+
sum(
|
|
98
|
+
CASE
|
|
99
|
+
WHEN n_dead_tup > 0 AND (n_live_tup + n_dead_tup) > 0
|
|
100
|
+
THEN (n_dead_tup::numeric / (n_live_tup + n_dead_tup) * pg_total_relation_size(relid))
|
|
101
|
+
ELSE 0
|
|
102
|
+
END
|
|
103
|
+
)::bigint,
|
|
104
|
+
0
|
|
105
|
+
) AS estimated_bloat_bytes,
|
|
106
|
+
coalesce(
|
|
107
|
+
round(
|
|
108
|
+
sum(n_dead_tup)::numeric /
|
|
109
|
+
nullif(sum(n_live_tup + n_dead_tup), 0) * 100,
|
|
110
|
+
2
|
|
111
|
+
),
|
|
112
|
+
0
|
|
113
|
+
) AS bloat_ratio
|
|
114
|
+
FROM pg_stat_user_tables
|
|
115
|
+
SQL
|
|
116
|
+
|
|
117
|
+
{
|
|
118
|
+
estimated_bloat_bytes: row["estimated_bloat_bytes"].to_i,
|
|
119
|
+
bloat_ratio: row["bloat_ratio"].to_f
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def collect_query_metrics(conn)
|
|
124
|
+
row = conn.select_one(<<~SQL)
|
|
125
|
+
SELECT
|
|
126
|
+
count(*) AS long_running_query_count,
|
|
127
|
+
coalesce(
|
|
128
|
+
max(extract(epoch from now() - query_start) * 1000)::bigint,
|
|
129
|
+
0
|
|
130
|
+
) AS longest_query_duration_ms
|
|
131
|
+
FROM pg_stat_activity
|
|
132
|
+
WHERE state = 'active'
|
|
133
|
+
AND query_start < now() - interval '30 seconds'
|
|
134
|
+
AND query NOT ILIKE '%pg_stat_activity%'
|
|
135
|
+
SQL
|
|
136
|
+
|
|
137
|
+
{
|
|
138
|
+
long_running_query_count: row["long_running_query_count"].to_i,
|
|
139
|
+
longest_query_duration_ms: row["longest_query_duration_ms"].to_i
|
|
140
|
+
}
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def collect_vacuum_metrics(conn)
|
|
144
|
+
row = conn.select_one(<<~SQL)
|
|
145
|
+
SELECT
|
|
146
|
+
coalesce(sum(n_dead_tup), 0)::bigint AS total_dead_tuples,
|
|
147
|
+
coalesce(sum(n_live_tup), 0)::bigint AS total_live_tuples,
|
|
148
|
+
count(*) FILTER (
|
|
149
|
+
WHERE n_dead_tup > n_live_tup * 0.1 AND n_live_tup > 0
|
|
150
|
+
) AS tables_needing_vacuum,
|
|
151
|
+
count(*) FILTER (
|
|
152
|
+
WHERE last_vacuum IS NULL AND last_autovacuum IS NULL AND n_live_tup > 0
|
|
153
|
+
) AS tables_never_vacuumed,
|
|
154
|
+
coalesce(
|
|
155
|
+
max(extract(epoch from now() - greatest(last_vacuum, last_autovacuum)))::int,
|
|
156
|
+
0
|
|
157
|
+
) AS oldest_vacuum_age_seconds
|
|
158
|
+
FROM pg_stat_user_tables
|
|
159
|
+
SQL
|
|
160
|
+
|
|
161
|
+
{
|
|
162
|
+
total_dead_tuples: row["total_dead_tuples"].to_i,
|
|
163
|
+
total_live_tuples: row["total_live_tuples"].to_i,
|
|
164
|
+
tables_needing_vacuum: row["tables_needing_vacuum"].to_i,
|
|
165
|
+
tables_never_vacuumed: row["tables_never_vacuumed"].to_i,
|
|
166
|
+
oldest_vacuum_age_seconds: row["oldest_vacuum_age_seconds"].to_i
|
|
167
|
+
}
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def collect_connection_metrics(conn)
|
|
171
|
+
row = conn.select_one(<<~SQL)
|
|
172
|
+
SELECT
|
|
173
|
+
count(*) FILTER (WHERE state IS NOT NULL) AS active_connections,
|
|
174
|
+
current_setting('max_connections')::int AS max_connections,
|
|
175
|
+
round(
|
|
176
|
+
count(*) FILTER (WHERE state IS NOT NULL)::numeric /
|
|
177
|
+
current_setting('max_connections')::numeric * 100,
|
|
178
|
+
2
|
|
179
|
+
) AS connection_utilization
|
|
180
|
+
FROM pg_stat_activity
|
|
181
|
+
WHERE datname = current_database()
|
|
182
|
+
SQL
|
|
183
|
+
|
|
184
|
+
{
|
|
185
|
+
active_connections: row["active_connections"].to_i,
|
|
186
|
+
max_connections: row["max_connections"].to_i,
|
|
187
|
+
connection_utilization: row["connection_utilization"].to_f
|
|
188
|
+
}
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Lantern
|
|
2
|
+
module Rails
|
|
3
|
+
class Configuration
|
|
4
|
+
attr_accessor :api_key, :host, :interval, :environment, :enabled, :collect_in_environments
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
@host = "https://uselantern.dev"
|
|
8
|
+
@interval = 300 # 5 minutes
|
|
9
|
+
@environment = detect_environment
|
|
10
|
+
@enabled = true
|
|
11
|
+
@collect_in_environments = %w[production staging]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def valid?
|
|
15
|
+
api_key.present?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def detect_environment
|
|
21
|
+
defined?(::Rails) ? ::Rails.env.to_s : "production"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Lantern
|
|
2
|
+
module Rails
|
|
3
|
+
module GitMetadata
|
|
4
|
+
def self.collect
|
|
5
|
+
sha = run("git rev-parse --short HEAD")
|
|
6
|
+
message = run("git log -1 --pretty=%s")
|
|
7
|
+
author = run("git log -1 --pretty=%ae")
|
|
8
|
+
{ sha: sha, message: message, author: author }
|
|
9
|
+
rescue => e
|
|
10
|
+
::Rails.logger.debug("[Lantern] Git metadata unavailable: #{e.message}")
|
|
11
|
+
{ sha: nil, message: nil, author: nil }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.run(cmd)
|
|
15
|
+
result = `#{cmd} 2>/dev/null`.strip
|
|
16
|
+
result.empty? ? nil : result
|
|
17
|
+
end
|
|
18
|
+
private_class_method :run
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Lantern
|
|
2
|
+
module Rails
|
|
3
|
+
class Railtie < ::Rails::Railtie
|
|
4
|
+
config.after_initialize do
|
|
5
|
+
config = Lantern::Rails.configuration
|
|
6
|
+
next unless config.valid?
|
|
7
|
+
next unless config.enabled
|
|
8
|
+
next unless config.collect_in_environments.include?(::Rails.env.to_s)
|
|
9
|
+
|
|
10
|
+
runner = Lantern::Rails::Runner.new(config)
|
|
11
|
+
|
|
12
|
+
at_exit { runner.stop }
|
|
13
|
+
|
|
14
|
+
runner.start
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require "net/http"
|
|
2
|
+
require "json"
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module Lantern
|
|
6
|
+
module Rails
|
|
7
|
+
class Reporter
|
|
8
|
+
def initialize(config)
|
|
9
|
+
@config = config
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def report_snapshot(payload)
|
|
13
|
+
post("/api/v1/snapshots", payload)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def report_deploy(payload)
|
|
17
|
+
post("/api/v1/deploy_events", payload)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def post(path, payload)
|
|
23
|
+
uri = URI("#{@config.host}#{path}")
|
|
24
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
25
|
+
http.use_ssl = uri.scheme == "https"
|
|
26
|
+
http.open_timeout = 5
|
|
27
|
+
http.read_timeout = 10
|
|
28
|
+
|
|
29
|
+
request = Net::HTTP::Post.new(uri)
|
|
30
|
+
request["Authorization"] = "Bearer #{@config.api_key}"
|
|
31
|
+
request["Content-Type"] = "application/json"
|
|
32
|
+
request["Accept"] = "application/json"
|
|
33
|
+
request.body = payload.to_json
|
|
34
|
+
|
|
35
|
+
response = http.request(request)
|
|
36
|
+
|
|
37
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
38
|
+
::Rails.logger.warn("[Lantern] API returned #{response.code}: #{response.body}")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
response
|
|
42
|
+
rescue => e
|
|
43
|
+
::Rails.logger.error("[Lantern] Failed to report to #{path}: #{e.message}")
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Lantern
|
|
2
|
+
module Rails
|
|
3
|
+
class Runner
|
|
4
|
+
def initialize(config)
|
|
5
|
+
@config = config
|
|
6
|
+
@thread = nil
|
|
7
|
+
@stopping = false
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def start
|
|
11
|
+
return unless @config.valid? && @config.enabled
|
|
12
|
+
|
|
13
|
+
@thread = Thread.new do
|
|
14
|
+
::Rails.logger.info("[Lantern] Collector started (interval: #{@config.interval}s)")
|
|
15
|
+
loop do
|
|
16
|
+
break if @stopping
|
|
17
|
+
collect_and_report
|
|
18
|
+
sleep @config.interval
|
|
19
|
+
break if @stopping
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
@thread.abort_on_exception = false
|
|
24
|
+
@thread.name = "lantern-collector"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def stop
|
|
28
|
+
@stopping = true
|
|
29
|
+
@thread&.join(5)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def collect_and_report
|
|
35
|
+
payload = Collector.new.collect
|
|
36
|
+
return unless payload
|
|
37
|
+
|
|
38
|
+
Reporter.new(@config).report_snapshot(payload)
|
|
39
|
+
rescue => e
|
|
40
|
+
::Rails.logger.error("[Lantern] Runner error: #{e.message}")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "lantern/rails/version"
|
|
2
|
+
require "lantern/rails/configuration"
|
|
3
|
+
require "lantern/rails/git_metadata"
|
|
4
|
+
require "lantern/rails/collector"
|
|
5
|
+
require "lantern/rails/reporter"
|
|
6
|
+
require "lantern/rails/runner"
|
|
7
|
+
require "lantern/rails/railtie" if defined?(::Rails)
|
|
8
|
+
|
|
9
|
+
module Lantern
|
|
10
|
+
module Rails
|
|
11
|
+
class << self
|
|
12
|
+
def configuration
|
|
13
|
+
@configuration ||= Configuration.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def configure
|
|
17
|
+
yield configuration
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Manually report a deploy event. Call this from your CI/CD pipeline
|
|
21
|
+
# or a deploy hook in your Rails app.
|
|
22
|
+
#
|
|
23
|
+
# Example (in a Rake task or initializer after deploy):
|
|
24
|
+
# Lantern::Rails.report_deploy
|
|
25
|
+
def report_deploy(git_sha: nil, git_message: nil, git_author: nil, deployer: nil)
|
|
26
|
+
return unless configuration.valid?
|
|
27
|
+
|
|
28
|
+
git = GitMetadata.collect
|
|
29
|
+
|
|
30
|
+
payload = {
|
|
31
|
+
git_sha: git_sha || git[:sha],
|
|
32
|
+
git_message: git_message || git[:message],
|
|
33
|
+
git_author: git_author || git[:author],
|
|
34
|
+
deployer: deployer,
|
|
35
|
+
environment: configuration.environment,
|
|
36
|
+
deployed_at: Time.current.iso8601
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Reporter.new(configuration).report_deploy(payload)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lantern-rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Eric Mumbower
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.0'
|
|
26
|
+
executables: []
|
|
27
|
+
extensions: []
|
|
28
|
+
extra_rdoc_files: []
|
|
29
|
+
files:
|
|
30
|
+
- LICENSE
|
|
31
|
+
- README.md
|
|
32
|
+
- lib/lantern-rails.rb
|
|
33
|
+
- lib/lantern/rails/collector.rb
|
|
34
|
+
- lib/lantern/rails/configuration.rb
|
|
35
|
+
- lib/lantern/rails/git_metadata.rb
|
|
36
|
+
- lib/lantern/rails/railtie.rb
|
|
37
|
+
- lib/lantern/rails/reporter.rb
|
|
38
|
+
- lib/lantern/rails/runner.rb
|
|
39
|
+
- lib/lantern/rails/version.rb
|
|
40
|
+
homepage: https://uselantern.dev
|
|
41
|
+
licenses:
|
|
42
|
+
- MIT
|
|
43
|
+
metadata: {}
|
|
44
|
+
rdoc_options: []
|
|
45
|
+
require_paths:
|
|
46
|
+
- lib
|
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
48
|
+
requirements:
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '3.1'
|
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '0'
|
|
57
|
+
requirements: []
|
|
58
|
+
rubygems_version: 3.6.9
|
|
59
|
+
specification_version: 4
|
|
60
|
+
summary: Postgres monitoring collector for Rails apps — sends health metrics to Lantern.
|
|
61
|
+
test_files: []
|