QueriesStatusHealthCheck.java
9.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
* Decompiled with CFR 0_118.
*
* Could not load the following classes:
* org.apache.felix.scr.annotations.Activate
* org.apache.felix.scr.annotations.Component
* org.apache.felix.scr.annotations.Modified
* org.apache.felix.scr.annotations.Properties
* org.apache.felix.scr.annotations.Property
* org.apache.felix.scr.annotations.PropertyUnbounded
* org.apache.felix.scr.annotations.Reference
* org.apache.felix.scr.annotations.ReferencePolicy
* org.apache.felix.scr.annotations.Service
* org.apache.sling.commons.osgi.PropertiesUtil
* org.apache.sling.hc.api.HealthCheck
* org.apache.sling.hc.api.Result
* org.apache.sling.hc.api.ResultLog
* org.apache.sling.hc.util.FormattingResultLog
*/
package com.adobe.granite.queries.impl.hc;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.QueryExp;
import javax.management.openmbean.CompositeDataSupport;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.PropertyUnbounded;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.hc.api.HealthCheck;
import org.apache.sling.hc.api.Result;
import org.apache.sling.hc.api.ResultLog;
import org.apache.sling.hc.util.FormattingResultLog;
@Component(metatype=1, label="Adobe Granite Queries Status Health Check", description="This heath check monitors the queries' response time.")
@Properties(value={@Property(name="hc.name", value={"Query Performance"}, propertyPrivate=1), @Property(name="hc.tags", unbounded=PropertyUnbounded.ARRAY, label="Tags", description="Tags for this health check to be used by composite health checks."), @Property(name="hc.mbean.name", value={"queriesStatus"}, propertyPrivate=1)})
@Service(value={HealthCheck.class})
public class QueriesStatusHealthCheck
implements HealthCheck {
@Property(label="Query Average Time Warning Threshold (millis)", description="The query time limit for a warning status", intValue={10})
public static final String WARN_THRESHHOLD = "queries.status.warn_threshold";
@Property(label="Query Critical Time Warning Threshold (millis)", description="The query time limit for an error status", intValue={15})
public static final String ERROR_THRESHHOLD = "queries.status.error_threshold";
private static long queryCriticalAverage;
private static long queryWarningThreshold;
private boolean oakInstance = false;
@Reference(policy=ReferencePolicy.STATIC)
MBeanServer server;
@Activate
protected void activate(Map<String, Object> properties) {
this.setProperties(properties);
}
@Modified
protected void modified(Map<String, Object> properties) {
this.setProperties(properties);
}
private void setProperties(Map<String, Object> properties) {
queryWarningThreshold = PropertiesUtil.toLong((Object)properties.get("queries.status.warn_threshold"), (long)10);
queryCriticalAverage = PropertiesUtil.toLong((Object)properties.get("queries.status.error_threshold"), (long)15);
}
public Result execute() {
FormattingResultLog resultLog = new FormattingResultLog();
if (this.server == null) {
resultLog.warn("Supporting components for queries health check not found.", new Object[0]);
return new Result((ResultLog)resultLog);
}
try {
ObjectName timeSeriesMbean = this.getQueryStatMBean(this.server);
if (timeSeriesMbean != null) {
long[] averages = this.getAverageQueryTimes(timeSeriesMbean);
if (averages == null || averages.length == 0) {
resultLog.warn("No query statistics found.", new Object[0]);
return new Result((ResultLog)resultLog);
}
boolean critical = false;
boolean warn = false;
long maxAvg = 0;
for (long minuteAvg : averages) {
if (minuteAvg > queryCriticalAverage) {
critical = true;
} else if (minuteAvg > queryWarningThreshold) {
warn = true;
}
if (minuteAvg <= maxAvg) continue;
maxAvg = minuteAvg;
}
resultLog.debug("[The query duration thresholds (warning, critical) can be modified via the health check configuration.]( )", new Object[0]);
resultLog.debug("[The statistics presented here are collected over the last 60 minutes, with a 1 minute sampling interval.]( )", new Object[0]);
long maxCount = this.getMaximumValue(timeSeriesMbean, "QueryCount");
resultLog.info("Maximum number of queries per minute: {}.", new Object[]{maxCount});
long maxDuration = this.getMaximumValue(timeSeriesMbean, "QueryDuration");
resultLog.info("Maximum total query duration: {} ms.", new Object[]{maxDuration});
resultLog.info("Maximum average query duration: {} ms.", new Object[]{maxAvg});
if (critical) {
resultLog.critical("Average query duration exceeded the {} ms critical threshold.", new Object[]{queryCriticalAverage});
resultLog.debug("[Check the Query Performance tool in the Diagnosis page for more details about the longest running queries.](/libs/granite/operations/content/diagnosis/tool.html/granite:queryperformance)", new Object[0]);
} else if (warn) {
resultLog.warn("Average query duration exceeded the {} ms warning threshold.", new Object[]{queryWarningThreshold});
resultLog.debug("[Check the Query Performance tool in the Diagnosis page for more details about the longest running queries](/libs/granite/operations/content/diagnosis/tool.html/granite:queryperformance)", new Object[0]);
} else {
resultLog.info("Average query duration is below the {} ms warning threshold.", new Object[]{queryWarningThreshold});
}
} else {
resultLog.warn("No query statistics found.", new Object[0]);
}
}
catch (Exception e) {
resultLog.warn(e.getLocalizedMessage(), new Object[0]);
}
return new Result((ResultLog)resultLog);
}
private ObjectName getQueryStatMBean(MBeanServer server) throws OperationsException, IOException {
Set<ObjectName> names = server.queryNames(new ObjectName("com.adobe.granite:type=TimeSeries,name=QUERY_AVERAGE,*"), null);
if (names.isEmpty()) {
names = this.queryNames(server, "RepositoryStats");
if (names.isEmpty()) {
names = this.queryNames(server, "\"RepositoryStats\"");
if (names.isEmpty()) {
return null;
}
this.oakInstance = true;
} else {
this.oakInstance = true;
}
}
return names.iterator().next();
}
private Set<ObjectName> queryNames(MBeanServer server, String name) throws OperationsException, IOException {
return server.queryNames(new ObjectName("org.apache.jackrabbit.oak:type=" + name + ",*"), null);
}
private long[] getAverageQueryTimes(ObjectName mbean) throws Exception {
if (this.oakInstance) {
Object queryAvgObj = this.server.getAttribute(mbean, "QueryAverage");
Object avgMinObj = ((CompositeDataSupport)queryAvgObj).get("per minute");
if (!(avgMinObj instanceof long[])) {
return null;
}
return (long[])avgMinObj;
}
Object avgObj = this.server.getAttribute(mbean, "ValuePerMinute");
if (!(avgObj instanceof long[])) {
return null;
}
return (long[])this.server.getAttribute(mbean, "ValuePerMinute");
}
private long getMaximumValue(ObjectName mbean, String propertyName) throws Exception {
Object queryStatsPerMinute;
long max = 0;
Object queryObj = this.server.getAttribute(mbean, propertyName);
if (queryObj != null && (queryStatsPerMinute = ((CompositeDataSupport)queryObj).get("per minute")) != null && queryStatsPerMinute instanceof long[]) {
long[] counts = (long[])queryStatsPerMinute;
for (int i = 0; i < counts.length; ++i) {
if (max >= counts[i]) continue;
max = counts[i];
}
}
return max;
}
protected void bindServer(MBeanServer mBeanServer) {
this.server = mBeanServer;
}
protected void unbindServer(MBeanServer mBeanServer) {
if (this.server == mBeanServer) {
this.server = null;
}
}
}