newshound 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/Gemfile +9 -0
- data/README.md +144 -0
- data/Rakefile +10 -0
- data/lib/newshound/configuration.rb +24 -0
- data/lib/newshound/daily_report_job.rb +19 -0
- data/lib/newshound/exception_reporter.rb +83 -0
- data/lib/newshound/que_reporter.rb +118 -0
- data/lib/newshound/railtie.rb +63 -0
- data/lib/newshound/scheduler.rb +41 -0
- data/lib/newshound/slack_notifier.rb +61 -0
- data/lib/newshound/version.rb +5 -0
- data/lib/newshound.rb +66 -0
- metadata +196 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: decd51fd02b481edd644a56bd700575010ae0b3d747fc2880803c68c32e50bfa
|
4
|
+
data.tar.gz: f44304b8a0f6621296ff13900a2e59ea702b62c08ed6491ad0a60b4839b76b6d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 813c1c7d310854f34722819c8321d24438cafd543bf9c4dcf164e90bcf716423953ae0583656fe97476d454342fb8c4dabdfa48b66d886d6adb21abc6466521b
|
7
|
+
data.tar.gz: 6c60ed849cd31e6e08066dc9893c2dcd3d3a82450923d97383717f2c5e21f2ea4943880b73ccce9926cbed83ead51b9cea0b2c08c91ba1fb8b63a6a8fb66e983
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# Newshound 🐕
|
2
|
+
|
3
|
+
A Ruby gem that sniffs out exceptions and job statuses in your Rails app and reports them daily to Slack.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- 📊 Daily Que job status reports (counts by job type, queue health)
|
8
|
+
- 🚨 Last 4 exceptions from exception-track
|
9
|
+
- 💬 Slack integration via webhook or Web API
|
10
|
+
- ⏰ Automatic daily scheduling with que-scheduler
|
11
|
+
- 🔧 Configurable report times and limits
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add to your Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'newshound', path: 'path/to/newshound' # or from git/rubygems when published
|
19
|
+
```
|
20
|
+
|
21
|
+
Then:
|
22
|
+
|
23
|
+
```bash
|
24
|
+
bundle install
|
25
|
+
```
|
26
|
+
|
27
|
+
## Configuration
|
28
|
+
|
29
|
+
Create an initializer `config/initializers/newshound.rb`:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
Newshound.configure do |config|
|
33
|
+
# Slack configuration (choose one method)
|
34
|
+
config.slack_webhook_url = ENV['SLACK_WEBHOOK_URL'] # Option 1: Webhook
|
35
|
+
# OR set ENV['SLACK_API_TOKEN'] for Web API # Option 2: Web API
|
36
|
+
|
37
|
+
config.slack_channel = "#ops-alerts" # Default: "#general"
|
38
|
+
config.report_time = "09:00" # Default: "09:00" (24-hour format)
|
39
|
+
config.exception_limit = 4 # Default: 4 (last N exceptions)
|
40
|
+
config.time_zone = "America/New_York" # Default: "America/New_York"
|
41
|
+
config.enabled = true # Default: true
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
### Slack Setup
|
46
|
+
|
47
|
+
#### Option 1: Webhook URL (Simpler)
|
48
|
+
1. Go to https://api.slack.com/apps
|
49
|
+
2. Create a new app or select existing
|
50
|
+
3. Enable "Incoming Webhooks"
|
51
|
+
4. Add a new webhook for your channel
|
52
|
+
5. Copy the webhook URL to your config
|
53
|
+
|
54
|
+
#### Option 2: Web API Token (More features)
|
55
|
+
1. Create a Slack app at https://api.slack.com/apps
|
56
|
+
2. Add OAuth scopes: `chat:write`, `chat:write.public`
|
57
|
+
3. Install to workspace
|
58
|
+
4. Copy the Bot User OAuth Token
|
59
|
+
5. Set as `ENV['SLACK_API_TOKEN']`
|
60
|
+
|
61
|
+
## Usage
|
62
|
+
|
63
|
+
### Automatic Daily Reports
|
64
|
+
|
65
|
+
If you have `que-scheduler` configured, Newshound will automatically schedule daily reports at your configured time.
|
66
|
+
|
67
|
+
### Manual Reports
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
# Send report immediately
|
71
|
+
Newshound.report!
|
72
|
+
|
73
|
+
# Or via rake task
|
74
|
+
rake newshound:report_now
|
75
|
+
|
76
|
+
# Enqueue for background processing
|
77
|
+
rake newshound:schedule
|
78
|
+
|
79
|
+
# Check configuration
|
80
|
+
rake newshound:config
|
81
|
+
```
|
82
|
+
|
83
|
+
### Integration with que-scheduler
|
84
|
+
|
85
|
+
Add to your `config/que_schedule.yml`:
|
86
|
+
|
87
|
+
```yaml
|
88
|
+
newshound_daily_report:
|
89
|
+
class: "Newshound::DailyReportJob"
|
90
|
+
cron: "0 9 * * *" # 9:00 AM daily
|
91
|
+
queue: default
|
92
|
+
```
|
93
|
+
|
94
|
+
Or let Newshound auto-configure based on your settings.
|
95
|
+
|
96
|
+
## Report Format
|
97
|
+
|
98
|
+
The daily report includes:
|
99
|
+
|
100
|
+
### Exception Section
|
101
|
+
- Last 4 exceptions (configurable)
|
102
|
+
- Exception class, message, controller/action
|
103
|
+
- Count of same exception type in last 24 hours
|
104
|
+
- Timestamp
|
105
|
+
|
106
|
+
### Que Jobs Section
|
107
|
+
- Job counts by type (success/failed/total)
|
108
|
+
- Queue health status
|
109
|
+
- Ready to run count
|
110
|
+
- Scheduled jobs count
|
111
|
+
- Failed jobs in retry queue
|
112
|
+
- Jobs completed today
|
113
|
+
|
114
|
+
## Development
|
115
|
+
|
116
|
+
```bash
|
117
|
+
# Run tests
|
118
|
+
bundle exec rspec
|
119
|
+
|
120
|
+
# Run linter
|
121
|
+
bundle exec rubocop
|
122
|
+
|
123
|
+
# Console
|
124
|
+
bin/console
|
125
|
+
```
|
126
|
+
|
127
|
+
## Testing in Your App
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
# In rails console
|
131
|
+
Newshound.configuration.slack_webhook_url = "your-webhook"
|
132
|
+
Newshound.report! # Should post to Slack immediately
|
133
|
+
```
|
134
|
+
|
135
|
+
## Troubleshooting
|
136
|
+
|
137
|
+
- **No reports sent**: Check `rake newshound:config` to verify configuration
|
138
|
+
- **No exceptions showing**: Ensure `exception-track` gem is installed and logging
|
139
|
+
- **No job data**: Verify Que is configured and `que_jobs` table exists
|
140
|
+
- **Slack not receiving**: Verify webhook URL or API token is correct
|
141
|
+
|
142
|
+
## License
|
143
|
+
|
144
|
+
MIT
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Newshound
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :slack_webhook_url, :slack_channel, :report_time,
|
6
|
+
:exception_limit, :time_zone, :enabled
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@slack_webhook_url = nil
|
10
|
+
@slack_channel = "#general"
|
11
|
+
@report_time = "09:00"
|
12
|
+
@exception_limit = 4
|
13
|
+
@time_zone = "America/New_York"
|
14
|
+
@enabled = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def valid?
|
18
|
+
return false unless enabled
|
19
|
+
return false if slack_webhook_url.nil? || slack_webhook_url.empty?
|
20
|
+
|
21
|
+
true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Newshound
|
4
|
+
class DailyReportJob < ::Que::Job
|
5
|
+
def run
|
6
|
+
return unless Newshound.configuration.valid?
|
7
|
+
|
8
|
+
Newshound.report!
|
9
|
+
|
10
|
+
destroy
|
11
|
+
rescue StandardError => e
|
12
|
+
Rails.logger.error "Newshound::DailyReportJob failed: #{e.message}"
|
13
|
+
Rails.logger.error e.backtrace.join("\n")
|
14
|
+
|
15
|
+
# Re-raise to let Que handle retry logic
|
16
|
+
raise
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Newshound
|
4
|
+
class ExceptionReporter
|
5
|
+
def generate_report
|
6
|
+
return no_exceptions_block if recent_exceptions.empty?
|
7
|
+
|
8
|
+
[
|
9
|
+
{
|
10
|
+
type: "section",
|
11
|
+
text: {
|
12
|
+
type: "mrkdwn",
|
13
|
+
text: "*🚨 Recent Exceptions (Last 24 Hours)*"
|
14
|
+
}
|
15
|
+
},
|
16
|
+
*format_exceptions
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def recent_exceptions
|
23
|
+
@recent_exceptions ||= fetch_recent_exceptions
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch_recent_exceptions
|
27
|
+
return [] unless defined?(ExceptionTrack::Log)
|
28
|
+
|
29
|
+
ExceptionTrack::Log
|
30
|
+
.where("created_at >= ?", 24.hours.ago)
|
31
|
+
.order(created_at: :desc)
|
32
|
+
.limit(Newshound.configuration.exception_limit)
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_exceptions
|
36
|
+
recent_exceptions.map.with_index do |exception, index|
|
37
|
+
{
|
38
|
+
type: "section",
|
39
|
+
text: {
|
40
|
+
type: "mrkdwn",
|
41
|
+
text: format_exception_text(exception, index + 1)
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def format_exception_text(exception, number)
|
48
|
+
<<~TEXT
|
49
|
+
*#{number}. #{exception.title || exception.exception_class}*
|
50
|
+
• *Time:* #{exception.created_at.strftime('%I:%M %p')}
|
51
|
+
• *Controller:* #{exception.controller_name}##{exception.action_name}
|
52
|
+
• *Count:* #{exception_count(exception)}
|
53
|
+
#{format_message(exception)}
|
54
|
+
TEXT
|
55
|
+
end
|
56
|
+
|
57
|
+
def format_message(exception)
|
58
|
+
return "" unless exception.message.present?
|
59
|
+
|
60
|
+
message = exception.message.truncate(100)
|
61
|
+
"• *Message:* `#{message}`"
|
62
|
+
end
|
63
|
+
|
64
|
+
def exception_count(exception)
|
65
|
+
ExceptionTrack::Log
|
66
|
+
.where(exception_class: exception.exception_class)
|
67
|
+
.where("created_at >= ?", 24.hours.ago)
|
68
|
+
.count
|
69
|
+
end
|
70
|
+
|
71
|
+
def no_exceptions_block
|
72
|
+
[
|
73
|
+
{
|
74
|
+
type: "section",
|
75
|
+
text: {
|
76
|
+
type: "mrkdwn",
|
77
|
+
text: "*✅ No Exceptions in the Last 24 Hours*"
|
78
|
+
}
|
79
|
+
}
|
80
|
+
]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Newshound
|
4
|
+
class QueReporter
|
5
|
+
def generate_report
|
6
|
+
[
|
7
|
+
{
|
8
|
+
type: "section",
|
9
|
+
text: {
|
10
|
+
type: "mrkdwn",
|
11
|
+
text: "*📊 Que Jobs Status*"
|
12
|
+
}
|
13
|
+
},
|
14
|
+
job_counts_section,
|
15
|
+
queue_health_section
|
16
|
+
].compact
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def job_counts_section
|
22
|
+
counts = job_counts_by_type
|
23
|
+
|
24
|
+
return no_jobs_section if counts.empty?
|
25
|
+
|
26
|
+
{
|
27
|
+
type: "section",
|
28
|
+
text: {
|
29
|
+
type: "mrkdwn",
|
30
|
+
text: format_job_counts(counts)
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def job_counts_by_type
|
36
|
+
return {} unless defined?(::Que::Job)
|
37
|
+
|
38
|
+
::Que::Job
|
39
|
+
.group(:job_class)
|
40
|
+
.group(:error_count)
|
41
|
+
.count
|
42
|
+
.each_with_object({}) do |((job_class, error_count), count), hash|
|
43
|
+
hash[job_class] ||= { success: 0, failed: 0, total: 0 }
|
44
|
+
|
45
|
+
if error_count.zero?
|
46
|
+
hash[job_class][:success] += count
|
47
|
+
else
|
48
|
+
hash[job_class][:failed] += count
|
49
|
+
end
|
50
|
+
|
51
|
+
hash[job_class][:total] += count
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def format_job_counts(counts)
|
56
|
+
lines = ["*Job Counts by Type:*"]
|
57
|
+
|
58
|
+
counts.each do |job_class, stats|
|
59
|
+
status_emoji = stats[:failed] > 0 ? "⚠️" : "✅"
|
60
|
+
lines << "• #{status_emoji} *#{job_class}*: #{stats[:total]} total (#{stats[:success]} success, #{stats[:failed]} failed)"
|
61
|
+
end
|
62
|
+
|
63
|
+
lines.join("\n")
|
64
|
+
end
|
65
|
+
|
66
|
+
def queue_health_section
|
67
|
+
stats = queue_statistics
|
68
|
+
|
69
|
+
{
|
70
|
+
type: "section",
|
71
|
+
text: {
|
72
|
+
type: "mrkdwn",
|
73
|
+
text: format_queue_health(stats)
|
74
|
+
}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def queue_statistics
|
79
|
+
return default_stats unless defined?(::Que::Job)
|
80
|
+
|
81
|
+
{
|
82
|
+
ready: ::Que::Job.where(finished_at: nil, expired_at: nil).where("run_at <= ?", Time.current).count,
|
83
|
+
scheduled: ::Que::Job.where(finished_at: nil, expired_at: nil).where("run_at > ?", Time.current).count,
|
84
|
+
failed: ::Que::Job.where.not(error_count: 0).where(finished_at: nil).count,
|
85
|
+
finished_today: ::Que::Job.where("finished_at >= ?", Date.current.beginning_of_day).count
|
86
|
+
}
|
87
|
+
rescue StandardError => e
|
88
|
+
Rails.logger.error "Failed to fetch Que statistics: #{e.message}"
|
89
|
+
default_stats
|
90
|
+
end
|
91
|
+
|
92
|
+
def default_stats
|
93
|
+
{ ready: 0, scheduled: 0, failed: 0, finished_today: 0 }
|
94
|
+
end
|
95
|
+
|
96
|
+
def format_queue_health(stats)
|
97
|
+
health_emoji = stats[:failed] > 10 ? "🔴" : stats[:failed] > 5 ? "🟡" : "🟢"
|
98
|
+
|
99
|
+
<<~TEXT
|
100
|
+
*Queue Health #{health_emoji}*
|
101
|
+
• *Ready to Run:* #{stats[:ready]}
|
102
|
+
• *Scheduled:* #{stats[:scheduled]}
|
103
|
+
• *Failed (Retry Queue):* #{stats[:failed]}
|
104
|
+
• *Completed Today:* #{stats[:finished_today]}
|
105
|
+
TEXT
|
106
|
+
end
|
107
|
+
|
108
|
+
def no_jobs_section
|
109
|
+
{
|
110
|
+
type: "section",
|
111
|
+
text: {
|
112
|
+
type: "mrkdwn",
|
113
|
+
text: "*No jobs found in the queue*"
|
114
|
+
}
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/railtie"
|
4
|
+
|
5
|
+
module Newshound
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
rake_tasks do
|
8
|
+
namespace :newshound do
|
9
|
+
desc "Send daily report immediately"
|
10
|
+
task report_now: :environment do
|
11
|
+
puts "Sending Newshound daily report..."
|
12
|
+
Newshound.report!
|
13
|
+
puts "Report sent successfully!"
|
14
|
+
rescue StandardError => e
|
15
|
+
puts "Failed to send report: #{e.message}"
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Schedule daily report job"
|
19
|
+
task schedule: :environment do
|
20
|
+
puts "Scheduling Newshound daily report..."
|
21
|
+
Newshound::Scheduler.run_now!
|
22
|
+
puts "Job enqueued successfully!"
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Show current configuration"
|
26
|
+
task config: :environment do
|
27
|
+
config = Newshound.configuration
|
28
|
+
puts "Newshound Configuration:"
|
29
|
+
puts " Enabled: #{config.enabled}"
|
30
|
+
puts " Slack Webhook: #{config.slack_webhook_url.present? ? '[CONFIGURED]' : '[NOT SET]'}"
|
31
|
+
puts " Slack Channel: #{config.slack_channel}"
|
32
|
+
puts " Report Time: #{config.report_time}"
|
33
|
+
puts " Exception Limit: #{config.exception_limit}"
|
34
|
+
puts " Time Zone: #{config.time_zone}"
|
35
|
+
puts " Valid: #{config.valid?}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
initializer "newshound.configure_que_scheduler" do
|
41
|
+
ActiveSupport.on_load(:active_record) do
|
42
|
+
if defined?(::Que::Scheduler)
|
43
|
+
require_relative "scheduler"
|
44
|
+
|
45
|
+
# Add our job to the que-scheduler configuration
|
46
|
+
schedule = Newshound::Scheduler.schedule_daily_report
|
47
|
+
|
48
|
+
Rails.logger.info "Newshound: Scheduled daily report at #{Newshound.configuration.report_time}"
|
49
|
+
else
|
50
|
+
Rails.logger.warn "Newshound: que-scheduler not found. Daily reports will need to be triggered manually."
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
config.after_initialize do
|
56
|
+
if Newshound.configuration.valid?
|
57
|
+
Rails.logger.info "Newshound initialized and ready to report!"
|
58
|
+
else
|
59
|
+
Rails.logger.warn "Newshound is not properly configured. Please check your configuration."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Newshound
|
4
|
+
class Scheduler
|
5
|
+
def self.schedule_daily_report
|
6
|
+
return unless defined?(::Que::Scheduler)
|
7
|
+
|
8
|
+
config = Newshound.configuration
|
9
|
+
return unless config.valid?
|
10
|
+
|
11
|
+
# Schedule the job using que-scheduler
|
12
|
+
# This will be picked up by que-scheduler's configuration
|
13
|
+
schedule_config = {
|
14
|
+
"newshound_daily_report" => {
|
15
|
+
"class" => "Newshound::DailyReportJob",
|
16
|
+
"cron" => build_cron_expression(config.report_time),
|
17
|
+
"queue" => "default",
|
18
|
+
"args" => []
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
# Merge with existing schedule if any
|
23
|
+
if defined?(::Que::Scheduler.configuration)
|
24
|
+
::Que::Scheduler.configuration.merge!(schedule_config)
|
25
|
+
end
|
26
|
+
|
27
|
+
schedule_config
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.build_cron_expression(time_string)
|
31
|
+
# Convert "09:00" format to cron expression
|
32
|
+
# Format: minute hour * * *
|
33
|
+
hour, minute = time_string.split(":").map(&:to_i)
|
34
|
+
"#{minute} #{hour} * * *"
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.run_now!
|
38
|
+
Newshound::DailyReportJob.enqueue
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "slack-ruby-client"
|
4
|
+
|
5
|
+
module Newshound
|
6
|
+
class SlackNotifier
|
7
|
+
def initialize
|
8
|
+
configure_slack_client
|
9
|
+
end
|
10
|
+
|
11
|
+
def post(message)
|
12
|
+
return unless valid_configuration?
|
13
|
+
|
14
|
+
if webhook_configured?
|
15
|
+
post_via_webhook(message)
|
16
|
+
elsif web_api_configured?
|
17
|
+
post_via_web_api(message)
|
18
|
+
else
|
19
|
+
Rails.logger.error "Newshound: No valid Slack configuration found"
|
20
|
+
end
|
21
|
+
rescue StandardError => e
|
22
|
+
Rails.logger.error "Newshound: Failed to send Slack notification: #{e.message}"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def configure_slack_client
|
28
|
+
Slack.configure do |config|
|
29
|
+
config.token = ENV["SLACK_API_TOKEN"] if ENV["SLACK_API_TOKEN"]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def valid_configuration?
|
34
|
+
return false unless Newshound.configuration.valid?
|
35
|
+
|
36
|
+
webhook_configured? || web_api_configured?
|
37
|
+
end
|
38
|
+
|
39
|
+
def webhook_configured?
|
40
|
+
Newshound.configuration.slack_webhook_url.present?
|
41
|
+
end
|
42
|
+
|
43
|
+
def web_api_configured?
|
44
|
+
ENV["SLACK_API_TOKEN"].present?
|
45
|
+
end
|
46
|
+
|
47
|
+
def post_via_webhook(message)
|
48
|
+
client = Slack::Incoming::Webhook.new(Newshound.configuration.slack_webhook_url)
|
49
|
+
client.post(message)
|
50
|
+
end
|
51
|
+
|
52
|
+
def post_via_web_api(message)
|
53
|
+
client = Slack::Web::Client.new
|
54
|
+
client.chat_postMessage(
|
55
|
+
channel: Newshound.configuration.slack_channel,
|
56
|
+
blocks: message[:blocks],
|
57
|
+
text: "Daily Newshound Report"
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/newshound.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "newshound/version"
|
4
|
+
require_relative "newshound/configuration"
|
5
|
+
require_relative "newshound/exception_reporter"
|
6
|
+
require_relative "newshound/que_reporter"
|
7
|
+
require_relative "newshound/slack_notifier"
|
8
|
+
require_relative "newshound/daily_report_job"
|
9
|
+
require_relative "newshound/scheduler"
|
10
|
+
require_relative "newshound/railtie" if defined?(Rails)
|
11
|
+
|
12
|
+
module Newshound
|
13
|
+
class Error < StandardError; end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def configuration
|
17
|
+
@configuration ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def configure
|
21
|
+
yield(configuration)
|
22
|
+
end
|
23
|
+
|
24
|
+
def report!
|
25
|
+
slack_notifier = SlackNotifier.new
|
26
|
+
|
27
|
+
exception_report = ExceptionReporter.new.generate_report
|
28
|
+
que_report = QueReporter.new.generate_report
|
29
|
+
|
30
|
+
message = format_daily_report(exception_report, que_report)
|
31
|
+
slack_notifier.post(message)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def format_daily_report(exception_report, que_report)
|
37
|
+
{
|
38
|
+
blocks: [
|
39
|
+
{
|
40
|
+
type: "header",
|
41
|
+
text: {
|
42
|
+
type: "plain_text",
|
43
|
+
text: "🐕 Daily Newshound Report",
|
44
|
+
emoji: true
|
45
|
+
}
|
46
|
+
},
|
47
|
+
{
|
48
|
+
type: "section",
|
49
|
+
text: {
|
50
|
+
type: "mrkdwn",
|
51
|
+
text: "*Date:* #{Date.current.strftime('%B %d, %Y')}"
|
52
|
+
}
|
53
|
+
},
|
54
|
+
{
|
55
|
+
type: "divider"
|
56
|
+
},
|
57
|
+
*exception_report,
|
58
|
+
{
|
59
|
+
type: "divider"
|
60
|
+
},
|
61
|
+
*que_report
|
62
|
+
]
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
metadata
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: newshound
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- salbanez
|
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: rails
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '6.0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '6.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: slack-ruby-client
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: que
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.0'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: que-scheduler
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '4.0'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '4.0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: exception-track
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0.1'
|
75
|
+
type: :runtime
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0.1'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: bundler
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '2.0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '2.0'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: rake
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '13.0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '13.0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rspec
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '3.0'
|
117
|
+
type: :development
|
118
|
+
prerelease: false
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '3.0'
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: rubocop
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '1.0'
|
131
|
+
type: :development
|
132
|
+
prerelease: false
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - "~>"
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '1.0'
|
138
|
+
- !ruby/object:Gem::Dependency
|
139
|
+
name: pg
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
type: :development
|
146
|
+
prerelease: false
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
description: Newshound sniffs out exceptions and job statuses in your Rails app and
|
153
|
+
reports them daily to Slack
|
154
|
+
email:
|
155
|
+
- salbanez@example.com
|
156
|
+
executables: []
|
157
|
+
extensions: []
|
158
|
+
extra_rdoc_files: []
|
159
|
+
files:
|
160
|
+
- Gemfile
|
161
|
+
- README.md
|
162
|
+
- Rakefile
|
163
|
+
- lib/newshound.rb
|
164
|
+
- lib/newshound/configuration.rb
|
165
|
+
- lib/newshound/daily_report_job.rb
|
166
|
+
- lib/newshound/exception_reporter.rb
|
167
|
+
- lib/newshound/que_reporter.rb
|
168
|
+
- lib/newshound/railtie.rb
|
169
|
+
- lib/newshound/scheduler.rb
|
170
|
+
- lib/newshound/slack_notifier.rb
|
171
|
+
- lib/newshound/version.rb
|
172
|
+
homepage: https://github.com/salbanez/newshound
|
173
|
+
licenses:
|
174
|
+
- MIT
|
175
|
+
metadata:
|
176
|
+
homepage_uri: https://github.com/salbanez/newshound
|
177
|
+
source_code_uri: https://github.com/salbanez/newshound
|
178
|
+
changelog_uri: https://github.com/salbanez/newshound/blob/main/CHANGELOG.md
|
179
|
+
rdoc_options: []
|
180
|
+
require_paths:
|
181
|
+
- lib
|
182
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: 2.7.0
|
187
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
188
|
+
requirements:
|
189
|
+
- - ">="
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
192
|
+
requirements: []
|
193
|
+
rubygems_version: 3.6.9
|
194
|
+
specification_version: 4
|
195
|
+
summary: Daily Slack reporter for Que jobs status and exception tracking
|
196
|
+
test_files: []
|