diff --git a/htrace-core/pom.xml b/htrace-core/pom.xml index 5c37dc8..d3b7f1b 100644 --- a/htrace-core/pom.xml +++ b/htrace-core/pom.xml @@ -145,20 +145,18 @@ language governing permissions and limitations under the License. --> junit test - - - commons-logging - commons-logging - com.fasterxml.jackson.core jackson-core - 2.4.0 com.fasterxml.jackson.core jackson-databind - 2.4.0 + + + + commons-logging + commons-logging diff --git a/htrace-htraced/pom.xml b/htrace-htraced/pom.xml new file mode 100644 index 0000000..57ab56b --- /dev/null +++ b/htrace-htraced/pom.xml @@ -0,0 +1,156 @@ + + + + 4.0.0 + + htrace-htraced + jar + + + htrace + org.apache.htrace + 3.1.0-incubating + .. + + + htrace-htraced + HTraced and HTraced clients + http://incubator.apache.org/projects/htrace.html + + + UTF-8 + + + + + + + maven-assembly-plugin + + true + + + + org.apache.maven.plugins + maven-source-plugin + + + maven-javadoc-plugin + + + maven-compiler-plugin + + + org.apache.maven.plugins + maven-gpg-plugin + + + org.apache.rat + apache-rat-plugin + + + + maven-deploy-plugin + + + maven-assembly-plugin + + + jar-with-dependencies + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + + + org.apache.commons.logging + org.apache.htrace.commons.logging + + + com.fasterxml.jackson + org.apache.htrace.fasterxml.jackson + + + org.eclipse.jetty + org.apache.htrace.jetty + + + + + shade + + + + + + + + + + org.apache.htrace + htrace-core + ${project.version} + provided + + + org.apache.htrace + htrace-core + ${project.version} + tests + test + + + + commons-logging + commons-logging + provided + + + junit + junit + test + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + + + org.eclipse.jetty + jetty-client + 9.2.6.v20141205 + + + + joda-time + joda-time + 2.6 + + + diff --git a/htrace-htraced/src/main/java/org/apache/htrace/impl/HTracedRESTReceiver.java b/htrace-htraced/src/main/java/org/apache/htrace/impl/HTracedRESTReceiver.java new file mode 100644 index 0000000..4b600e5 --- /dev/null +++ b/htrace-htraced/src/main/java/org/apache/htrace/impl/HTracedRESTReceiver.java @@ -0,0 +1,240 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.htrace.impl; + + +import java.io.IOException; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.htrace.Span; +import org.apache.htrace.SpanReceiver; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.joda.time.Duration; + +/** + * A {@link SpanReceiver} that passes Spans to htraced via REST. Implementation minimizes + * dependencies and aims for small footprint since this client will be the guest of another, + * the process traced. + * + *

This client depends on the REST defined in rest.go in the htraced REST server. + * For example, a GET on /serverInfo returns the htraced server info. + * + *

Create an instance by doing: + * SpanReceiver receiver = + * HTracedRESTReceiver.builder().hostname(HTRACED_HOSTNAME).port(HTRACED_PORT).build(); + * Then call receiver.receiveSpan(Span); to send spans to htraced. This method + * returns immediately. It sends the spans in background asynchronously. + * + *

