Skip to content

Commit d246bc3

Browse files
authored
New ClientErrors monitor + cleanups to built-in monitors (#416)
1 parent 5db9760 commit d246bc3

File tree

3 files changed

+51
-47
lines changed

3 files changed

+51
-47
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* Improved the handling of track log timestamps - these can now be supplied by the client and are no
1515
longer bound to insert time of DB record. Latest Hoist React uses *start* of the tracked activity.
1616
* Support for persisting of memory monitoring results
17+
* New built-in monitor `xhClientErrorsMonitor`
18+
* New methods `MonitorResult.getParam` and `MonitorResult.getRequiredParam`
1719

1820
### ⚙️ Technical
1921

src/main/groovy/io/xh/hoist/monitor/MonitorResult.groovy

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ class MonitorResult implements JSONFormat {
3939
monitor.params ? JSONParser.parseObject(monitor.params) : [:]
4040
}
4141

42+
<T> T getParam(String name, T defaultVal = null) {
43+
params.containsKey(name) ? params[name] : defaultVal
44+
}
45+
46+
<T> T getRequiredParam(String name) {
47+
if (!params.containsKey(name)) {
48+
throw new RuntimeException("Missing required parameter ${name}")
49+
}
50+
params[name]
51+
}
52+
4253
/** Combines the given string with 'message', separated by formatting */
4354
void prependMessage(String prependStr) {
4455
// Space character before the newlines is for fallback formatting in `hoist-react <= v51.0.0`

src/main/groovy/io/xh/hoist/monitor/provided/DefaultMonitorDefinitionService.groovy

Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77

88
package io.xh.hoist.monitor.provided
99

10+
import grails.gorm.transactions.ReadOnly
1011
import grails.gorm.transactions.Transactional
1112
import groovy.sql.Sql
1213
import io.xh.hoist.BaseService
13-
import io.xh.hoist.data.filter.Filter
1414
import io.xh.hoist.monitor.Monitor
1515
import io.xh.hoist.monitor.MonitorResult
1616
import io.xh.hoist.util.Utils
17+
import io.xh.hoist.clienterror.ClientError
18+
import io.xh.hoist.track.TrackLog
1719

1820
import static io.xh.hoist.monitor.MonitorStatus.FAIL
1921
import static io.xh.hoist.monitor.MonitorStatus.INACTIVE
@@ -51,18 +53,14 @@ class DefaultMonitorDefinitionService extends BaseService {
5153
return
5254
}
5355

54-
def aggregate = result.params.aggregate ?: 'avg'
55-
if (!['avg', 'max'].contains(aggregate)) {
56-
throw new RuntimeException("Invalid aggregate parameter: ${result.params.aggregate}")
56+
def aggregate = result.getParam('aggregate', 'avg')
57+
if (!(aggregate in ['avg', 'max'])) {
58+
throw new RuntimeException("Invalid aggregate parameter: $aggregate")
5759
}
5860

59-
def lookbackMinutes = result.params.lookbackMinutes
60-
if (!lookbackMinutes) {
61-
throw new RuntimeException('No \"lookbackMinutes\" parameter provided')
62-
}
63-
64-
def cutOffTime = currentTimeMillis() - lookbackMinutes * MINUTES
65-
def snapshots = memoryMonitoringService.snapshots.findAll {it.key > cutOffTime}.values()
61+
def lookback = result.getRequiredParam('lookbackMinutes') * MINUTES,
62+
cutoffTime = currentTimeMillis() - lookback,
63+
snapshots = memoryMonitoringService.snapshots.findAll {it.key > cutoffTime}.values()
6664

6765
if (!snapshots) {
6866
result.metric = 0
@@ -74,50 +72,34 @@ class DefaultMonitorDefinitionService extends BaseService {
7472
: snapshots.max{it.usedPctMax}.usedPctMax
7573
}
7674

75+
@ReadOnly
76+
def xhClientErrorsMonitor(MonitorResult result) {
77+
def lookback = result.getRequiredParam('lookbackMinutes') * MINUTES,
78+
cutoffDate = new Date(currentTimeMillis() - lookback)
79+
80+
result.metric = ClientError.countByDateCreatedGreaterThan(cutoffDate)
81+
}
82+
83+
@ReadOnly
7784
def xhLoadTimeMonitor(MonitorResult result) {
7885
if (!trackLogAdminService.enabled) {
7986
result.status = INACTIVE
8087
return
8188
}
8289

83-
def lookbackMinutes = result.params.lookbackMinutes
84-
if (!lookbackMinutes) {
85-
throw new RuntimeException('No \"lookbackMinutes\" parameter provided.')
86-
}
87-
88-
def cutOffTime = currentTimeMillis() - lookbackMinutes * MINUTES
89-
def logs = trackLogAdminService.queryTrackLog(
90-
Filter.parse([
91-
filters: [
92-
[
93-
field: 'dateCreated',
94-
op: '>',
95-
value: new Date(cutOffTime)
96-
],
97-
[
98-
field: 'elapsed',
99-
op: '!=',
100-
value: null
101-
]
102-
],
103-
op: "AND"
104-
])
105-
)
106-
107-
if (!logs) {
108-
result.metric = 0
109-
return
110-
}
90+
def lookback = result.getRequiredParam('lookbackMinutes') * MINUTES,
91+
cutoffDate = new Date(currentTimeMillis() - lookback),
92+
logs = TrackLog.findAllByDateCreatedGreaterThanAndElapsedIsNotNull(cutoffDate)
11193

112-
result.metric = logs.max{it.elapsed}.elapsed / SECONDS
94+
result.metric = logs ? logs.max{it.elapsed}.elapsed / SECONDS : 0
11395
}
11496

11597
def xhDbConnectionMonitor(MonitorResult result) {
11698
def startTime = currentTimeMillis()
11799
Sql sql = new Sql(dataSource)
118100
try {
119101
// Support configurable table name for edge case where XH tables are in a custom schema.
120-
def tableName = result.params.tableName ?: 'xh_monitor'
102+
def tableName = result.getParam('tableName', 'xh_monitor')
121103
sql.rows("SELECT * FROM ${Sql.expand(tableName)} WHERE code = 'xhDbConnectionMonitor'")
122104
} finally {
123105
sql.close()
@@ -132,12 +114,9 @@ class DefaultMonitorDefinitionService extends BaseService {
132114
return
133115
}
134116

135-
if (!result.params.queryUser) {
136-
throw new RuntimeException("No \"queryUser\" parameter provided.")
137-
}
138-
139-
def startTime = currentTimeMillis()
140-
def user = ldapService.lookupUser(result.params.queryUser)
117+
def queryUser = result.getRequiredParam('queryUser'),
118+
user = ldapService.lookupUser(queryUser),
119+
startTime = currentTimeMillis()
141120

142121
if (!user) {
143122
result.message = "Failed to find expected user: ${result.params.queryUser}"
@@ -177,6 +156,18 @@ class DefaultMonitorDefinitionService extends BaseService {
177156
+ 'Set "aggregate" to "avg" to report average heap usage (default).\n'
178157
+ 'Set "aggregate" to "max" to report the largest heap usage.'
179158
],
159+
[
160+
code: 'xhClientErrorsMonitor',
161+
name: 'Client Errors (Last 30m)',
162+
metricType: 'Ceil',
163+
metricUnit: 's',
164+
warnThreshold: 1,
165+
failThreshold: 10,
166+
active: true,
167+
primaryOnly: true,
168+
params: '{\n\t"lookbackMinutes": 30\n}',
169+
notes: 'Reports the longest tracked event in the last {lookbackMinutes} minutes.'
170+
],
180171
[
181172
code: 'xhLoadTimeMonitor',
182173
name: 'Max Load Time (Last 30m)',

0 commit comments

Comments
 (0)