Add metrics for http/server/rest_api calls
Break these down by the view implementation class showing what part of Gerrit handled the request. Include latency information, aggregate counts, and errors when HTTP status code is >= 400. Change-Id: Ifc806e4dbf157b4eea368e9a223d8ad1e551b8e7
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
// Copyright (C) 2015 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.httpd.restapi;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.httpd.restapi.RestApiServlet.ViewData;
|
||||
import com.google.gerrit.metrics.Counter1;
|
||||
import com.google.gerrit.metrics.Counter2;
|
||||
import com.google.gerrit.metrics.Description;
|
||||
import com.google.gerrit.metrics.Field;
|
||||
import com.google.gerrit.metrics.MetricMaker;
|
||||
import com.google.gerrit.metrics.Timer1;
|
||||
import com.google.gerrit.metrics.Description.Units;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class RestApiMetrics {
|
||||
private static final String[] PKGS = {
|
||||
"com.google.gerrit.server.",
|
||||
"com.google.gerrit.",
|
||||
};
|
||||
|
||||
final Counter1<String> count;
|
||||
final Counter2<String, Integer> errorCount;
|
||||
final Timer1<String> serverLatency;
|
||||
|
||||
@Inject
|
||||
RestApiMetrics(MetricMaker metrics) {
|
||||
Field<String> view = Field.ofString("view", "view implementation class");
|
||||
count = metrics.newCounter(
|
||||
"http/server/rest_api/count",
|
||||
new Description("REST API calls by view")
|
||||
.setRate(),
|
||||
view);
|
||||
|
||||
errorCount = metrics.newCounter(
|
||||
"http/server/rest_api/error_count",
|
||||
new Description("REST API calls by view")
|
||||
.setRate(),
|
||||
view,
|
||||
Field.ofInteger("error_code", "HTTP status code"));
|
||||
|
||||
serverLatency = metrics.newTimer(
|
||||
"http/server/rest_api/server_latency",
|
||||
new Description("REST API call latency by view")
|
||||
.setCumulative()
|
||||
.setUnit(Units.MILLISECONDS),
|
||||
view);
|
||||
}
|
||||
|
||||
String view(ViewData viewData) {
|
||||
String impl = viewData.view.getClass().getName().replace('$', '.');
|
||||
for (String p : PKGS) {
|
||||
if (impl.startsWith(p)) {
|
||||
impl = impl.substring(p.length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(viewData.pluginName)
|
||||
&& !"gerrit".equals(viewData.pluginName)) {
|
||||
impl = viewData.pluginName + '-' + impl;
|
||||
}
|
||||
return impl;
|
||||
}
|
||||
}
|
||||
@@ -125,6 +125,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
@@ -164,16 +165,19 @@ public class RestApiServlet extends HttpServlet {
|
||||
final DynamicItem<WebSession> webSession;
|
||||
final Provider<ParameterParser> paramParser;
|
||||
final AuditService auditService;
|
||||
final RestApiMetrics metrics;
|
||||
|
||||
@Inject
|
||||
Globals(Provider<CurrentUser> currentUser,
|
||||
DynamicItem<WebSession> webSession,
|
||||
Provider<ParameterParser> paramParser,
|
||||
AuditService auditService) {
|
||||
AuditService auditService,
|
||||
RestApiMetrics metrics) {
|
||||
this.currentUser = currentUser;
|
||||
this.webSession = webSession;
|
||||
this.paramParser = paramParser;
|
||||
this.auditService = auditService;
|
||||
this.metrics = metrics;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +201,7 @@ public class RestApiServlet extends HttpServlet {
|
||||
@Override
|
||||
protected final void service(HttpServletRequest req, HttpServletResponse res)
|
||||
throws ServletException, IOException {
|
||||
final long startNanos = System.nanoTime();
|
||||
long auditStartTs = TimeUtil.nowMs();
|
||||
res.setHeader("Content-Disposition", "attachment");
|
||||
res.setHeader("X-Content-Type-Options", "nosniff");
|
||||
@@ -389,6 +394,17 @@ public class RestApiServlet extends HttpServlet {
|
||||
status = SC_INTERNAL_SERVER_ERROR;
|
||||
handleException(e, req, res);
|
||||
} finally {
|
||||
String metric = viewData != null && viewData.view != null
|
||||
? globals.metrics.view(viewData)
|
||||
: "_unknown";
|
||||
globals.metrics.count.increment(metric);
|
||||
if (status >= SC_BAD_REQUEST) {
|
||||
globals.metrics.errorCount.increment(metric, status);
|
||||
}
|
||||
globals.metrics.serverLatency.record(
|
||||
metric,
|
||||
System.nanoTime() - startNanos,
|
||||
TimeUnit.NANOSECONDS);
|
||||
globals.auditService.dispatch(new ExtendedHttpAuditEvent(globals.webSession.get()
|
||||
.getSessionId(), globals.currentUser.get(), req,
|
||||
auditStartTs, params, inputRequestBody, status,
|
||||
@@ -1099,7 +1115,7 @@ public class RestApiServlet extends HttpServlet {
|
||||
}
|
||||
}
|
||||
|
||||
private static class ViewData {
|
||||
static class ViewData {
|
||||
String pluginName;
|
||||
RestView<RestResource> view;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user