TODO: How to be more dependent on rest.go so we break if it changes? + * TODO: Figure logging. Jetty does its own. + * TODO: Add tests. Add start/stop htraced. + */ +public class HTracedRESTReceiver implements SpanReceiver { + // Uses Builder and Fluent patterns as described here: + // http://jlordiales.me/2012/12/13/the-builder-pattern-in-practice/ + private static final Log LOG = LogFactory.getLog(HTracedRESTReceiver.class); + + private final HttpClient httpClient; + // TODO: Take process name and add this to user agent? Help debugging? + // TODO: A logger to use. Have it passed into the receiver builder. TODO. + + /** + * REST URL to use writing Spans. + */ + private final String writeSpansRESTURL; + + /** + * Runs background task to do the REST PUT. + * TODO: Make period configurable. + */ + private final ScheduledExecutorService scheduler; + + /** + * Keep around reference so can cancel any running scheduled task. + */ + private final ScheduledFuture scheduledFuture; + + /** + * Simple queue to hold spans between periodic runs of the httpclient. + * This queue is unbounded but it gets serviced and elements drained + * on a period. The draining thread, even if it dies, ScheduledExecutorService + * launches a new one (as I read it -- TODO: test). + */ + private final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); + + /** + * Builder class. Get instance by calling {@link HTracedRESTReceiver#builder()}. + */ + public static class Builder { + private Duration timeout = new Duration(10000); + private String hostname = "localhost"; + /** + * Default port for htraced. + */ + private int port = 9095; + + private Builder() {} + + public Builder hostname(final String hostname) { + this.hostname = hostname; + return this; + } + + public Builder port(final int port) { + this.port = port; + return this; + } + + /** + * @param timeout Connection setup and request timeouts. + * @return This + */ + public Builder timeout(final long timeout) { + this.timeout = new Duration(timeout); + return this; + } + + public HTracedRESTReceiver build() throws Exception { + return new HTracedRESTReceiver(this); + } + } + + private HTracedRESTReceiver(Builder builder) throws Exception { + this.httpClient = new HttpClient(); + this.httpClient.setUserAgentField(new HttpField(HttpHeader.USER_AGENT, + this.getClass().getSimpleName())); + this.httpClient.setConnectTimeout(builder.timeout.getMillis()); + // Use same timeout for connection and idle for now. + this.httpClient.setIdleTimeout(builder.timeout.getMillis()); + this.httpClient.start(); + // Build up the writeSpans URL. + this.writeSpansRESTURL = "http://" + builder.hostname + ":" + builder.port + "/writeSpans"; + // Make a scheduler with one thread to run our POST of spans on a period. + this.scheduler = Executors.newScheduledThreadPool(1); + this.scheduledFuture = + this.scheduler.scheduleAtFixedRate(new PostSpans(this.queue), 1, 1, TimeUnit.SECONDS); + } + + /** + * Post spans runnable. + */ + private class PostSpans implements Runnable { + private final Queue q; + + PostSpans(final Queue q) { + this.q = q; + } + + @Override + public void run() { + Span span = this.q.poll(); + if (span == null) return; + // We got a span. Send at least this one span. + Request request = httpClient.newRequest(writeSpansRESTURL).method(HttpMethod.POST); + request.header(HttpHeader.CONTENT_TYPE, "application/json"); + int count = 1; + request.content(new StringContentProvider(span.toJson())); + // Drain queue of spans if more than just one. + while ((span = this.q.poll()) != null) { + request.content(new StringContentProvider(span.toJson())); + count++; + } + try { + ContentResponse response = request.send(); + if (response.getStatus() == HttpStatus.OK_200) { + LOG.info("POSTED " + count + " spans"); + } else { + LOG.error(response.getStatus()); + LOG.error(response.getHeaders()); + LOG.error(response.getContentAsString()); + } + } catch (InterruptedException e) { + LOG.error(e); + } catch (TimeoutException e) { + LOG.error(e); + } catch (ExecutionException e) { + LOG.error(e); + } + } + } + + /** + * @return New builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + @Override + public void close() throws IOException { + if (this.scheduledFuture != null) this.scheduledFuture.cancel(true); + if (this.scheduler == null) this.scheduler.shutdown(); + if (this.httpClient != null) { + try { + this.httpClient.stop(); + } catch (Exception e) { + throw new IOException(e); + } + } + } + + @Override + public void receiveSpan(Span span) { + this.queue.add(span); + } + + /** + * Exercise our little span. + * @param args + * @throws Exception + */ + public static void main(String[] args) throws Exception { + HTracedRESTReceiver receiver = + HTracedRESTReceiver.builder().build(); + try { + // Do basic a GET /server/info against localhost:9095 htraced + ContentResponse response = receiver.httpClient.GET("http://localhost:9095/server/info"); + System.out.println(response.getMediaType()); + System.out.println(response.getContentAsString()); + // TODO: Fix MilliSpan. Requires a parentid. Shouldn't have to have one. + for (int i = 0; i < 100; i++) { + Span span = new MilliSpan.Builder().parents(new long [] {1L}).build(); + receiver.receiveSpan(span); + Thread.sleep(100); + } + } finally { + receiver.close(); + } + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index bf85272..38ae4b3 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ language governing permissions and limitations under the License. --> htrace-zipkin htrace-hbase htrace-flume + htrace-htraced @@ -209,16 +210,16 @@ language governing permissions and limitations under the License. --> + + org.apache.maven.plugins + maven-shade-plugin + 2.1 + org.apache.maven.plugins - maven-shade-plugin - 2.1 - - - org.apache.maven.plugins maven-gpg-plugin @@ -306,6 +307,16 @@ language governing permissions and limitations under the License. --> 4.10 test + + com.fasterxml.jackson.core + jackson-core + 2.4.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.4.0 +