All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.susom.vertx.base.MetricsHandler Maven / Gradle / Ivy

/*
 * Copyright 2016 The Board of Trustees of The Leland Stanford Junior University.
 * All Rights Reserved.
 *
 * See the NOTICE and LICENSE files distributed with this work for information
 * regarding copyright ownership and licensing. You may not use this file except
 * in compliance with a written license agreement with Stanford University.
 *
 * 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 your
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.github.susom.vertx.base;

import com.github.susom.database.Metric;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nonnull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

/**
 * Simple body handler that enforces a size limit (16k by default)
 * and prohibits file uploads.
 *
 * @author garricko
 */
public class MetricsHandler implements Handler {
  private static final Logger log = LoggerFactory.getLogger(MetricsHandler.class);
  private final boolean dumpRequest;
  private final String requestIdPrefix;
  private long requestId = 1;
  private List headersToLog = new ArrayList<>();

  public MetricsHandler(SecureRandom secureRandom) {
    this(secureRandom, false);
  }

  public MetricsHandler(SecureRandom secureRandom, boolean dumpRequest) {
    this.requestIdPrefix = new TokenGenerator(secureRandom).create(5);
    this.dumpRequest = dumpRequest;
  }

  public MetricsHandler logHeaders(String... headers) {
    headersToLog.addAll(Arrays.asList(headers));
    return this;
  }

  public MetricsHandler logXForwardedFor() {
    headersToLog.add("X-Forwarded-For");
    return this;
  }

  public MetricsHandler logUserAgent() {
    headersToLog.add("User-Agent");
    return this;
  }

  /**
   * Get the Metric object for the specified routing context, creating it
   * if it does not already exist. This gets called automatically by this
   * handler. It is mainly intended for situations where you might have a
   * handler sitting in front of this one (e.g. for authentication), and
   * you want to make sure the metrics object is available and starts its
   * timer before you do anything.
   *
   * @deprecated use checkpoint() instead because Metric is shaded by this library
   */
  @Nonnull @Deprecated
  public static Metric metricFor(@Nonnull RoutingContext rc) {
    Metric metric = rc.get("metric");
    if (metric == null) {
      metric = new Metric(log.isDebugEnabled());
      rc.put("metric", metric);
    }
    return metric;
  }

  public static void checkpoint(@Nonnull RoutingContext rc, String label) {
    Metric metric = rc.get("metric");
    if (metric == null) {
      metric = new Metric(log.isDebugEnabled());
      rc.put("metric", metric);
    }
    metric.checkpoint(label);
  }

  public void handle(RoutingContext rc) {
    String externalRequestId = rc.request().getHeader("X-REQUEST-ID");
    if (externalRequestId == null || !externalRequestId.matches("[a-zA-Z0-9:]{1,80}")) {
      externalRequestId = "";
    } else {
      externalRequestId = ":" + externalRequestId;
    }
    MDC.put("requestId", requestIdPrefix + Long.toString(requestId++, Character.MAX_RADIX) + externalRequestId);
    Metric metric = metricFor(rc);
    String query = rc.request().query();
    if (log.isDebugEnabled()) {
      StringBuilder message = new StringBuilder();
      message.append("Received ").append(rc.request().method()).append(" ").append(rc.request().path());
      if (query != null && query.length() > 0) {
        message.append("?").append(query);
      }
      if (headersToLog != null) {
        for (String header : headersToLog) {
          String headerValue = rc.request().getHeader(header);
          if (headerValue != null) {
            message.append("\n    ").append(header).append(": ").append(headerValue);
          }
        }
      }
      log.debug(message.toString());
    }
    rc.response().headersEndHandler(h -> metric.checkpoint("call"));
    rc.addBodyEndHandler(h -> {
      metric.done("send");

      if (log.isDebugEnabled()) {
        StringBuilder buf = new StringBuilder();
        buf.append("Served ").append(rc.response().getStatusCode()).append(": ");
        metric.printMessage(buf);
        buf.append(' ').append(rc.request().path());
        if (dumpRequest) {
          buf.append("\nHeaders: ").append(rc.request().headers().entries());
          String contentType = rc.request().headers().get("Content-Type");
          if (contentType != null && contentType.contains("application/json")) {
            JsonObject body = new JsonObject(rc.getBodyAsString());
            if (body.containsKey("password")) {
              body.put("password", "xxx");
            }
            buf.append("\nBody: ").append(body.encodePrettily());
          }
        }
        log.debug(buf.toString());
      }
    });
    rc.next();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy