newshound 0.2.0 → 0.2.1
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/lib/newshound/exception_reporter.rb +91 -12
- data/lib/newshound/middleware/banner_injector.rb +4 -4
- data/lib/newshound/que_reporter.rb +54 -20
- data/lib/newshound/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3128baf64381d76f8ba53efd4887d333b875ba981d08f9ca6d6ebde9f5d23921
|
|
4
|
+
data.tar.gz: 35110f37798b54c435be13f5ebe0a1d1dfeb0e2629a676fa51c15b2ebafee9ef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d99bc945f8fd1a1bb0550c94a215006833b962e815a4450fc84281e3ca6d0f37fa98fd6ecfedd5aee9e1f4dc4ad93da9e4cf30710906a0651d1e1959a554c47
|
|
7
|
+
data.tar.gz: b01b19aa141e0fe32db7dd188487fd6b8e7840b68194bf1b06b1f666c463240a353d3d8f0c1c408a2d60d0c58a13df517c83b78303d78ebd16caa504a0552180
|
|
@@ -24,6 +24,14 @@ module Newshound
|
|
|
24
24
|
*format_exceptions
|
|
25
25
|
]
|
|
26
26
|
end
|
|
27
|
+
alias_method :report, :generate_report
|
|
28
|
+
|
|
29
|
+
# Returns data formatted for the banner UI
|
|
30
|
+
def banner_data
|
|
31
|
+
{
|
|
32
|
+
exceptions: recent_exceptions.map { |exception| format_exception_for_banner(exception) }
|
|
33
|
+
}
|
|
34
|
+
end
|
|
27
35
|
|
|
28
36
|
private
|
|
29
37
|
|
|
@@ -53,29 +61,100 @@ module Newshound
|
|
|
53
61
|
end
|
|
54
62
|
|
|
55
63
|
def format_exception_text(exception, number)
|
|
64
|
+
details = parse_exception_details(exception)
|
|
65
|
+
|
|
56
66
|
<<~TEXT
|
|
57
|
-
*#{number}. #{exception
|
|
67
|
+
*#{number}. #{exception_title(exception)}*
|
|
58
68
|
• *Time:* #{exception.created_at.strftime('%I:%M %p')}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
#{format_message(exception)}
|
|
69
|
+
#{format_controller(details)}
|
|
70
|
+
#{format_message(exception, details)}
|
|
62
71
|
TEXT
|
|
63
72
|
end
|
|
64
73
|
|
|
65
|
-
def
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
def exception_title(exception)
|
|
75
|
+
if exception.respond_to?(:title) && exception.title.present?
|
|
76
|
+
exception.title
|
|
77
|
+
elsif exception.respond_to?(:exception_class) && exception.exception_class.present?
|
|
78
|
+
exception.exception_class
|
|
79
|
+
else
|
|
80
|
+
"Unknown Exception"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def parse_exception_details(exception)
|
|
85
|
+
return {} unless exception.respond_to?(:body) && exception.body.present?
|
|
86
|
+
|
|
87
|
+
JSON.parse(exception.body)
|
|
88
|
+
rescue JSON::ParserError
|
|
89
|
+
{}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def format_controller(details)
|
|
93
|
+
return String.new unless details["controller_name"] && details["action_name"]
|
|
94
|
+
|
|
95
|
+
"• *Controller:* #{details['controller_name']}##{details['action_name']}\n"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def format_message(exception, details = nil)
|
|
99
|
+
details ||= parse_exception_details(exception)
|
|
100
|
+
|
|
101
|
+
# Try to get message from different sources
|
|
102
|
+
message = if details["message"].present?
|
|
103
|
+
details["message"]
|
|
104
|
+
elsif exception.respond_to?(:message) && exception.message.present?
|
|
105
|
+
exception.message
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
return String.new unless message.present?
|
|
109
|
+
|
|
110
|
+
message = message.to_s.truncate(100)
|
|
69
111
|
"• *Message:* `#{message}`"
|
|
70
112
|
end
|
|
71
113
|
|
|
72
114
|
def exception_count(exception)
|
|
73
115
|
return 0 unless exception_source
|
|
74
116
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
117
|
+
# Use title for exception-track, exception_class for other systems
|
|
118
|
+
if exception.respond_to?(:title) && exception.title.present?
|
|
119
|
+
exception_source
|
|
120
|
+
.where(title: exception.title)
|
|
121
|
+
.where("created_at >= ?", time_range.ago)
|
|
122
|
+
.count
|
|
123
|
+
elsif exception.respond_to?(:exception_class)
|
|
124
|
+
exception_source
|
|
125
|
+
.where(exception_class: exception.exception_class)
|
|
126
|
+
.where("created_at >= ?", time_range.ago)
|
|
127
|
+
.count
|
|
128
|
+
else
|
|
129
|
+
0
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def format_exception_for_banner(exception)
|
|
134
|
+
details = parse_exception_details(exception)
|
|
135
|
+
|
|
136
|
+
# Extract message
|
|
137
|
+
message = if details["message"].present?
|
|
138
|
+
details["message"].to_s
|
|
139
|
+
elsif exception.respond_to?(:message) && exception.message.present?
|
|
140
|
+
exception.message.to_s
|
|
141
|
+
else
|
|
142
|
+
String.new
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Extract location
|
|
146
|
+
location = if details["controller_name"] && details["action_name"]
|
|
147
|
+
"#{details['controller_name']}##{details['action_name']}"
|
|
148
|
+
else
|
|
149
|
+
String.new
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
{
|
|
153
|
+
title: exception_title(exception),
|
|
154
|
+
message: message.truncate(100),
|
|
155
|
+
location: location,
|
|
156
|
+
time: exception.created_at.strftime('%I:%M %p')
|
|
157
|
+
}
|
|
79
158
|
end
|
|
80
159
|
|
|
81
160
|
def no_exceptions_block
|
|
@@ -48,7 +48,7 @@ module Newshound
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def response_body(response)
|
|
51
|
-
body =
|
|
51
|
+
body = String.new
|
|
52
52
|
response.each { |part| body << part }
|
|
53
53
|
body
|
|
54
54
|
end
|
|
@@ -57,8 +57,8 @@ module Newshound
|
|
|
57
57
|
exception_reporter = Newshound::ExceptionReporter.new
|
|
58
58
|
que_reporter = Newshound::QueReporter.new
|
|
59
59
|
|
|
60
|
-
exception_data = exception_reporter.
|
|
61
|
-
job_data = que_reporter.
|
|
60
|
+
exception_data = exception_reporter.banner_data
|
|
61
|
+
job_data = que_reporter.banner_data
|
|
62
62
|
|
|
63
63
|
# Generate HTML from template
|
|
64
64
|
render_banner(exception_data, job_data)
|
|
@@ -275,7 +275,7 @@ module Newshound
|
|
|
275
275
|
end
|
|
276
276
|
|
|
277
277
|
def escape_html(text)
|
|
278
|
-
return
|
|
278
|
+
return String.new unless text.present?
|
|
279
279
|
text.to_s
|
|
280
280
|
.gsub('&', '&')
|
|
281
281
|
.gsub('<', '<')
|
|
@@ -22,6 +22,21 @@ module Newshound
|
|
|
22
22
|
queue_health_section
|
|
23
23
|
].compact
|
|
24
24
|
end
|
|
25
|
+
alias_method :report, :generate_report
|
|
26
|
+
|
|
27
|
+
# Returns data formatted for the banner UI
|
|
28
|
+
def banner_data
|
|
29
|
+
stats = queue_statistics
|
|
30
|
+
|
|
31
|
+
{
|
|
32
|
+
queue_stats: {
|
|
33
|
+
ready_to_run: stats[:ready],
|
|
34
|
+
scheduled: stats[:scheduled],
|
|
35
|
+
failed: stats[:failed],
|
|
36
|
+
completed_today: stats[:finished_today]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
end
|
|
25
40
|
|
|
26
41
|
private
|
|
27
42
|
|
|
@@ -42,21 +57,33 @@ module Newshound
|
|
|
42
57
|
def job_counts_by_type
|
|
43
58
|
return {} unless job_source
|
|
44
59
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
# Use raw SQL since Que::Job may not support ActiveRecord's .group method
|
|
61
|
+
results = ActiveRecord::Base.connection.execute(<<~SQL)
|
|
62
|
+
SELECT job_class, error_count, COUNT(*) as count
|
|
63
|
+
FROM que_jobs
|
|
64
|
+
WHERE finished_at IS NULL
|
|
65
|
+
GROUP BY job_class, error_count
|
|
66
|
+
ORDER BY job_class
|
|
67
|
+
SQL
|
|
68
|
+
|
|
69
|
+
results.each_with_object({}) do |row, hash|
|
|
70
|
+
job_class = row["job_class"]
|
|
71
|
+
error_count = row["error_count"].to_i
|
|
72
|
+
count = row["count"].to_i
|
|
73
|
+
|
|
74
|
+
hash[job_class] ||= {success: 0, failed: 0, total: 0}
|
|
75
|
+
|
|
76
|
+
if error_count.zero?
|
|
77
|
+
hash[job_class][:success] += count
|
|
78
|
+
else
|
|
79
|
+
hash[job_class][:failed] += count
|
|
59
80
|
end
|
|
81
|
+
|
|
82
|
+
hash[job_class][:total] += count
|
|
83
|
+
end
|
|
84
|
+
rescue StandardError => e
|
|
85
|
+
logger.error "Failed to fetch job counts: #{e.message}"
|
|
86
|
+
{}
|
|
60
87
|
end
|
|
61
88
|
|
|
62
89
|
def format_job_counts(counts)
|
|
@@ -85,20 +112,27 @@ module Newshound
|
|
|
85
112
|
def queue_statistics
|
|
86
113
|
return default_stats unless job_source
|
|
87
114
|
|
|
88
|
-
|
|
89
|
-
|
|
115
|
+
conn = ActiveRecord::Base.connection
|
|
116
|
+
current_time = conn.quote(Time.now)
|
|
117
|
+
beginning_of_day = conn.quote(Date.today.to_time)
|
|
90
118
|
|
|
91
119
|
{
|
|
92
|
-
ready:
|
|
93
|
-
scheduled:
|
|
94
|
-
failed:
|
|
95
|
-
finished_today:
|
|
120
|
+
ready: count_jobs("finished_at IS NULL AND expired_at IS NULL AND run_at <= #{current_time}"),
|
|
121
|
+
scheduled: count_jobs("finished_at IS NULL AND expired_at IS NULL AND run_at > #{current_time}"),
|
|
122
|
+
failed: count_jobs("error_count > 0 AND finished_at IS NULL"),
|
|
123
|
+
finished_today: count_jobs("finished_at >= #{beginning_of_day}")
|
|
96
124
|
}
|
|
97
125
|
rescue StandardError => e
|
|
98
126
|
logger.error "Failed to fetch Que statistics: #{e.message}"
|
|
99
127
|
default_stats
|
|
100
128
|
end
|
|
101
129
|
|
|
130
|
+
def count_jobs(where_clause)
|
|
131
|
+
ActiveRecord::Base.connection.select_value(
|
|
132
|
+
"SELECT COUNT(*) FROM que_jobs WHERE #{where_clause}"
|
|
133
|
+
).to_i
|
|
134
|
+
end
|
|
135
|
+
|
|
102
136
|
def default_stats
|
|
103
137
|
{ ready: 0, scheduled: 0, failed: 0, finished_today: 0 }
|
|
104
138
|
end
|
data/lib/newshound/version.rb
CHANGED