error_radar 1.0.6 → 1.0.7
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/app/controllers/error_radar/settings_controller.rb +41 -0
- data/app/models/error_radar/setting.rb +36 -0
- data/app/views/error_radar/settings/show.html.erb +59 -0
- data/app/views/layouts/error_radar/application.html.erb +1 -0
- data/config/routes.rb +3 -0
- data/lib/error_radar/configuration.rb +13 -0
- data/lib/error_radar/notifications/chatwork.rb +58 -0
- data/lib/error_radar/notifier.rb +37 -0
- data/lib/error_radar/version.rb +1 -1
- data/lib/generators/error_radar/upgrade_v107/templates/create_error_radar_settings.rb.tt +10 -0
- data/lib/generators/error_radar/upgrade_v107/upgrade_v107_generator.rb +20 -0
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 97d73a27edbc8351ce6ff95be373a45ac9213057b332affd769980223a643b37
|
|
4
|
+
data.tar.gz: d079439643d93715219b0297184f3b595d8cf609a6bb8cd74633d714aa142033
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f55e7e0ca86fb47c6d739fdc13816380cfbc58378f8fea6b75b2db8867393a537ced7d50bca30e81025c5fb5cc2c5fe5ff9c12956b7314eaa16ab8962690da09
|
|
7
|
+
data.tar.gz: 77610793d856a3dbc46621ad99edb937724a8280edaa90e03bc3a65e7b2e180b4ddc77ae1476699b5129d11b63b00aa5daca4f5db88f8596ae7322b21db04957
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ErrorRadar
|
|
4
|
+
class SettingsController < ApplicationController
|
|
5
|
+
before_action :authenticate_request!
|
|
6
|
+
|
|
7
|
+
CHATWORK_KEYS = %w[chatwork_api_token chatwork_room_id chatwork_notify_sources].freeze
|
|
8
|
+
|
|
9
|
+
def show
|
|
10
|
+
@settings = CHATWORK_KEYS.each_with_object({}) do |key, h|
|
|
11
|
+
h[key] = Setting.get(key)
|
|
12
|
+
end
|
|
13
|
+
# notify_sources is stored as an array; join for the textarea
|
|
14
|
+
@settings['chatwork_notify_sources'] = Array(@settings['chatwork_notify_sources']).join("\n")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update
|
|
18
|
+
CHATWORK_KEYS.each do |key|
|
|
19
|
+
value = settings_params[key]
|
|
20
|
+
next if value.nil?
|
|
21
|
+
|
|
22
|
+
if key == 'chatwork_notify_sources'
|
|
23
|
+
lines = value.split(/[\n,]+/).map(&:strip).reject(&:empty?)
|
|
24
|
+
Setting.set(key, lines)
|
|
25
|
+
else
|
|
26
|
+
Setting.set(key, value.strip.presence)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
redirect_to settings_path, notice: 'Settings saved.'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def settings_params
|
|
36
|
+
params.require(:settings).permit(*CHATWORK_KEYS)
|
|
37
|
+
rescue ActionController::ParameterMissing
|
|
38
|
+
{}
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ErrorRadar
|
|
4
|
+
# Key-value store for UI-configurable settings (e.g. ChatWork credentials).
|
|
5
|
+
# Values are JSON-encoded so arrays/booleans survive the round-trip.
|
|
6
|
+
class Setting < ApplicationRecord
|
|
7
|
+
validates :key, presence: true, uniqueness: true
|
|
8
|
+
|
|
9
|
+
CHATWORK_KEYS = %w[
|
|
10
|
+
chatwork_api_token
|
|
11
|
+
chatwork_room_id
|
|
12
|
+
chatwork_notify_sources
|
|
13
|
+
].freeze
|
|
14
|
+
|
|
15
|
+
def self.get(key)
|
|
16
|
+
row = find_by(key: key.to_s)
|
|
17
|
+
return nil if row.nil? || row.value.nil?
|
|
18
|
+
|
|
19
|
+
JSON.parse(row.value)
|
|
20
|
+
rescue JSON::ParserError
|
|
21
|
+
row.value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.set(key, value)
|
|
25
|
+
row = find_or_initialize_by(key: key.to_s)
|
|
26
|
+
row.value = value.nil? ? nil : value.to_json
|
|
27
|
+
row.save!
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.chatwork_settings
|
|
31
|
+
CHATWORK_KEYS.each_with_object({}) do |key, hash|
|
|
32
|
+
hash[key] = get(key)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<h1>Settings</h1>
|
|
2
|
+
<p class="sub">Configure notification channels. Changes take effect immediately — no deploy needed.</p>
|
|
3
|
+
|
|
4
|
+
<% if notice.present? %>
|
|
5
|
+
<div style="background:#d1fae5;border:1px solid #6ee7b7;color:#065f46;padding:10px 14px;border-radius:8px;margin-bottom:16px;font-size:13px;">
|
|
6
|
+
<%= notice %>
|
|
7
|
+
</div>
|
|
8
|
+
<% end %>
|
|
9
|
+
|
|
10
|
+
<%= form_with url: settings_path, method: :patch, html: { style: 'max-width:640px' } do |f| %>
|
|
11
|
+
<div class="panel" style="margin-bottom:20px;">
|
|
12
|
+
<h2>ChatWork Notifications</h2>
|
|
13
|
+
<p style="font-size:12px;color:#6b7280;margin:0 0 16px;">
|
|
14
|
+
Send error alerts to a ChatWork room. Leave Token or Room ID blank to disable.
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<div style="margin-bottom:14px;">
|
|
18
|
+
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:4px;" for="chatwork_api_token">
|
|
19
|
+
API Token
|
|
20
|
+
</label>
|
|
21
|
+
<%= f.text_field 'settings[chatwork_api_token]',
|
|
22
|
+
value: @settings['chatwork_api_token'],
|
|
23
|
+
id: 'chatwork_api_token',
|
|
24
|
+
placeholder: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
|
25
|
+
style: 'width:100%;padding:8px 10px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;font-family:monospace;' %>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div style="margin-bottom:14px;">
|
|
29
|
+
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:4px;" for="chatwork_room_id">
|
|
30
|
+
Room ID
|
|
31
|
+
</label>
|
|
32
|
+
<%= f.text_field 'settings[chatwork_room_id]',
|
|
33
|
+
value: @settings['chatwork_room_id'],
|
|
34
|
+
id: 'chatwork_room_id',
|
|
35
|
+
placeholder: '123456789',
|
|
36
|
+
style: 'width:100%;padding:8px 10px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;font-family:monospace;' %>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div style="margin-bottom:20px;">
|
|
40
|
+
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:4px;" for="chatwork_notify_sources">
|
|
41
|
+
Notify Sources <span style="font-weight:400;color:#6b7280;">(one per line — leave blank to notify all sources)</span>
|
|
42
|
+
</label>
|
|
43
|
+
<%= f.text_area 'settings[chatwork_notify_sources]',
|
|
44
|
+
value: @settings['chatwork_notify_sources'],
|
|
45
|
+
id: 'chatwork_notify_sources',
|
|
46
|
+
placeholder: "Token::ValidatorService\nPostScheduler::",
|
|
47
|
+
rows: 5,
|
|
48
|
+
style: 'width:100%;padding:8px 10px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;font-family:monospace;resize:vertical;' %>
|
|
49
|
+
<p style="font-size:11px;color:#6b7280;margin:4px 0 0;">
|
|
50
|
+
Substring match. An error is sent to ChatWork only when its <strong>source</strong> contains one of these strings.
|
|
51
|
+
</p>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div>
|
|
55
|
+
<%= f.submit 'Save settings',
|
|
56
|
+
style: 'background:#2563eb;color:#fff;border:none;padding:9px 20px;border-radius:6px;font-size:13px;cursor:pointer;font-weight:600;' %>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
<% end %>
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
<a href="<%= root_path %>" class="nav-link <%= 'active' if controller_name == 'dashboard' %>">Dashboard</a>
|
|
58
58
|
<a href="<%= errors_path %>" class="nav-link <%= 'active' if controller_name == 'errors' %>">All Errors</a>
|
|
59
59
|
<a href="<%= guide_path %>" class="nav-link <%= 'active' if controller_name == 'guide' %>">Guide</a>
|
|
60
|
+
<a href="<%= settings_path %>" class="nav-link <%= 'active' if controller_name == 'settings' %>">Settings</a>
|
|
60
61
|
</div>
|
|
61
62
|
</nav>
|
|
62
63
|
<div class="wrap"><%= yield %></div>
|
data/config/routes.rb
CHANGED
|
@@ -57,6 +57,16 @@ module ErrorRadar
|
|
|
57
57
|
# One or more plain HTTPS URLs that receive a POST with JSON body on each alert.
|
|
58
58
|
attr_accessor :webhook_urls
|
|
59
59
|
|
|
60
|
+
# ChatWork ────────────────────────────────────────────────────────────────
|
|
61
|
+
# API token from https://www.chatwork.com/service/packages/chatwork/subpackages/api/apply_token.php
|
|
62
|
+
attr_accessor :chatwork_api_token
|
|
63
|
+
# Room ID to post messages into (numeric string).
|
|
64
|
+
attr_accessor :chatwork_room_id
|
|
65
|
+
# Filter by source: array of strings/regexps. Only errors whose `source`
|
|
66
|
+
# matches at least one entry are sent to ChatWork. nil or [] = notify all.
|
|
67
|
+
# Example: ['Maintain::CheckSidekiqServ', /FetchInsights/]
|
|
68
|
+
attr_accessor :chatwork_notify_sources
|
|
69
|
+
|
|
60
70
|
# App name shown in notification subject/title. Defaults to Rails app name.
|
|
61
71
|
attr_accessor :app_name
|
|
62
72
|
# Base URL used to build deep-links (e.g. "https://myapp.com").
|
|
@@ -165,6 +175,9 @@ module ErrorRadar
|
|
|
165
175
|
@email_recipients = []
|
|
166
176
|
@email_from = nil
|
|
167
177
|
@webhook_urls = []
|
|
178
|
+
@chatwork_api_token = nil
|
|
179
|
+
@chatwork_room_id = nil
|
|
180
|
+
@chatwork_notify_sources = []
|
|
168
181
|
@app_name = nil
|
|
169
182
|
@app_host = nil
|
|
170
183
|
@error_callbacks = []
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
|
|
6
|
+
module ErrorRadar
|
|
7
|
+
module Notifications
|
|
8
|
+
module Chatwork
|
|
9
|
+
API_BASE = 'https://api.chatwork.com/v2'.freeze
|
|
10
|
+
|
|
11
|
+
def self.deliver(log, event = :recurring)
|
|
12
|
+
cfg = ErrorRadar.config
|
|
13
|
+
token, room_id, = ErrorRadar::Notifier.chatwork_db_settings(cfg)
|
|
14
|
+
return if token.empty? || room_id.empty?
|
|
15
|
+
|
|
16
|
+
uri = URI("#{API_BASE}/rooms/#{room_id}/messages")
|
|
17
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
18
|
+
http.use_ssl = true
|
|
19
|
+
http.open_timeout = 5
|
|
20
|
+
http.read_timeout = 5
|
|
21
|
+
|
|
22
|
+
req = Net::HTTP::Post.new(uri)
|
|
23
|
+
req['X-ChatWorkToken'] = token
|
|
24
|
+
req['Content-Type'] = 'application/x-www-form-urlencoded'
|
|
25
|
+
req.body = URI.encode_www_form(body: build_message(log, event))
|
|
26
|
+
|
|
27
|
+
http.request(req)
|
|
28
|
+
rescue StandardError => e
|
|
29
|
+
ErrorRadar::Tracking.warn_internal("ChatWork notification failed: #{e.message}")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.build_message(log, event)
|
|
33
|
+
app = ErrorRadar::Notifier.app_name
|
|
34
|
+
link = ErrorRadar::Notifier.error_url(log)
|
|
35
|
+
|
|
36
|
+
title = case event
|
|
37
|
+
when :spike
|
|
38
|
+
spike = log.instance_variable_get(:@spike_data)
|
|
39
|
+
"Spike — #{spike[:count]} hits in #{spike[:window_minutes]} min: #{log.error_class}"
|
|
40
|
+
when :new_error
|
|
41
|
+
"New error: #{log.error_class}"
|
|
42
|
+
else
|
|
43
|
+
"#{log.severity.capitalize} error: #{log.error_class}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
body_lines = []
|
|
47
|
+
body_lines << "Source: #{log.source || 'unknown'}"
|
|
48
|
+
body_lines << "Category: #{log.category}"
|
|
49
|
+
body_lines << "Severity: #{log.severity}"
|
|
50
|
+
body_lines << "Occurrences: #{log.occurrences}"
|
|
51
|
+
body_lines << "Message: #{log.message.to_s.truncate(300)}"
|
|
52
|
+
body_lines << "URL: #{link}" if link
|
|
53
|
+
|
|
54
|
+
"[info][title][#{app}] #{title}[/title]#{body_lines.join("\n")}[/info]"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/error_radar/notifier.rb
CHANGED
|
@@ -82,6 +82,7 @@ module ErrorRadar
|
|
|
82
82
|
send_slack(log, event) if cfg.slack_webhook_url.to_s.start_with?('http')
|
|
83
83
|
send_discord(log, event) if cfg.discord_webhook_url.to_s.start_with?('http')
|
|
84
84
|
send_email(log, event) if cfg.email_recipients.any?
|
|
85
|
+
send_chatwork(log, event) if chatwork_enabled?(cfg, log)
|
|
85
86
|
cfg.webhook_urls.each { |url| send_webhook(log, url, event) }
|
|
86
87
|
cfg.error_callbacks.each { |cb| safe_call(cb, log) }
|
|
87
88
|
end
|
|
@@ -106,6 +107,42 @@ module ErrorRadar
|
|
|
106
107
|
Notifications::Webhook.deliver(log, url: url, event: event)
|
|
107
108
|
end
|
|
108
109
|
|
|
110
|
+
def self.send_chatwork(log, event)
|
|
111
|
+
require 'error_radar/notifications/chatwork'
|
|
112
|
+
Notifications::Chatwork.deliver(log, event)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def self.chatwork_enabled?(cfg, log)
|
|
116
|
+
token, room_id, sources = chatwork_db_settings(cfg)
|
|
117
|
+
return false if token.empty? || room_id.empty?
|
|
118
|
+
|
|
119
|
+
return true if sources.empty?
|
|
120
|
+
|
|
121
|
+
source = log.source.to_s
|
|
122
|
+
sources.any? { |filter| filter.is_a?(Regexp) ? filter.match?(source) : source.include?(filter.to_s) }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Returns [token, room_id, sources_array] — DB settings take priority over config.
|
|
126
|
+
def self.chatwork_db_settings(cfg)
|
|
127
|
+
if defined?(ErrorRadar::Setting) && ErrorRadar::Setting.table_exists?
|
|
128
|
+
token = ErrorRadar::Setting.get('chatwork_api_token').to_s
|
|
129
|
+
room_id = ErrorRadar::Setting.get('chatwork_room_id').to_s
|
|
130
|
+
sources = Array(ErrorRadar::Setting.get('chatwork_notify_sources')).compact
|
|
131
|
+
# Fall back to config when DB row is empty
|
|
132
|
+
token = cfg.chatwork_api_token.to_s if token.empty?
|
|
133
|
+
room_id = cfg.chatwork_room_id.to_s if room_id.empty?
|
|
134
|
+
sources = Array(cfg.chatwork_notify_sources).compact if sources.empty?
|
|
135
|
+
else
|
|
136
|
+
token = cfg.chatwork_api_token.to_s
|
|
137
|
+
room_id = cfg.chatwork_room_id.to_s
|
|
138
|
+
sources = Array(cfg.chatwork_notify_sources).compact
|
|
139
|
+
end
|
|
140
|
+
[token, room_id, sources]
|
|
141
|
+
rescue StandardError
|
|
142
|
+
[cfg.chatwork_api_token.to_s, cfg.chatwork_room_id.to_s,
|
|
143
|
+
Array(cfg.chatwork_notify_sources).compact]
|
|
144
|
+
end
|
|
145
|
+
|
|
109
146
|
def self.safe_call(cb, log)
|
|
110
147
|
cb.call(log)
|
|
111
148
|
rescue StandardError => e
|
data/lib/error_radar/version.rb
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators/active_record'
|
|
4
|
+
|
|
5
|
+
module ErrorRadar
|
|
6
|
+
module Generators
|
|
7
|
+
class UpgradeV107Generator < ActiveRecord::Generators::Base
|
|
8
|
+
source_root File.expand_path('templates', __dir__)
|
|
9
|
+
|
|
10
|
+
argument :name, type: :string, default: 'upgrade_v107'
|
|
11
|
+
|
|
12
|
+
desc 'Creates the error_radar_settings table for UI-configurable notification settings (v1.0.7).'
|
|
13
|
+
|
|
14
|
+
def create_migration
|
|
15
|
+
migration_template 'create_error_radar_settings.rb.tt',
|
|
16
|
+
'db/migrate/create_error_radar_settings.rb'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: error_radar
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- chienbn9x
|
|
@@ -64,6 +64,7 @@ files:
|
|
|
64
64
|
- app/controllers/error_radar/dashboard_controller.rb
|
|
65
65
|
- app/controllers/error_radar/errors_controller.rb
|
|
66
66
|
- app/controllers/error_radar/guide_controller.rb
|
|
67
|
+
- app/controllers/error_radar/settings_controller.rb
|
|
67
68
|
- app/mailers/error_radar/digest_mailer.rb
|
|
68
69
|
- app/mailers/error_radar/error_mailer.rb
|
|
69
70
|
- app/models/error_radar/application_record.rb
|
|
@@ -71,6 +72,7 @@ files:
|
|
|
71
72
|
- app/models/error_radar/error_comment.rb
|
|
72
73
|
- app/models/error_radar/error_log.rb
|
|
73
74
|
- app/models/error_radar/error_occurrence.rb
|
|
75
|
+
- app/models/error_radar/setting.rb
|
|
74
76
|
- app/views/error_radar/dashboard/index.html.erb
|
|
75
77
|
- app/views/error_radar/dashboard/show.html.erb
|
|
76
78
|
- app/views/error_radar/digest_mailer/digest.html.erb
|
|
@@ -80,6 +82,7 @@ files:
|
|
|
80
82
|
- app/views/error_radar/errors/index.html.erb
|
|
81
83
|
- app/views/error_radar/errors/show.html.erb
|
|
82
84
|
- app/views/error_radar/guide/index.html.erb
|
|
85
|
+
- app/views/error_radar/settings/show.html.erb
|
|
83
86
|
- app/views/layouts/error_radar/application.html.erb
|
|
84
87
|
- config/routes.rb
|
|
85
88
|
- lib/error_radar.rb
|
|
@@ -94,6 +97,7 @@ files:
|
|
|
94
97
|
- lib/error_radar/integrations/rake.rb
|
|
95
98
|
- lib/error_radar/integrations/sidekiq.rb
|
|
96
99
|
- lib/error_radar/middleware.rb
|
|
100
|
+
- lib/error_radar/notifications/chatwork.rb
|
|
97
101
|
- lib/error_radar/notifications/discord.rb
|
|
98
102
|
- lib/error_radar/notifications/email.rb
|
|
99
103
|
- lib/error_radar/notifications/slack.rb
|
|
@@ -112,6 +116,8 @@ files:
|
|
|
112
116
|
- lib/generators/error_radar/upgrade_v060/upgrade_v060_generator.rb
|
|
113
117
|
- lib/generators/error_radar/upgrade_v100/templates/upgrade_to_v100.rb.tt
|
|
114
118
|
- lib/generators/error_radar/upgrade_v100/upgrade_v100_generator.rb
|
|
119
|
+
- lib/generators/error_radar/upgrade_v107/templates/create_error_radar_settings.rb.tt
|
|
120
|
+
- lib/generators/error_radar/upgrade_v107/upgrade_v107_generator.rb
|
|
115
121
|
- lib/tasks/error_radar.rake
|
|
116
122
|
homepage: https://github.com/chienbn9x/error_radar
|
|
117
123
|
licenses:
|