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

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

There is a newer version: 1.1.2
Show newest version
/**
 * Copyright 2016-2017 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.zip.GZIPOutputStream;
import zipkin.reporter.BytesMessageEncoder;
import zipkin.reporter.Callback;
import zipkin.reporter.Encoding;
import zipkin.reporter.Sender;

/**
 * Reports spans to Zipkin, using its POST endpoint.
 *
 * 

This sender is thread-safe. */ @AutoValue public abstract class URLConnectionSender implements Sender { /** Creates a sender that posts {@link Encoding#THRIFT} messages. */ public static URLConnectionSender create(String endpoint) { return builder().endpoint(endpoint).build(); } /** Creates a sender that posts {@link Encoding#JSON} messages. */ public static URLConnectionSender json(String endpoint) { return builder().encoding(Encoding.JSON).endpoint(endpoint).build(); } public static Builder builder() { return new AutoValue_URLConnectionSender.Builder() .encoding(Encoding.THRIFT) .connectTimeout(10 * 1000) .readTimeout(60 * 1000) .compressionEnabled(true) .messageMaxBytes(5 * 1024 * 1024); } @AutoValue.Builder public static abstract class Builder { /** * No default. The POST URL for zipkin's v2 api, * usually "http://zipkinhost:9411/api/v2/spans" */ // customizable so that users can re-map /api/v2/spans ex for browser-originated traces public final Builder endpoint(String endpoint) { if (endpoint == null) throw new NullPointerException("endpoint == null"); 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); /** Maximum size of a message. Default 5MiB */ public abstract Builder messageMaxBytes(int messageMaxBytes); /** Controls the "Content-Type" header when sending spans. */ public abstract Builder encoding(Encoding encoding); abstract Encoding encoding(); public final URLConnectionSender build() { if (encoding() == Encoding.JSON) { return mediaType("application/json").encoder(BytesMessageEncoder.JSON).autoBuild(); } else if (encoding() == Encoding.THRIFT) { return mediaType("application/x-thrift").encoder(BytesMessageEncoder.THRIFT).autoBuild(); } throw new UnsupportedOperationException("Unsupported encoding: " + encoding().name()); } abstract Builder encoder(BytesMessageEncoder encoder); abstract Builder mediaType(String mediaType); abstract URLConnectionSender autoBuild(); Builder() { } } public abstract Builder toBuilder(); abstract BytesMessageEncoder encoder(); abstract URL endpoint(); abstract int connectTimeout(); abstract int readTimeout(); abstract boolean compressionEnabled(); abstract String mediaType(); /** close is typically called from a different thread */ volatile boolean closeCalled; @Override public int messageSizeInBytes(List encodedSpans) { return encoding().listSizeInBytes(encodedSpans); } /** Asynchronously sends the spans as a POST to {@link #endpoint()}. */ @Override public void sendSpans(List encodedSpans, Callback callback) { if (closeCalled) throw new IllegalStateException("close"); try { byte[] message = encoder().encode(encodedSpans); send(message, mediaType()); callback.onComplete(); } catch (Throwable e) { callback.onError(e); if (e instanceof Error) throw (Error) e; } } /** Sends an empty json message to the configured endpoint. */ @Override public CheckResult check() { try { send(new byte[] {'[', ']'}, "application/json"); return CheckResult.OK; } catch (Exception e) { return CheckResult.failed(e); } } @Override public void close() { closeCalled = true; } 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(); GZIPOutputStream compressor = new GZIPOutputStream(gzipped); try { compressor.write(body); } finally { compressor.close(); } body = gzipped.toByteArray(); } connection.setDoOutput(true); connection.setFixedLengthStreamingMode(body.length); connection.getOutputStream().write(body); skipAllContent(connection); } /** This utility is verbose as we have a minimum java version of 6 */ static void skipAllContent(HttpURLConnection connection) throws IOException { InputStream in = connection.getInputStream(); IOException thrown = skipAndSuppress(in); if (thrown == null) return; InputStream err = connection.getErrorStream(); if (err != null) skipAndSuppress(err); // null is possible, if the connection was dropped throw thrown; } static IOException skipAndSuppress(InputStream in) { try { while (in.read() != -1) ; // skip return null; } catch (IOException e) { return e; } finally { try { in.close(); } catch (IOException suppressed) { } } } @Override public final String toString() { return "URLConnectionSender(" + endpoint() + ")"; } URLConnectionSender() { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy