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.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.GZIPOutputStream;
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
@@ -164,16 +165,19 @@ public class RestApiServlet extends HttpServlet {
|
|||||||
final DynamicItem<WebSession> webSession;
|
final DynamicItem<WebSession> webSession;
|
||||||
final Provider<ParameterParser> paramParser;
|
final Provider<ParameterParser> paramParser;
|
||||||
final AuditService auditService;
|
final AuditService auditService;
|
||||||
|
final RestApiMetrics metrics;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Globals(Provider<CurrentUser> currentUser,
|
Globals(Provider<CurrentUser> currentUser,
|
||||||
DynamicItem<WebSession> webSession,
|
DynamicItem<WebSession> webSession,
|
||||||
Provider<ParameterParser> paramParser,
|
Provider<ParameterParser> paramParser,
|
||||||
AuditService auditService) {
|
AuditService auditService,
|
||||||
|
RestApiMetrics metrics) {
|
||||||
this.currentUser = currentUser;
|
this.currentUser = currentUser;
|
||||||
this.webSession = webSession;
|
this.webSession = webSession;
|
||||||
this.paramParser = paramParser;
|
this.paramParser = paramParser;
|
||||||
this.auditService = auditService;
|
this.auditService = auditService;
|
||||||
|
this.metrics = metrics;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +201,7 @@ public class RestApiServlet extends HttpServlet {
|
|||||||
@Override
|
@Override
|
||||||
protected final void service(HttpServletRequest req, HttpServletResponse res)
|
protected final void service(HttpServletRequest req, HttpServletResponse res)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
final long startNanos = System.nanoTime();
|
||||||
long auditStartTs = TimeUtil.nowMs();
|
long auditStartTs = TimeUtil.nowMs();
|
||||||
res.setHeader("Content-Disposition", "attachment");
|
res.setHeader("Content-Disposition", "attachment");
|
||||||
res.setHeader("X-Content-Type-Options", "nosniff");
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
||||||
@@ -389,6 +394,17 @@ public class RestApiServlet extends HttpServlet {
|
|||||||
status = SC_INTERNAL_SERVER_ERROR;
|
status = SC_INTERNAL_SERVER_ERROR;
|
||||||
handleException(e, req, res);
|
handleException(e, req, res);
|
||||||
} finally {
|
} 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()
|
globals.auditService.dispatch(new ExtendedHttpAuditEvent(globals.webSession.get()
|
||||||
.getSessionId(), globals.currentUser.get(), req,
|
.getSessionId(), globals.currentUser.get(), req,
|
||||||
auditStartTs, params, inputRequestBody, status,
|
auditStartTs, params, inputRequestBody, status,
|
||||||
@@ -1099,7 +1115,7 @@ public class RestApiServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ViewData {
|
static class ViewData {
|
||||||
String pluginName;
|
String pluginName;
|
||||||
RestView<RestResource> view;
|
RestView<RestResource> view;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user