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

zipkin.reporter.urlconnection.URLConnectionReporter Maven / Gradle / Ivy

/**
 * Copyright 2016 The OpenZipkin Authors
 *
 * 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 zipkin.reporter.urlconnection;

import com.google.auto.value.AutoValue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.zip.GZIPOutputStream;
import zipkin.Codec;
import zipkin.Span;
import zipkin.reporter.Reporter;

import static zipkin.internal.Util.checkNotNull;

/**
 * Reports spans to Zipkin, using its POST endpoint.
 */
@AutoValue
public abstract class URLConnectionReporter implements Reporter {

  public static Builder builder() {
    return new AutoValue_URLConnectionReporter.Builder()
        .connectTimeout(10 * 1000)
        .readTimeout(60 * 1000)
        .compressionEnabled(true)
        .executor(Runnable::run);
  }

  @AutoValue.Builder
  public static abstract class Builder {
    /**
     * No default. The POST URL for zipkin's v1 api,
     * usually "http://zipkinhost:9411/api/v1/spans"
     */
    // customizable so that users can re-map /api/v1/spans ex for browser-originated traces
    public final Builder endpoint(String endpoint) {
      checkNotNull(endpoint, "endpoint ex: http://zipkinhost:9411/api/v1/spans");
      try {
        return endpoint(new URL(endpoint));
      } catch (MalformedURLException e) {
        throw new IllegalArgumentException(e.getMessage());
      }
    }

    public abstract Builder endpoint(URL postUrl);

    /** Default 10 * 1000 milliseconds. 0 implies no timeout. */
    public abstract Builder connectTimeout(int connectTimeout);

    /** Default 60 * 1000 milliseconds. 0 implies no timeout. */
    public abstract Builder readTimeout(int readTimeout);

    /** Default true. true implies that spans will be gzipped before transport. */
    public abstract Builder compressionEnabled(boolean compressSpans);

    /** Default calling thread. The executor used to defer http requests. */
    public abstract Builder executor(Executor executor);

    public abstract URLConnectionReporter build();

    Builder() {
    }
  }

  public Builder toBuilder() {
    return new AutoValue_URLConnectionReporter.Builder(this);
  }

  abstract URL endpoint();

  abstract int connectTimeout();

  abstract int readTimeout();

  abstract boolean compressionEnabled();

  abstract Executor executor();

  /** Asynchronously sends the spans as a json POST to {@link #endpoint()}. */
  @Override public void report(List spans, Callback callback) {
    executor().execute(() -> {
      try {
        byte[] body = Codec.JSON.writeSpans(spans);
        send(body, "application/json");
        callback.onComplete();
      } catch (RuntimeException | IOException | Error e) {
        callback.onError(e);
        if (e instanceof Error) throw (Error) e;
      }
    });
  }

  void send(byte[] body, String mediaType) throws IOException {
    // intentionally not closing the connection, so as to use keep-alives
    HttpURLConnection connection = (HttpURLConnection) endpoint().openConnection();
    connection.setConnectTimeout(connectTimeout());
    connection.setReadTimeout(readTimeout());
    connection.setRequestMethod("POST");
    connection.addRequestProperty("Content-Type", mediaType);
    if (compressionEnabled()) {
      connection.addRequestProperty("Content-Encoding", "gzip");
      ByteArrayOutputStream gzipped = new ByteArrayOutputStream();
      try (GZIPOutputStream compressor = new GZIPOutputStream(gzipped)) {
        compressor.write(body);
      }
      body = gzipped.toByteArray();
    }
    connection.setDoOutput(true);
    connection.setFixedLengthStreamingMode(body.length);
    connection.getOutputStream().write(body);

    try (InputStream in = connection.getInputStream()) {
      while (in.read() != -1) ; // skip
    } catch (IOException e) {
      try (InputStream err = connection.getErrorStream()) {
        if (err != null) { // possible, if the connection was dropped
          while (err.read() != -1) ; // skip
        }
      }
      throw e;
    }
  }

  URLConnectionReporter() {
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy