Add Histogram0..3 to track bytes sent
The pack_bytes metric reports on the size of data sent to clients. Using a histogram allows the server to track the distribution of these files over time, as well as total amount sent. The response_bytes metric reports on size of REST API replies, broken down by view. Change-Id: I7c06cb9afb2f4a8da1ef1eec85b5831f75b88116
This commit is contained in:
		@@ -20,6 +20,7 @@ import com.google.gerrit.metrics.Counter1;
 | 
				
			|||||||
import com.google.gerrit.metrics.Counter2;
 | 
					import com.google.gerrit.metrics.Counter2;
 | 
				
			||||||
import com.google.gerrit.metrics.Description;
 | 
					import com.google.gerrit.metrics.Description;
 | 
				
			||||||
import com.google.gerrit.metrics.Field;
 | 
					import com.google.gerrit.metrics.Field;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram1;
 | 
				
			||||||
import com.google.gerrit.metrics.MetricMaker;
 | 
					import com.google.gerrit.metrics.MetricMaker;
 | 
				
			||||||
import com.google.gerrit.metrics.Timer1;
 | 
					import com.google.gerrit.metrics.Timer1;
 | 
				
			||||||
import com.google.gerrit.metrics.Description.Units;
 | 
					import com.google.gerrit.metrics.Description.Units;
 | 
				
			||||||
@@ -36,6 +37,7 @@ public class RestApiMetrics {
 | 
				
			|||||||
  final Counter1<String> count;
 | 
					  final Counter1<String> count;
 | 
				
			||||||
  final Counter2<String, Integer> errorCount;
 | 
					  final Counter2<String, Integer> errorCount;
 | 
				
			||||||
  final Timer1<String> serverLatency;
 | 
					  final Timer1<String> serverLatency;
 | 
				
			||||||
 | 
					  final Histogram1<String> responseBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  RestApiMetrics(MetricMaker metrics) {
 | 
					  RestApiMetrics(MetricMaker metrics) {
 | 
				
			||||||
@@ -59,6 +61,13 @@ public class RestApiMetrics {
 | 
				
			|||||||
          .setCumulative()
 | 
					          .setCumulative()
 | 
				
			||||||
          .setUnit(Units.MILLISECONDS),
 | 
					          .setUnit(Units.MILLISECONDS),
 | 
				
			||||||
        view);
 | 
					        view);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    responseBytes = metrics.newHistogram(
 | 
				
			||||||
 | 
					        "http/server/rest_api/response_bytes",
 | 
				
			||||||
 | 
					        new Description("Size of response on network (may be gzip compressed)")
 | 
				
			||||||
 | 
					          .setCumulative()
 | 
				
			||||||
 | 
					          .setUnit(Units.BYTES),
 | 
				
			||||||
 | 
					        view);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  String view(ViewData viewData) {
 | 
					  String view(ViewData viewData) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,7 @@ import com.google.common.collect.Maps;
 | 
				
			|||||||
import com.google.common.collect.Multimap;
 | 
					import com.google.common.collect.Multimap;
 | 
				
			||||||
import com.google.common.collect.Sets;
 | 
					import com.google.common.collect.Sets;
 | 
				
			||||||
import com.google.common.io.BaseEncoding;
 | 
					import com.google.common.io.BaseEncoding;
 | 
				
			||||||
 | 
					import com.google.common.io.CountingOutputStream;
 | 
				
			||||||
import com.google.common.math.IntMath;
 | 
					import com.google.common.math.IntMath;
 | 
				
			||||||
import com.google.common.net.HttpHeaders;
 | 
					import com.google.common.net.HttpHeaders;
 | 
				
			||||||
import com.google.gerrit.audit.AuditService;
 | 
					import com.google.gerrit.audit.AuditService;
 | 
				
			||||||
@@ -206,6 +207,7 @@ public class RestApiServlet extends HttpServlet {
 | 
				
			|||||||
    res.setHeader("Content-Disposition", "attachment");
 | 
					    res.setHeader("Content-Disposition", "attachment");
 | 
				
			||||||
    res.setHeader("X-Content-Type-Options", "nosniff");
 | 
					    res.setHeader("X-Content-Type-Options", "nosniff");
 | 
				
			||||||
    int status = SC_OK;
 | 
					    int status = SC_OK;
 | 
				
			||||||
 | 
					    long responseBytes = -1;
 | 
				
			||||||
    Object result = null;
 | 
					    Object result = null;
 | 
				
			||||||
    Multimap<String, String> params = LinkedHashMultimap.create();
 | 
					    Multimap<String, String> params = LinkedHashMultimap.create();
 | 
				
			||||||
    Object inputRequestBody = null;
 | 
					    Object inputRequestBody = null;
 | 
				
			||||||
@@ -353,46 +355,47 @@ public class RestApiServlet extends HttpServlet {
 | 
				
			|||||||
      if (result != Response.none()) {
 | 
					      if (result != Response.none()) {
 | 
				
			||||||
        result = Response.unwrap(result);
 | 
					        result = Response.unwrap(result);
 | 
				
			||||||
        if (result instanceof BinaryResult) {
 | 
					        if (result instanceof BinaryResult) {
 | 
				
			||||||
          replyBinaryResult(req, res, (BinaryResult) result);
 | 
					          responseBytes = replyBinaryResult(req, res, (BinaryResult) result);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          replyJson(req, res, config, result);
 | 
					          responseBytes = replyJson(req, res, config, result);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (MalformedJsonException e) {
 | 
					    } catch (MalformedJsonException e) {
 | 
				
			||||||
      replyError(req, res, status = SC_BAD_REQUEST,
 | 
					      responseBytes = replyError(req, res, status = SC_BAD_REQUEST,
 | 
				
			||||||
          "Invalid " + JSON_TYPE + " in request", e);
 | 
					          "Invalid " + JSON_TYPE + " in request", e);
 | 
				
			||||||
    } catch (JsonParseException e) {
 | 
					    } catch (JsonParseException e) {
 | 
				
			||||||
      replyError(req, res, status = SC_BAD_REQUEST,
 | 
					      responseBytes = replyError(req, res, status = SC_BAD_REQUEST,
 | 
				
			||||||
          "Invalid " + JSON_TYPE + " in request", e);
 | 
					          "Invalid " + JSON_TYPE + " in request", e);
 | 
				
			||||||
    } catch (BadRequestException e) {
 | 
					    } catch (BadRequestException e) {
 | 
				
			||||||
      replyError(req, res, status = SC_BAD_REQUEST, messageOr(e, "Bad Request"),
 | 
					      responseBytes = replyError(req, res, status = SC_BAD_REQUEST,
 | 
				
			||||||
          e.caching(), e);
 | 
					          messageOr(e, "Bad Request"), e.caching(), e);
 | 
				
			||||||
    } catch (AuthException e) {
 | 
					    } catch (AuthException e) {
 | 
				
			||||||
      replyError(req, res, status = SC_FORBIDDEN, messageOr(e, "Forbidden"),
 | 
					      responseBytes = replyError(req, res, status = SC_FORBIDDEN,
 | 
				
			||||||
          e.caching(), e);
 | 
					          messageOr(e, "Forbidden"), e.caching(), e);
 | 
				
			||||||
    } catch (AmbiguousViewException e) {
 | 
					    } catch (AmbiguousViewException e) {
 | 
				
			||||||
      replyError(req, res, status = SC_NOT_FOUND, messageOr(e, "Ambiguous"), e);
 | 
					      responseBytes = replyError(req, res, status = SC_NOT_FOUND,
 | 
				
			||||||
 | 
					          messageOr(e, "Ambiguous"), e);
 | 
				
			||||||
    } catch (ResourceNotFoundException e) {
 | 
					    } catch (ResourceNotFoundException e) {
 | 
				
			||||||
      replyError(req, res, status = SC_NOT_FOUND, messageOr(e, "Not Found"),
 | 
					      responseBytes = replyError(req, res, status = SC_NOT_FOUND,
 | 
				
			||||||
          e.caching(), e);
 | 
					          messageOr(e, "Not Found"), e.caching(), e);
 | 
				
			||||||
    } catch (MethodNotAllowedException e) {
 | 
					    } catch (MethodNotAllowedException e) {
 | 
				
			||||||
      replyError(req, res, status = SC_METHOD_NOT_ALLOWED,
 | 
					      responseBytes = replyError(req, res, status = SC_METHOD_NOT_ALLOWED,
 | 
				
			||||||
          messageOr(e, "Method Not Allowed"), e.caching(), e);
 | 
					          messageOr(e, "Method Not Allowed"), e.caching(), e);
 | 
				
			||||||
    } catch (ResourceConflictException e) {
 | 
					    } catch (ResourceConflictException e) {
 | 
				
			||||||
      replyError(req, res, status = SC_CONFLICT, messageOr(e, "Conflict"),
 | 
					      responseBytes = replyError(req, res, status = SC_CONFLICT,
 | 
				
			||||||
          e.caching(), e);
 | 
					          messageOr(e, "Conflict"), e.caching(), e);
 | 
				
			||||||
    } catch (PreconditionFailedException e) {
 | 
					    } catch (PreconditionFailedException e) {
 | 
				
			||||||
      replyError(req, res, status = SC_PRECONDITION_FAILED,
 | 
					      responseBytes = replyError(req, res, status = SC_PRECONDITION_FAILED,
 | 
				
			||||||
          messageOr(e, "Precondition Failed"), e.caching(), e);
 | 
					          messageOr(e, "Precondition Failed"), e.caching(), e);
 | 
				
			||||||
    } catch (UnprocessableEntityException e) {
 | 
					    } catch (UnprocessableEntityException e) {
 | 
				
			||||||
      replyError(req, res, status = 422, messageOr(e, "Unprocessable Entity"),
 | 
					      responseBytes = replyError(req, res, status = 422,
 | 
				
			||||||
          e.caching(), e);
 | 
					          messageOr(e, "Unprocessable Entity"), e.caching(), e);
 | 
				
			||||||
    } catch (NotImplementedException e) {
 | 
					    } catch (NotImplementedException e) {
 | 
				
			||||||
      replyError(req, res, status = SC_NOT_IMPLEMENTED,
 | 
					      responseBytes = replyError(req, res, status = SC_NOT_IMPLEMENTED,
 | 
				
			||||||
          messageOr(e, "Not Implemented"), e);
 | 
					          messageOr(e, "Not Implemented"), e);
 | 
				
			||||||
    } catch (Exception e) {
 | 
					    } catch (Exception e) {
 | 
				
			||||||
      status = SC_INTERNAL_SERVER_ERROR;
 | 
					      status = SC_INTERNAL_SERVER_ERROR;
 | 
				
			||||||
      handleException(e, req, res);
 | 
					      responseBytes = handleException(e, req, res);
 | 
				
			||||||
    } finally {
 | 
					    } finally {
 | 
				
			||||||
      String metric = viewData != null && viewData.view != null
 | 
					      String metric = viewData != null && viewData.view != null
 | 
				
			||||||
          ? globals.metrics.view(viewData)
 | 
					          ? globals.metrics.view(viewData)
 | 
				
			||||||
@@ -401,6 +404,9 @@ public class RestApiServlet extends HttpServlet {
 | 
				
			|||||||
      if (status >= SC_BAD_REQUEST) {
 | 
					      if (status >= SC_BAD_REQUEST) {
 | 
				
			||||||
        globals.metrics.errorCount.increment(metric, status);
 | 
					        globals.metrics.errorCount.increment(metric, status);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      if (responseBytes != -1) {
 | 
				
			||||||
 | 
					        globals.metrics.responseBytes.record(metric, responseBytes);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      globals.metrics.serverLatency.record(
 | 
					      globals.metrics.serverLatency.record(
 | 
				
			||||||
          metric,
 | 
					          metric,
 | 
				
			||||||
          System.nanoTime() - startNanos,
 | 
					          System.nanoTime() - startNanos,
 | 
				
			||||||
@@ -667,7 +673,7 @@ public class RestApiServlet extends HttpServlet {
 | 
				
			|||||||
    throw new InstantiationException("Cannot make " + type);
 | 
					    throw new InstantiationException("Cannot make " + type);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static void replyJson(@Nullable HttpServletRequest req,
 | 
					  public static long replyJson(@Nullable HttpServletRequest req,
 | 
				
			||||||
      HttpServletResponse res,
 | 
					      HttpServletResponse res,
 | 
				
			||||||
      Multimap<String, String> config,
 | 
					      Multimap<String, String> config,
 | 
				
			||||||
      Object result)
 | 
					      Object result)
 | 
				
			||||||
@@ -683,7 +689,7 @@ public class RestApiServlet extends HttpServlet {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    w.write('\n');
 | 
					    w.write('\n');
 | 
				
			||||||
    w.flush();
 | 
					    w.flush();
 | 
				
			||||||
    replyBinaryResult(req, res, asBinaryResult(buf)
 | 
					    return replyBinaryResult(req, res, asBinaryResult(buf)
 | 
				
			||||||
      .setContentType(JSON_TYPE)
 | 
					      .setContentType(JSON_TYPE)
 | 
				
			||||||
      .setCharacterEncoding(UTF_8));
 | 
					      .setCharacterEncoding(UTF_8));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -752,7 +758,7 @@ public class RestApiServlet extends HttpServlet {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @SuppressWarnings("resource")
 | 
					  @SuppressWarnings("resource")
 | 
				
			||||||
  static void replyBinaryResult(
 | 
					  static long replyBinaryResult(
 | 
				
			||||||
      @Nullable HttpServletRequest req,
 | 
					      @Nullable HttpServletRequest req,
 | 
				
			||||||
      HttpServletResponse res,
 | 
					      HttpServletResponse res,
 | 
				
			||||||
      BinaryResult bin) throws IOException {
 | 
					      BinaryResult bin) throws IOException {
 | 
				
			||||||
@@ -783,10 +789,13 @@ public class RestApiServlet extends HttpServlet {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (req == null || !"HEAD".equals(req.getMethod())) {
 | 
					      if (req == null || !"HEAD".equals(req.getMethod())) {
 | 
				
			||||||
        try (OutputStream dst = res.getOutputStream()) {
 | 
					        try (CountingOutputStream dst =
 | 
				
			||||||
 | 
					            new CountingOutputStream(res.getOutputStream())) {
 | 
				
			||||||
          bin.writeTo(dst);
 | 
					          bin.writeTo(dst);
 | 
				
			||||||
 | 
					          return dst.getCount();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      return 0;
 | 
				
			||||||
    } finally {
 | 
					    } finally {
 | 
				
			||||||
      appResult.close();
 | 
					      appResult.close();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -993,7 +1002,7 @@ public class RestApiServlet extends HttpServlet {
 | 
				
			|||||||
        viewData.pluginName, viewData.view.getClass());
 | 
					        viewData.pluginName, viewData.view.getClass());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static void handleException(Throwable err, HttpServletRequest req,
 | 
					  private static long handleException(Throwable err, HttpServletRequest req,
 | 
				
			||||||
      HttpServletResponse res) throws IOException {
 | 
					      HttpServletResponse res) throws IOException {
 | 
				
			||||||
    String uri = req.getRequestURI();
 | 
					    String uri = req.getRequestURI();
 | 
				
			||||||
    if (!Strings.isNullOrEmpty(req.getQueryString())) {
 | 
					    if (!Strings.isNullOrEmpty(req.getQueryString())) {
 | 
				
			||||||
@@ -1003,16 +1012,17 @@ public class RestApiServlet extends HttpServlet {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (!res.isCommitted()) {
 | 
					    if (!res.isCommitted()) {
 | 
				
			||||||
      res.reset();
 | 
					      res.reset();
 | 
				
			||||||
      replyError(req, res, SC_INTERNAL_SERVER_ERROR, "Internal server error", err);
 | 
					      return replyError(req, res, SC_INTERNAL_SERVER_ERROR, "Internal server error", err);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static void replyError(HttpServletRequest req, HttpServletResponse res,
 | 
					  public static long replyError(HttpServletRequest req, HttpServletResponse res,
 | 
				
			||||||
      int statusCode, String msg, @Nullable Throwable err) throws IOException {
 | 
					      int statusCode, String msg, @Nullable Throwable err) throws IOException {
 | 
				
			||||||
    replyError(req, res, statusCode, msg, CacheControl.NONE, err);
 | 
					    return replyError(req, res, statusCode, msg, CacheControl.NONE, err);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static void replyError(HttpServletRequest req,
 | 
					  public static long replyError(HttpServletRequest req,
 | 
				
			||||||
      HttpServletResponse res, int statusCode, String msg,
 | 
					      HttpServletResponse res, int statusCode, String msg,
 | 
				
			||||||
      CacheControl c, @Nullable Throwable err) throws IOException {
 | 
					      CacheControl c, @Nullable Throwable err) throws IOException {
 | 
				
			||||||
    if (err != null) {
 | 
					    if (err != null) {
 | 
				
			||||||
@@ -1020,18 +1030,18 @@ public class RestApiServlet extends HttpServlet {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    configureCaching(req, res, null, null, c);
 | 
					    configureCaching(req, res, null, null, c);
 | 
				
			||||||
    res.setStatus(statusCode);
 | 
					    res.setStatus(statusCode);
 | 
				
			||||||
    replyText(req, res, msg);
 | 
					    return replyText(req, res, msg);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static void replyText(@Nullable HttpServletRequest req,
 | 
					  static long replyText(@Nullable HttpServletRequest req,
 | 
				
			||||||
      HttpServletResponse res, String text) throws IOException {
 | 
					      HttpServletResponse res, String text) throws IOException {
 | 
				
			||||||
    if ((req == null || isGetOrHead(req)) && isMaybeHTML(text)) {
 | 
					    if ((req == null || isGetOrHead(req)) && isMaybeHTML(text)) {
 | 
				
			||||||
      replyJson(req, res, ImmutableMultimap.of("pp", "0"), new JsonPrimitive(text));
 | 
					      return replyJson(req, res, ImmutableMultimap.of("pp", "0"), new JsonPrimitive(text));
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      if (!text.endsWith("\n")) {
 | 
					      if (!text.endsWith("\n")) {
 | 
				
			||||||
        text += "\n";
 | 
					        text += "\n";
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      replyBinaryResult(req, res,
 | 
					      return replyBinaryResult(req, res,
 | 
				
			||||||
          BinaryResult.create(text).setContentType("text/plain"));
 | 
					          BinaryResult.create(text).setContentType("text/plain"));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -91,6 +91,41 @@ public class DisabledMetricMaker extends MetricMaker {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public Histogram0 newHistogram(String name, Description desc) {
 | 
				
			||||||
 | 
					    return new Histogram0() {
 | 
				
			||||||
 | 
					      @Override public void record(long value) {}
 | 
				
			||||||
 | 
					      @Override public void remove() {}
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public <F1> Histogram1<F1> newHistogram(String name, Description desc,
 | 
				
			||||||
 | 
					      Field<F1> field1) {
 | 
				
			||||||
 | 
					    return new Histogram1<F1>() {
 | 
				
			||||||
 | 
					      @Override public void record(F1 field1, long value) {}
 | 
				
			||||||
 | 
					      @Override public void remove() {}
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public <F1, F2> Histogram2<F1, F2> newHistogram(String name, Description desc,
 | 
				
			||||||
 | 
					      Field<F1> field1, Field<F2> field2) {
 | 
				
			||||||
 | 
					    return new Histogram2<F1, F2>() {
 | 
				
			||||||
 | 
					      @Override public void record(F1 field1, F2 field2, long value) {}
 | 
				
			||||||
 | 
					      @Override public void remove() {}
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public <F1, F2, F3> Histogram3<F1, F2, F3> newHistogram(String name,
 | 
				
			||||||
 | 
					      Description desc, Field<F1> field1, Field<F2> field2, Field<F3> field3) {
 | 
				
			||||||
 | 
					    return new Histogram3<F1, F2, F3>() {
 | 
				
			||||||
 | 
					      @Override public void record(F1 field1, F2 field2, F3 field3, long value) {}
 | 
				
			||||||
 | 
					      @Override public void remove() {}
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public <V> CallbackMetric0<V> newCallbackMetric(String name,
 | 
					  public <V> CallbackMetric0<V> newCallbackMetric(String name,
 | 
				
			||||||
      Class<V> valueClass, Description desc) {
 | 
					      Class<V> valueClass, Description desc) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					// 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.metrics;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Measures the statistical distribution of values in a stream of data.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Suitable uses are "response size in bytes", etc.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class Histogram0 implements RegistrationHandle {
 | 
				
			||||||
 | 
					  /** Record a sample of a specified amount. */
 | 
				
			||||||
 | 
					  public abstract void record(long value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					// 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.metrics;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Measures the statistical distribution of values in a stream of data.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Suitable uses are "response size in bytes", etc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param <F1> type of the field.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class Histogram1<F1> implements RegistrationHandle {
 | 
				
			||||||
 | 
					  /** Record a sample of a specified amount. */
 | 
				
			||||||
 | 
					  public abstract void record(F1 field1, long value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					// 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.metrics;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Measures the statistical distribution of values in a stream of data.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Suitable uses are "response size in bytes", etc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param <F1> type of the field.
 | 
				
			||||||
 | 
					 * @param <F2> type of the field.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class Histogram2<F1, F2> implements RegistrationHandle {
 | 
				
			||||||
 | 
					  /** Record a sample of a specified amount. */
 | 
				
			||||||
 | 
					  public abstract void record(F1 field1, F2 field2, long value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					// 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.metrics;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.extensions.registration.RegistrationHandle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Measures the statistical distribution of values in a stream of data.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Suitable uses are "response size in bytes", etc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param <F1> type of the field.
 | 
				
			||||||
 | 
					 * @param <F2> type of the field.
 | 
				
			||||||
 | 
					 * @param <F3> type of the field.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class Histogram3<F1, F2, F3> implements RegistrationHandle {
 | 
				
			||||||
 | 
					  /** Record a sample of a specified amount. */
 | 
				
			||||||
 | 
					  public abstract void record(F1 field1, F2 field2, F3 field3, long value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -46,6 +46,18 @@ public abstract class MetricMaker {
 | 
				
			|||||||
      String name, Description desc,
 | 
					      String name, Description desc,
 | 
				
			||||||
      Field<F1> field1, Field<F2> field2, Field<F3> field3);
 | 
					      Field<F1> field1, Field<F2> field2, Field<F3> field3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Metric statistical distribution of values. */
 | 
				
			||||||
 | 
					  public abstract Histogram0 newHistogram(String name, Description desc);
 | 
				
			||||||
 | 
					  public abstract <F1> Histogram1<F1> newHistogram(
 | 
				
			||||||
 | 
					      String name, Description desc,
 | 
				
			||||||
 | 
					      Field<F1> field1);
 | 
				
			||||||
 | 
					  public abstract <F1, F2> Histogram2<F1, F2> newHistogram(
 | 
				
			||||||
 | 
					      String name, Description desc,
 | 
				
			||||||
 | 
					      Field<F1> field1, Field<F2> field2);
 | 
				
			||||||
 | 
					  public abstract <F1, F2, F3> Histogram3<F1, F2, F3> newHistogram(
 | 
				
			||||||
 | 
					      String name, Description desc,
 | 
				
			||||||
 | 
					      Field<F1> field1, Field<F2> field2, Field<F3> field3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Constant value that does not change.
 | 
					   * Constant value that does not change.
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					// 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.metrics.dropwizard;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.common.base.Function;
 | 
				
			||||||
 | 
					import com.google.common.collect.ImmutableList;
 | 
				
			||||||
 | 
					import com.google.common.collect.Maps;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Description;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Field;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker.HistogramImpl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.codahale.metrics.Metric;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentHashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Abstract histogram broken down into buckets by {@link Field} values. */
 | 
				
			||||||
 | 
					abstract class BucketedHistogram implements BucketedMetric {
 | 
				
			||||||
 | 
					  private final DropWizardMetricMaker metrics;
 | 
				
			||||||
 | 
					  private final String name;
 | 
				
			||||||
 | 
					  private final Description.FieldOrdering ordering;
 | 
				
			||||||
 | 
					  protected final Field<?>[] fields;
 | 
				
			||||||
 | 
					  protected final HistogramImpl total;
 | 
				
			||||||
 | 
					  private final Map<Object, HistogramImpl> cells;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BucketedHistogram(DropWizardMetricMaker metrics, String name,
 | 
				
			||||||
 | 
					      Description desc, Field<?>... fields) {
 | 
				
			||||||
 | 
					    this.metrics = metrics;
 | 
				
			||||||
 | 
					    this.name = name;
 | 
				
			||||||
 | 
					    this.ordering = desc.getFieldOrdering();
 | 
				
			||||||
 | 
					    this.fields = fields;
 | 
				
			||||||
 | 
					    this.total = metrics.newHistogramImpl(name + "_total");
 | 
				
			||||||
 | 
					    this.cells = new ConcurrentHashMap<>();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void doRemove() {
 | 
				
			||||||
 | 
					    for (HistogramImpl c : cells.values()) {
 | 
				
			||||||
 | 
					      c.remove();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    total.remove();
 | 
				
			||||||
 | 
					    metrics.remove(name);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  HistogramImpl forceCreate(Object f1, Object f2) {
 | 
				
			||||||
 | 
					    return forceCreate(ImmutableList.of(f1, f2));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  HistogramImpl forceCreate(Object f1, Object f2, Object f3) {
 | 
				
			||||||
 | 
					    return forceCreate(ImmutableList.of(f1, f2, f3));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  HistogramImpl forceCreate(Object key) {
 | 
				
			||||||
 | 
					    HistogramImpl c = cells.get(key);
 | 
				
			||||||
 | 
					    if (c != null) {
 | 
				
			||||||
 | 
					      return c;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized (cells) {
 | 
				
			||||||
 | 
					      c = cells.get(key);
 | 
				
			||||||
 | 
					      if (c == null) {
 | 
				
			||||||
 | 
					        c = metrics.newHistogramImpl(submetric(key));
 | 
				
			||||||
 | 
					        cells.put(key, c);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return c;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private String submetric(Object key) {
 | 
				
			||||||
 | 
					    return DropWizardMetricMaker.name(ordering, name, name(key));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  abstract String name(Object key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public Metric getTotal() {
 | 
				
			||||||
 | 
					    return total.metric;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public Field<?>[] getFields() {
 | 
				
			||||||
 | 
					    return fields;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public Map<Object, Metric> getCells() {
 | 
				
			||||||
 | 
					    return Maps.transformValues(
 | 
				
			||||||
 | 
					        cells,
 | 
				
			||||||
 | 
					        new Function<HistogramImpl, Metric> () {
 | 
				
			||||||
 | 
					          @Override
 | 
				
			||||||
 | 
					          public Metric apply(HistogramImpl in) {
 | 
				
			||||||
 | 
					            return in.metric;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -32,6 +32,10 @@ import com.google.gerrit.metrics.Counter3;
 | 
				
			|||||||
import com.google.gerrit.metrics.Description;
 | 
					import com.google.gerrit.metrics.Description;
 | 
				
			||||||
import com.google.gerrit.metrics.Description.FieldOrdering;
 | 
					import com.google.gerrit.metrics.Description.FieldOrdering;
 | 
				
			||||||
import com.google.gerrit.metrics.Field;
 | 
					import com.google.gerrit.metrics.Field;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram0;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram1;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram2;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram3;
 | 
				
			||||||
import com.google.gerrit.metrics.MetricMaker;
 | 
					import com.google.gerrit.metrics.MetricMaker;
 | 
				
			||||||
import com.google.gerrit.metrics.Timer0;
 | 
					import com.google.gerrit.metrics.Timer0;
 | 
				
			||||||
import com.google.gerrit.metrics.Timer1;
 | 
					import com.google.gerrit.metrics.Timer1;
 | 
				
			||||||
@@ -226,6 +230,56 @@ public class DropWizardMetricMaker extends MetricMaker {
 | 
				
			|||||||
    return new TimerImpl(name, registry.timer(name));
 | 
					    return new TimerImpl(name, registry.timer(name));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public synchronized Histogram0 newHistogram(String name, Description desc) {
 | 
				
			||||||
 | 
					    checkHistogramDescription(name, desc);
 | 
				
			||||||
 | 
					    define(name, desc);
 | 
				
			||||||
 | 
					    return newHistogramImpl(name);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public synchronized <F1> Histogram1<F1> newHistogram(String name,
 | 
				
			||||||
 | 
					      Description desc, Field<F1> field1) {
 | 
				
			||||||
 | 
					    checkHistogramDescription(name, desc);
 | 
				
			||||||
 | 
					    HistogramImpl1<F1> m = new HistogramImpl1<>(this, name, desc, field1);
 | 
				
			||||||
 | 
					    define(name, desc);
 | 
				
			||||||
 | 
					    bucketed.put(name, m);
 | 
				
			||||||
 | 
					    return m.histogram1();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public synchronized <F1, F2> Histogram2<F1, F2> newHistogram(String name,
 | 
				
			||||||
 | 
					      Description desc, Field<F1> field1, Field<F2> field2) {
 | 
				
			||||||
 | 
					    checkHistogramDescription(name, desc);
 | 
				
			||||||
 | 
					    HistogramImplN m = new HistogramImplN(this, name, desc, field1, field2);
 | 
				
			||||||
 | 
					    define(name, desc);
 | 
				
			||||||
 | 
					    bucketed.put(name, m);
 | 
				
			||||||
 | 
					    return m.histogram2();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public synchronized <F1, F2, F3> Histogram3<F1, F2, F3> newHistogram(
 | 
				
			||||||
 | 
					      String name, Description desc,
 | 
				
			||||||
 | 
					      Field<F1> field1, Field<F2> field2, Field<F3> field3) {
 | 
				
			||||||
 | 
					    checkHistogramDescription(name, desc);
 | 
				
			||||||
 | 
					    HistogramImplN m = new HistogramImplN(this, name, desc, field1, field2, field3);
 | 
				
			||||||
 | 
					    define(name, desc);
 | 
				
			||||||
 | 
					    bucketed.put(name, m);
 | 
				
			||||||
 | 
					    return m.histogram3();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static void checkHistogramDescription(String name, Description desc) {
 | 
				
			||||||
 | 
					    checkMetricName(name);
 | 
				
			||||||
 | 
					    checkArgument(!desc.isConstant(), "histogram must not be constant");
 | 
				
			||||||
 | 
					    checkArgument(!desc.isGauge(), "histogram must not be a gauge");
 | 
				
			||||||
 | 
					    checkArgument(!desc.isRate(), "histogram must not be a rate");
 | 
				
			||||||
 | 
					    checkArgument(desc.isCumulative(), "histogram must be cumulative");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  HistogramImpl newHistogramImpl(String name) {
 | 
				
			||||||
 | 
					    return new HistogramImpl(name, registry.histogram(name));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public <V> CallbackMetric0<V> newCallbackMetric(
 | 
					  public <V> CallbackMetric0<V> newCallbackMetric(
 | 
				
			||||||
      String name, Class<V> valueClass, Description desc) {
 | 
					      String name, Class<V> valueClass, Description desc) {
 | 
				
			||||||
@@ -339,4 +393,25 @@ public class DropWizardMetricMaker extends MetricMaker {
 | 
				
			|||||||
      registry.remove(name);
 | 
					      registry.remove(name);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  class HistogramImpl extends Histogram0 {
 | 
				
			||||||
 | 
					    private final String name;
 | 
				
			||||||
 | 
					    final com.codahale.metrics.Histogram metric;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private HistogramImpl(String name, com.codahale.metrics.Histogram metric) {
 | 
				
			||||||
 | 
					      this.name = name;
 | 
				
			||||||
 | 
					      this.metric = metric;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void record(long value) {
 | 
				
			||||||
 | 
					      metric.update(value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void remove() {
 | 
				
			||||||
 | 
					      descriptions.remove(name);
 | 
				
			||||||
 | 
					      registry.remove(name);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					// 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.metrics.dropwizard;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.common.base.Function;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Description;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Field;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Optimized version of {@link BucketedHistogram} for single dimension. */
 | 
				
			||||||
 | 
					class HistogramImpl1<F1> extends BucketedHistogram implements BucketedMetric {
 | 
				
			||||||
 | 
					  HistogramImpl1(DropWizardMetricMaker metrics, String name,
 | 
				
			||||||
 | 
					      Description desc, Field<F1> field1) {
 | 
				
			||||||
 | 
					    super(metrics, name, desc, field1);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Histogram1<F1> histogram1() {
 | 
				
			||||||
 | 
					    return new Histogram1<F1>() {
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void record(F1 field1, long value) {
 | 
				
			||||||
 | 
					        total.record(value);
 | 
				
			||||||
 | 
					        forceCreate(field1).record(value);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void remove() {
 | 
				
			||||||
 | 
					        doRemove();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  String name(Object field1) {
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    Function<Object, String> fmt =
 | 
				
			||||||
 | 
					        (Function<Object, String>) fields[0].formatter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return fmt.apply(field1).replace('/', '-');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					// 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.metrics.dropwizard;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.common.base.Function;
 | 
				
			||||||
 | 
					import com.google.common.base.Joiner;
 | 
				
			||||||
 | 
					import com.google.common.collect.ImmutableList;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Description;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Field;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram2;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Generalized implementation of N-dimensional Histogram metrics. */
 | 
				
			||||||
 | 
					class HistogramImplN extends BucketedHistogram implements BucketedMetric {
 | 
				
			||||||
 | 
					  HistogramImplN(DropWizardMetricMaker metrics, String name,
 | 
				
			||||||
 | 
					      Description desc, Field<?>... fields) {
 | 
				
			||||||
 | 
					    super(metrics, name, desc, fields);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <F1, F2> Histogram2<F1, F2> histogram2() {
 | 
				
			||||||
 | 
					    return new Histogram2<F1, F2>() {
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void record(F1 field1, F2 field2, long value) {
 | 
				
			||||||
 | 
					        total.record(value);
 | 
				
			||||||
 | 
					        forceCreate(field1, field2).record(value);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void remove() {
 | 
				
			||||||
 | 
					        doRemove();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <F1, F2, F3> Histogram3<F1, F2, F3> histogram3() {
 | 
				
			||||||
 | 
					    return new Histogram3<F1, F2, F3>() {
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void record(F1 field1, F2 field2, F3 field3, long value) {
 | 
				
			||||||
 | 
					        total.record(value);
 | 
				
			||||||
 | 
					        forceCreate(field1, field2, field3).record(value);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void remove() {
 | 
				
			||||||
 | 
					        doRemove();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  String name(Object key) {
 | 
				
			||||||
 | 
					    ImmutableList<Object> keyList = (ImmutableList<Object>) key;
 | 
				
			||||||
 | 
					    String[] parts = new String[fields.length];
 | 
				
			||||||
 | 
					    for (int i = 0; i < fields.length; i++) {
 | 
				
			||||||
 | 
					      Function<Object, String> fmt =
 | 
				
			||||||
 | 
					          (Function<Object, String>) fields[i].formatter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      parts[i] = fmt.apply(keyList.get(i)).replace('/', '-');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return Joiner.on('/').join(parts);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -22,6 +22,7 @@ import com.google.gerrit.metrics.Field;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.codahale.metrics.Counter;
 | 
					import com.codahale.metrics.Counter;
 | 
				
			||||||
import com.codahale.metrics.Gauge;
 | 
					import com.codahale.metrics.Gauge;
 | 
				
			||||||
 | 
					import com.codahale.metrics.Histogram;
 | 
				
			||||||
import com.codahale.metrics.Meter;
 | 
					import com.codahale.metrics.Meter;
 | 
				
			||||||
import com.codahale.metrics.Metric;
 | 
					import com.codahale.metrics.Metric;
 | 
				
			||||||
import com.codahale.metrics.Snapshot;
 | 
					import com.codahale.metrics.Snapshot;
 | 
				
			||||||
@@ -56,7 +57,9 @@ class MetricJson {
 | 
				
			|||||||
  Double p99_9;
 | 
					  Double p99_9;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Double min;
 | 
					  Double min;
 | 
				
			||||||
 | 
					  Double avg;
 | 
				
			||||||
  Double max;
 | 
					  Double max;
 | 
				
			||||||
 | 
					  Double sum;
 | 
				
			||||||
  Double std_dev;
 | 
					  Double std_dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  List<FieldJson> fields;
 | 
					  List<FieldJson> fields;
 | 
				
			||||||
@@ -123,6 +126,24 @@ class MetricJson {
 | 
				
			|||||||
      min = s.getMin() / div;
 | 
					      min = s.getMin() / div;
 | 
				
			||||||
      max = s.getMax() / div;
 | 
					      max = s.getMax() / div;
 | 
				
			||||||
      std_dev = s.getStdDev() / div;
 | 
					      std_dev = s.getStdDev() / div;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else if (metric instanceof Histogram) {
 | 
				
			||||||
 | 
					      Histogram m = (Histogram) metric;
 | 
				
			||||||
 | 
					      Snapshot s = m.getSnapshot();
 | 
				
			||||||
 | 
					      count = m.getCount();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      p50 = s.getMedian();
 | 
				
			||||||
 | 
					      p75 = s.get75thPercentile();
 | 
				
			||||||
 | 
					      p95 = s.get95thPercentile();
 | 
				
			||||||
 | 
					      p98 = s.get98thPercentile();
 | 
				
			||||||
 | 
					      p99 = s.get99thPercentile();
 | 
				
			||||||
 | 
					      p99_9 = s.get999thPercentile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      min = (double) s.getMin();
 | 
				
			||||||
 | 
					      avg = (double) s.getMean();
 | 
				
			||||||
 | 
					      max = (double) s.getMax();
 | 
				
			||||||
 | 
					      sum = s.getMean() * m.getCount();
 | 
				
			||||||
 | 
					      std_dev = s.getStdDev();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ import com.google.gerrit.metrics.Counter1;
 | 
				
			|||||||
import com.google.gerrit.metrics.Description;
 | 
					import com.google.gerrit.metrics.Description;
 | 
				
			||||||
import com.google.gerrit.metrics.Description.Units;
 | 
					import com.google.gerrit.metrics.Description.Units;
 | 
				
			||||||
import com.google.gerrit.metrics.Field;
 | 
					import com.google.gerrit.metrics.Field;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram1;
 | 
				
			||||||
import com.google.gerrit.metrics.MetricMaker;
 | 
					import com.google.gerrit.metrics.MetricMaker;
 | 
				
			||||||
import com.google.gerrit.metrics.Timer1;
 | 
					import com.google.gerrit.metrics.Timer1;
 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
@@ -39,6 +40,7 @@ public class UploadPackMetricsHook implements PostUploadHook {
 | 
				
			|||||||
  private final Timer1<Operation> counting;
 | 
					  private final Timer1<Operation> counting;
 | 
				
			||||||
  private final Timer1<Operation> compressing;
 | 
					  private final Timer1<Operation> compressing;
 | 
				
			||||||
  private final Timer1<Operation> writing;
 | 
					  private final Timer1<Operation> writing;
 | 
				
			||||||
 | 
					  private final Histogram1<Operation> packBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  UploadPackMetricsHook(MetricMaker metricMaker) {
 | 
					  UploadPackMetricsHook(MetricMaker metricMaker) {
 | 
				
			||||||
@@ -70,6 +72,13 @@ public class UploadPackMetricsHook implements PostUploadHook {
 | 
				
			|||||||
          .setCumulative()
 | 
					          .setCumulative()
 | 
				
			||||||
          .setUnit(Units.MILLISECONDS),
 | 
					          .setUnit(Units.MILLISECONDS),
 | 
				
			||||||
        operation);
 | 
					        operation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    packBytes = metricMaker.newHistogram(
 | 
				
			||||||
 | 
					        "git/upload-pack/pack_bytes",
 | 
				
			||||||
 | 
					        new Description("Distribution of sizes of packs sent to clients")
 | 
				
			||||||
 | 
					          .setCumulative()
 | 
				
			||||||
 | 
					          .setUnit(Units.BYTES),
 | 
				
			||||||
 | 
					        operation);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
@@ -84,5 +93,6 @@ public class UploadPackMetricsHook implements PostUploadHook {
 | 
				
			|||||||
    counting.record(op, stats.getTimeCounting(), MILLISECONDS);
 | 
					    counting.record(op, stats.getTimeCounting(), MILLISECONDS);
 | 
				
			||||||
    compressing.record(op, stats.getTimeCompressing(), MILLISECONDS);
 | 
					    compressing.record(op, stats.getTimeCompressing(), MILLISECONDS);
 | 
				
			||||||
    writing.record(op, stats.getTimeWriting(), MILLISECONDS);
 | 
					    writing.record(op, stats.getTimeWriting(), MILLISECONDS);
 | 
				
			||||||
 | 
					    packBytes.record(op, stats.getTotalBytes());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,10 @@ import com.google.gerrit.metrics.Counter2;
 | 
				
			|||||||
import com.google.gerrit.metrics.Counter3;
 | 
					import com.google.gerrit.metrics.Counter3;
 | 
				
			||||||
import com.google.gerrit.metrics.Description;
 | 
					import com.google.gerrit.metrics.Description;
 | 
				
			||||||
import com.google.gerrit.metrics.Field;
 | 
					import com.google.gerrit.metrics.Field;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram0;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram1;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram2;
 | 
				
			||||||
 | 
					import com.google.gerrit.metrics.Histogram3;
 | 
				
			||||||
import com.google.gerrit.metrics.MetricMaker;
 | 
					import com.google.gerrit.metrics.MetricMaker;
 | 
				
			||||||
import com.google.gerrit.metrics.Timer0;
 | 
					import com.google.gerrit.metrics.Timer0;
 | 
				
			||||||
import com.google.gerrit.metrics.Timer1;
 | 
					import com.google.gerrit.metrics.Timer1;
 | 
				
			||||||
@@ -117,6 +121,41 @@ class PluginMetricMaker extends MetricMaker implements LifecycleListener {
 | 
				
			|||||||
    return m;
 | 
					    return m;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public Histogram0 newHistogram(String name, Description desc) {
 | 
				
			||||||
 | 
					    Histogram0 m = root.newHistogram(prefix + name, desc);
 | 
				
			||||||
 | 
					    cleanup.add(m);
 | 
				
			||||||
 | 
					    return m;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public <F1> Histogram1<F1> newHistogram(
 | 
				
			||||||
 | 
					      String name, Description desc,
 | 
				
			||||||
 | 
					      Field<F1> field1) {
 | 
				
			||||||
 | 
					    Histogram1<F1> m = root.newHistogram(prefix + name, desc, field1);
 | 
				
			||||||
 | 
					    cleanup.add(m);
 | 
				
			||||||
 | 
					    return m;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public <F1, F2> Histogram2<F1, F2> newHistogram(
 | 
				
			||||||
 | 
					      String name, Description desc,
 | 
				
			||||||
 | 
					      Field<F1> field1, Field<F2> field2) {
 | 
				
			||||||
 | 
					    Histogram2<F1, F2> m = root.newHistogram(prefix + name, desc, field1, field2);
 | 
				
			||||||
 | 
					    cleanup.add(m);
 | 
				
			||||||
 | 
					    return m;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public <F1, F2, F3> Histogram3<F1, F2, F3> newHistogram(
 | 
				
			||||||
 | 
					      String name, Description desc,
 | 
				
			||||||
 | 
					      Field<F1> field1, Field<F2> field2, Field<F3> field3) {
 | 
				
			||||||
 | 
					    Histogram3<F1, F2, F3> m =
 | 
				
			||||||
 | 
					        root.newHistogram(prefix + name, desc, field1, field2, field3);
 | 
				
			||||||
 | 
					    cleanup.add(m);
 | 
				
			||||||
 | 
					    return m;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public <V> CallbackMetric0<V> newCallbackMetric(
 | 
					  public <V> CallbackMetric0<V> newCallbackMetric(
 | 
				
			||||||
      String name, Class<V> valueClass, Description desc) {
 | 
					      String name, Class<V> valueClass, Description desc) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user