Merge pull request #60 from rlinehan/TK-402-configurable-metric-namespace

(TK-402) Allow metric namespace to be configurable
This commit is contained in:
Chris Price 2016-10-04 14:22:30 -07:00 committed by GitHub
commit c038015730
19 changed files with 306 additions and 57 deletions

View file

@ -38,6 +38,13 @@ The following are the base set of options supported by the `create-client` funct
to. If provided, metrics will automatically be registered for all requests to. If provided, metrics will automatically be registered for all requests
made by the client. See the [metrics documentation](./metrics.md) for more made by the client. See the [metrics documentation](./metrics.md) for more
info. info.
* `:server-id`: a string for the name of the server the request is being made
from. If specified, used in the namespace for metrics:
`puppetlabs.<server-id>.http-client.experimental`.
* `:metric-prefix`: a string for the prefix for metrics. If specified, metric
namespace is `<metric-prefix>.http-client.experimental`. If both
`metric-prefix` and `server-id` are specified, `metric-prefix` takes
precendence.
### SSL Options ### SSL Options

View file

@ -10,7 +10,7 @@ change.
For using metrics with either the Java client or the Clojure client you must For using metrics with either the Java client or the Clojure client you must
already have created a Dropwizard `MetricRegistry`. already have created a Dropwizard `MetricRegistry`.
- [Metrics prefix](#metrics-prefix) - [Metric namespace](#metric-namespace)
- [Types of metrics](#types-of-metrics) - [Types of metrics](#types-of-metrics)
- [Getting back metrics](#getting-back-metrics) - [Getting back metrics](#getting-back-metrics)
- [Clojure API](#clojure-api) - [Clojure API](#clojure-api)
@ -29,9 +29,26 @@ already have created a Dropwizard `MetricRegistry`.
- [Filtering by metric-id](#filtering-by-metric-id-1) - [Filtering by metric-id](#filtering-by-metric-id-1)
## Metrics prefix ## Metric namespace
All http metrics are prefixed with `puppetlabs.http-client.experimental`. By default, http metrics are prefixed with the namespace
`puppetlabs.http-client.experimental`. This namespace can be customized with two
client options, `server-id` and `metric-prefix`.
When `server-id` is set, the metric namespace becomes
`puppetlabs.<server-id>.http-client.experimental`.
When `metric-prefix` is set, the metric namespace becomes
`<metric-prefix>.http-client.experimental`.
If both `server-id` and `metric-prefix` are set, `metric-prefix` wins out and
a warning message is logged.
For a Clojure client, the `get-client-metric-namespace` protocol method can
be used to get back the metric namespace set for the client.
For a Java client, the `getMetricNamespace` method can be used to get back the
configured metric namespace.
## Types of metrics ## Types of metrics

View file

@ -1,5 +1,5 @@
(def ks-version "1.2.0") (def ks-version "1.3.0")
(def tk-version "1.1.1") (def tk-version "1.5.1")
(def tk-jetty-version "1.5.0") (def tk-jetty-version "1.5.0")
(defproject puppetlabs/http-client "0.5.1-SNAPSHOT" (defproject puppetlabs/http-client "0.5.1-SNAPSHOT"
@ -17,7 +17,7 @@
[org.apache.httpcomponents/httpasyncclient "4.1.2"] [org.apache.httpcomponents/httpasyncclient "4.1.2"]
[org.apache.commons/commons-lang3 "3.4"] [org.apache.commons/commons-lang3 "3.4"]
[prismatic/schema "1.0.4"] [prismatic/schema "1.0.4"]
[org.slf4j/slf4j-api "1.7.13"] [org.slf4j/slf4j-api "1.7.20"]
[commons-io "2.4"] [commons-io "2.4"]
[io.dropwizard.metrics/metrics-core "3.1.2"] [io.dropwizard.metrics/metrics-core "3.1.2"]

View file

@ -16,7 +16,8 @@
(org.apache.http.nio.client HttpAsyncClient) (org.apache.http.nio.client HttpAsyncClient)
(com.codahale.metrics MetricRegistry)) (com.codahale.metrics MetricRegistry))
(:require [puppetlabs.http.client.common :as common] (:require [puppetlabs.http.client.common :as common]
[schema.core :as schema]) [schema.core :as schema]
[puppetlabs.http.client.metrics :as metrics])
(:refer-clojure :exclude (get))) (:refer-clojure :exclude (get)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -25,7 +26,8 @@
(schema/defn ^:always-validate create-default-client :- HttpAsyncClient (schema/defn ^:always-validate create-default-client :- HttpAsyncClient
[{:keys [ssl-context ssl-ca-cert ssl-cert ssl-key ssl-protocols cipher-suites [{:keys [ssl-context ssl-ca-cert ssl-cert ssl-key ssl-protocols cipher-suites
follow-redirects force-redirects connect-timeout-milliseconds follow-redirects force-redirects connect-timeout-milliseconds
socket-timeout-milliseconds metric-registry]}:- common/ClientOptions] socket-timeout-milliseconds metric-registry server-id
metric-prefix]}:- common/ClientOptions]
(let [client-options (ClientOptions.)] (let [client-options (ClientOptions.)]
(cond-> client-options (cond-> client-options
(some? ssl-context) (.setSslContext ssl-context) (some? ssl-context) (.setSslContext ssl-context)
@ -40,7 +42,9 @@
(.setConnectTimeoutMilliseconds connect-timeout-milliseconds) (.setConnectTimeoutMilliseconds connect-timeout-milliseconds)
(some? socket-timeout-milliseconds) (some? socket-timeout-milliseconds)
(.setSocketTimeoutMilliseconds socket-timeout-milliseconds) (.setSocketTimeoutMilliseconds socket-timeout-milliseconds)
(some? metric-registry) (.setMetricRegistry metric-registry)) (some? metric-registry) (.setMetricRegistry metric-registry)
(some? server-id) (.setServerId server-id)
(some? metric-prefix) (.setMetricPrefix metric-prefix))
(JavaClient/createClient client-options))) (JavaClient/createClient client-options)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -161,11 +165,12 @@
([opts :- common/RawUserRequestOptions ([opts :- common/RawUserRequestOptions
callback :- common/ResponseCallbackFn callback :- common/ResponseCallbackFn
client :- HttpAsyncClient] client :- HttpAsyncClient]
(request-with-client opts callback client nil)) (request-with-client opts callback client nil nil))
([opts :- common/RawUserRequestOptions ([opts :- common/RawUserRequestOptions
callback :- common/ResponseCallbackFn callback :- common/ResponseCallbackFn
client :- HttpAsyncClient client :- HttpAsyncClient
metric-registry :- (schema/maybe MetricRegistry)] metric-registry :- (schema/maybe MetricRegistry)
metric-namespace :- (schema/maybe schema/Str)]
(let [result (promise) (let [result (promise)
defaults {:headers {} defaults {:headers {}
:body nil :body nil
@ -176,7 +181,8 @@
java-method (clojure-method->java opts) java-method (clojure-method->java opts)
response-delivery-delegate (get-response-delivery-delegate opts result)] response-delivery-delegate (get-response-delivery-delegate opts result)]
(JavaClient/requestWithClient java-request-options java-method callback (JavaClient/requestWithClient java-request-options java-method callback
client response-delivery-delegate metric-registry) client response-delivery-delegate metric-registry
metric-namespace)
result))) result)))
(schema/defn create-client :- (schema/protocol common/HTTPClient) (schema/defn create-client :- (schema/protocol common/HTTPClient)
@ -223,7 +229,8 @@
* :ssl-ca-cert - path to a PEM file containing the CA cert" * :ssl-ca-cert - path to a PEM file containing the CA cert"
[opts :- common/ClientOptions] [opts :- common/ClientOptions]
(let [client (create-default-client opts) (let [client (create-default-client opts)
metric-registry (:metric-registry opts)] metric-registry (:metric-registry opts)
metric-namespace (metrics/build-metric-namespace (:metric-prefix opts) (:server-id opts))]
(reify common/HTTPClient (reify common/HTTPClient
(get [this url] (common/get this url {})) (get [this url] (common/get this url {}))
(get [this url opts] (common/make-request this url :get opts)) (get [this url opts] (common/make-request this url :get opts))
@ -244,6 +251,8 @@
(make-request [this url method] (common/make-request this url method {})) (make-request [this url method] (common/make-request this url method {}))
(make-request [_ url method opts] (request-with-client (make-request [_ url method opts] (request-with-client
(assoc opts :method method :url url) (assoc opts :method method :url url)
nil client metric-registry)) nil client metric-registry
metric-namespace))
(close [_] (.close client)) (close [_] (.close client))
(get-client-metric-registry [_] metric-registry)))) (get-client-metric-registry [_] metric-registry)
(get-client-metric-namespace [_] metric-namespace))))

View file

@ -23,7 +23,8 @@
(patch [this url] [this url opts]) (patch [this url] [this url opts])
(make-request [this url method] [this url method opts]) (make-request [this url method] [this url method opts])
(close [this]) (close [this])
(get-client-metric-registry [this])) (get-client-metric-registry [this])
(get-client-metric-namespace [this]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Schemas ;;; Schemas
@ -113,7 +114,9 @@
(ok :follow-redirects) schema/Bool (ok :follow-redirects) schema/Bool
(ok :connect-timeout-milliseconds) schema/Int (ok :connect-timeout-milliseconds) schema/Int
(ok :socket-timeout-milliseconds) schema/Int (ok :socket-timeout-milliseconds) schema/Int
(ok :metric-registry) MetricRegistry}) (ok :metric-registry) MetricRegistry
(ok :server-id) schema/Str
(ok :metric-prefix) schema/Str})
(def UserRequestOptions (def UserRequestOptions
"A cleaned-up version of RawUserRequestClientOptions, which is formed after "A cleaned-up version of RawUserRequestClientOptions, which is formed after

View file

@ -2,7 +2,7 @@
(:require [schema.core :as schema] (:require [schema.core :as schema]
[puppetlabs.http.client.common :as common]) [puppetlabs.http.client.common :as common])
(:import (com.puppetlabs.http.client.metrics Metrics$MetricType Metrics (:import (com.puppetlabs.http.client.metrics Metrics$MetricType Metrics
ClientMetricData) ClientMetricData)
(com.codahale.metrics MetricRegistry))) (com.codahale.metrics MetricRegistry)))
(schema/defn get-base-metric-data :- common/BaseMetricData (schema/defn get-base-metric-data :- common/BaseMetricData
@ -33,6 +33,10 @@
[method] [method]
(clojure.string/upper-case (name method))) (clojure.string/upper-case (name method)))
(defn build-metric-namespace
[metric-prefix server-id]
(Metrics/buildMetricNamespace metric-prefix server-id))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Public ;;; Public

View file

@ -4,7 +4,8 @@
(ns puppetlabs.http.client.sync (ns puppetlabs.http.client.sync
(:require [puppetlabs.http.client.async :as async] (:require [puppetlabs.http.client.async :as async]
[schema.core :as schema] [schema.core :as schema]
[puppetlabs.http.client.common :as common]) [puppetlabs.http.client.common :as common]
[puppetlabs.http.client.metrics :as metrics])
(:refer-clojure :exclude (get))) (:refer-clojure :exclude (get)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -24,10 +25,10 @@
(defn request-with-client (defn request-with-client
([req client] ([req client]
(request-with-client req client nil)) (request-with-client req client nil nil))
([req client metric-registry] ([req client metric-registry metric-namespace]
(let [{:keys [error] :as resp} @(async/request-with-client (let [{:keys [error] :as resp} @(async/request-with-client
req nil client metric-registry)] req nil client metric-registry metric-namespace)]
(if error (if error
(throw error) (throw error)
resp)))) resp))))
@ -37,12 +38,13 @@
(defn request (defn request
[req] [req]
(with-open [client (async/create-default-client (extract-client-opts req))] (with-open [client (async/create-default-client (extract-client-opts req))]
(request-with-client (extract-request-opts req) client nil))) (request-with-client (extract-request-opts req) client)))
(schema/defn create-client :- (schema/protocol common/HTTPClient) (schema/defn create-client :- (schema/protocol common/HTTPClient)
[opts :- common/ClientOptions] [opts :- common/ClientOptions]
(let [client (async/create-default-client opts) (let [client (async/create-default-client opts)
metric-registry (:metric-registry opts)] metric-registry (:metric-registry opts)
metric-namespace (metrics/build-metric-namespace (:metric-prefix opts) (:server-id opts))]
(reify common/HTTPClient (reify common/HTTPClient
(get [this url] (common/get this url {})) (get [this url] (common/get this url {}))
(get [this url opts] (common/make-request this url :get opts)) (get [this url opts] (common/make-request this url :get opts))
@ -63,9 +65,10 @@
(make-request [this url method] (common/make-request this url method {})) (make-request [this url method] (common/make-request this url method {}))
(make-request [_ url method opts] (request-with-client (make-request [_ url method opts] (request-with-client
(assoc opts :method method :url url) (assoc opts :method method :url url)
client metric-registry)) client metric-registry metric-namespace))
(close [_] (.close client)) (close [_] (.close client))
(get-client-metric-registry [_] metric-registry)))) (get-client-metric-registry [_] metric-registry)
(get-client-metric-namespace [_] metric-namespace))))
(defn get (defn get
"Issue a synchronous HTTP GET request. This will raise an exception if an "Issue a synchronous HTTP GET request. This will raise an exception if an

View file

@ -1,10 +1,8 @@
package com.puppetlabs.http.client; package com.puppetlabs.http.client;
import com.puppetlabs.http.client.impl.SslUtils;
import com.puppetlabs.http.client.impl.JavaClient; import com.puppetlabs.http.client.impl.JavaClient;
import com.puppetlabs.http.client.impl.PersistentAsyncHttpClient; import com.puppetlabs.http.client.impl.PersistentAsyncHttpClient;
import com.puppetlabs.http.client.impl.CoercedClientOptions; import com.puppetlabs.http.client.metrics.Metrics;
import com.codahale.metrics.MetricRegistry;
/** /**
* This class allows you to create an AsyncHttpClient for use in making * This class allows you to create an AsyncHttpClient for use in making
@ -21,7 +19,9 @@ public class Async {
* @return an AsyncHttpClient that can be used to make requests * @return an AsyncHttpClient that can be used to make requests
*/ */
public static AsyncHttpClient createClient(ClientOptions clientOptions) { public static AsyncHttpClient createClient(ClientOptions clientOptions) {
final String metricNamespace = Metrics.buildMetricNamespace(clientOptions.getMetricPrefix(),
clientOptions.getServerId());
return new PersistentAsyncHttpClient(JavaClient.createClient(clientOptions), return new PersistentAsyncHttpClient(JavaClient.createClient(clientOptions),
clientOptions.getMetricRegistry()); clientOptions.getMetricRegistry(),metricNamespace);
} }
} }

View file

@ -19,6 +19,11 @@ public interface AsyncHttpClient extends Closeable{
*/ */
public MetricRegistry getMetricRegistry(); public MetricRegistry getMetricRegistry();
/**
* @return the String metricNamespace for this Client
*/
public String getMetricNamespace();
/** /**
* Performs a GET request * Performs a GET request
* @param url the URL against which to make the GET request * @param url the URL against which to make the GET request

View file

@ -27,6 +27,8 @@ public class ClientOptions {
private int connectTimeoutMilliseconds = -1; private int connectTimeoutMilliseconds = -1;
private int socketTimeoutMilliseconds = -1; private int socketTimeoutMilliseconds = -1;
private MetricRegistry metricRegistry; private MetricRegistry metricRegistry;
private String metricPrefix;
private String serverId;
/** /**
* Constructor for the ClientOptions class. When this constructor is called, * Constructor for the ClientOptions class. When this constructor is called,
@ -183,4 +185,22 @@ public class ClientOptions {
this.metricRegistry = metricRegistry; this.metricRegistry = metricRegistry;
return this; return this;
} }
public String getMetricPrefix() {
return metricPrefix;
}
public ClientOptions setMetricPrefix(String metricPrefix) {
this.metricPrefix = metricPrefix;
return this;
}
public String getServerId() {
return serverId;
}
public ClientOptions setServerId(String serverId) {
this.serverId = serverId;
return this;
}
} }

View file

@ -2,6 +2,7 @@ package com.puppetlabs.http.client;
import com.puppetlabs.http.client.impl.JavaClient; import com.puppetlabs.http.client.impl.JavaClient;
import com.puppetlabs.http.client.impl.PersistentSyncHttpClient; import com.puppetlabs.http.client.impl.PersistentSyncHttpClient;
import com.puppetlabs.http.client.metrics.Metrics;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -84,8 +85,10 @@ public class Sync {
* @return A persistent synchronous HTTP client * @return A persistent synchronous HTTP client
*/ */
public static SyncHttpClient createClient(ClientOptions clientOptions) { public static SyncHttpClient createClient(ClientOptions clientOptions) {
final String metricNamespace = Metrics.buildMetricNamespace(clientOptions.getMetricPrefix(),
clientOptions.getServerId());
return new PersistentSyncHttpClient(JavaClient.createClient(clientOptions), return new PersistentSyncHttpClient(JavaClient.createClient(clientOptions),
clientOptions.getMetricRegistry()); clientOptions.getMetricRegistry(), metricNamespace);
} }
/** /**

View file

@ -18,6 +18,11 @@ public interface SyncHttpClient extends Closeable {
*/ */
public MetricRegistry getMetricRegistry(); public MetricRegistry getMetricRegistry();
/**
* @return the String metricNamespace for this Client
*/
public String getMetricNamespace();
/** /**
* Makes a configurable HTTP request * Makes a configurable HTTP request
* @param requestOptions the options to configure the request * @param requestOptions the options to configure the request

View file

@ -263,7 +263,8 @@ public class JavaClient {
final FutureCallback<HttpResponse> futureCallback, final FutureCallback<HttpResponse> futureCallback,
final HttpRequestBase request, final HttpRequestBase request,
final MetricRegistry metricRegistry, final MetricRegistry metricRegistry,
final String[] metricId) { final String[] metricId,
final String metricNamespace) {
/* /*
* Create an Apache AsyncResponseConsumer that will return the response to us as soon as it is available, * Create an Apache AsyncResponseConsumer that will return the response to us as soon as it is available,
@ -315,7 +316,7 @@ public class JavaClient {
TimedFutureCallback<HttpResponse> timedStreamingCompleteCallback = TimedFutureCallback<HttpResponse> timedStreamingCompleteCallback =
new TimedFutureCallback<>(streamingCompleteCallback, new TimedFutureCallback<>(streamingCompleteCallback,
TimerUtils.startFullResponseTimers(metricRegistry, request, metricId)); TimerUtils.startFullResponseTimers(metricRegistry, request, metricId, metricNamespace));
client.execute(HttpAsyncMethods.create(request), consumer, timedStreamingCompleteCallback); client.execute(HttpAsyncMethods.create(request), consumer, timedStreamingCompleteCallback);
} }
@ -324,7 +325,8 @@ public class JavaClient {
final IResponseCallback callback, final IResponseCallback callback,
final CloseableHttpAsyncClient client, final CloseableHttpAsyncClient client,
final ResponseDeliveryDelegate responseDeliveryDelegate, final ResponseDeliveryDelegate responseDeliveryDelegate,
final MetricRegistry registry) { final MetricRegistry registry,
final String metricNamespace) {
final CoercedRequestOptions coercedRequestOptions = coerceRequestOptions(requestOptions, method); final CoercedRequestOptions coercedRequestOptions = coerceRequestOptions(requestOptions, method);
@ -355,11 +357,11 @@ public class JavaClient {
final String[] metricId = requestOptions.getMetricId(); final String[] metricId = requestOptions.getMetricId();
if (requestOptions.getAs() == ResponseBodyType.UNBUFFERED_STREAM) { if (requestOptions.getAs() == ResponseBodyType.UNBUFFERED_STREAM) {
executeWithConsumer(client, futureCallback, request, registry, metricId); executeWithConsumer(client, futureCallback, request, registry, metricId, metricNamespace);
} else { } else {
TimedFutureCallback<HttpResponse> timedFutureCallback = TimedFutureCallback<HttpResponse> timedFutureCallback =
new TimedFutureCallback<>(futureCallback, new TimedFutureCallback<>(futureCallback,
TimerUtils.startFullResponseTimers(registry, request, metricId)); TimerUtils.startFullResponseTimers(registry, request, metricId, metricNamespace));
client.execute(request, timedFutureCallback); client.execute(request, timedFutureCallback);
} }
} }

View file

@ -14,11 +14,14 @@ import java.net.URISyntaxException;
public class PersistentAsyncHttpClient implements AsyncHttpClient { public class PersistentAsyncHttpClient implements AsyncHttpClient {
private CloseableHttpAsyncClient client; private CloseableHttpAsyncClient client;
private MetricRegistry metricRegistry; private MetricRegistry metricRegistry;
private String metricNamespace;
public PersistentAsyncHttpClient(CloseableHttpAsyncClient client, public PersistentAsyncHttpClient(CloseableHttpAsyncClient client,
MetricRegistry metricRegistry) { MetricRegistry metricRegistry,
String metricNamespace) {
this.client = client; this.client = client;
this.metricRegistry = metricRegistry; this.metricRegistry = metricRegistry;
this.metricNamespace = metricNamespace;
} }
public void close() throws IOException { public void close() throws IOException {
@ -29,11 +32,15 @@ public class PersistentAsyncHttpClient implements AsyncHttpClient {
return metricRegistry; return metricRegistry;
} }
public String getMetricNamespace() {
return metricNamespace;
}
private Promise<Response> request(RequestOptions requestOptions, HttpMethod method) { private Promise<Response> request(RequestOptions requestOptions, HttpMethod method) {
final Promise<Response> promise = new Promise<>(); final Promise<Response> promise = new Promise<>();
final JavaResponseDeliveryDelegate responseDelivery = new JavaResponseDeliveryDelegate(promise); final JavaResponseDeliveryDelegate responseDelivery = new JavaResponseDeliveryDelegate(promise);
JavaClient.requestWithClient(requestOptions, method, null, JavaClient.requestWithClient(requestOptions, method, null,
client, responseDelivery, metricRegistry); client, responseDelivery, metricRegistry, metricNamespace);
return promise; return promise;
} }

View file

@ -17,12 +17,15 @@ import java.net.URISyntaxException;
public class PersistentSyncHttpClient implements SyncHttpClient { public class PersistentSyncHttpClient implements SyncHttpClient {
private CloseableHttpAsyncClient client; private CloseableHttpAsyncClient client;
private MetricRegistry metricRegistry; private MetricRegistry metricRegistry;
private String metricNamespace;
private static final Logger LOGGER = LoggerFactory.getLogger(PersistentSyncHttpClient.class); private static final Logger LOGGER = LoggerFactory.getLogger(PersistentSyncHttpClient.class);
public PersistentSyncHttpClient(CloseableHttpAsyncClient client, public PersistentSyncHttpClient(CloseableHttpAsyncClient client,
MetricRegistry metricRegistry) { MetricRegistry metricRegistry,
String metricNamespace) {
this.client = client; this.client = client;
this.metricRegistry = metricRegistry; this.metricRegistry = metricRegistry;
this.metricNamespace = metricNamespace;
} }
private static void logAndRethrow(String msg, Throwable t) { private static void logAndRethrow(String msg, Throwable t) {
@ -34,11 +37,15 @@ public class PersistentSyncHttpClient implements SyncHttpClient {
return metricRegistry; return metricRegistry;
} }
public String getMetricNamespace() {
return metricNamespace;
}
public Response request(RequestOptions requestOptions, HttpMethod method) { public Response request(RequestOptions requestOptions, HttpMethod method) {
final Promise<Response> promise = new Promise<>(); final Promise<Response> promise = new Promise<>();
final JavaResponseDeliveryDelegate responseDelivery = new JavaResponseDeliveryDelegate(promise); final JavaResponseDeliveryDelegate responseDelivery = new JavaResponseDeliveryDelegate(promise);
JavaClient.requestWithClient(requestOptions, method, null, client, JavaClient.requestWithClient(requestOptions, method, null, client,
responseDelivery, metricRegistry); responseDelivery, metricRegistry, metricNamespace);
Response response = null; Response response = null;
try { try {
response = promise.deref(); response = promise.deref();

View file

@ -41,7 +41,8 @@ public class TimerUtils {
} }
private static ArrayList<Timer.Context> startFullResponseMetricIdTimers(MetricRegistry registry, private static ArrayList<Timer.Context> startFullResponseMetricIdTimers(MetricRegistry registry,
String[] metricId) { String[] metricId,
String metricPrefix) {
ArrayList<Timer.Context> timerContexts = new ArrayList<>(); ArrayList<Timer.Context> timerContexts = new ArrayList<>();
for (int i = 0; i < metricId.length; i++) { for (int i = 0; i < metricId.length; i++) {
ArrayList<String> currentId = new ArrayList<>(); ArrayList<String> currentId = new ArrayList<>();
@ -52,7 +53,7 @@ public class TimerUtils {
currentIdWithNamespace.add(Metrics.NAMESPACE_METRIC_ID); currentIdWithNamespace.add(Metrics.NAMESPACE_METRIC_ID);
currentIdWithNamespace.addAll(currentId); currentIdWithNamespace.addAll(currentId);
currentIdWithNamespace.add(Metrics.NAMESPACE_FULL_RESPONSE); currentIdWithNamespace.add(Metrics.NAMESPACE_FULL_RESPONSE);
String metric_name = MetricRegistry.name(Metrics.NAMESPACE_PREFIX, String metric_name = MetricRegistry.name(metricPrefix,
currentIdWithNamespace.toArray(new String[currentIdWithNamespace.size()])); currentIdWithNamespace.toArray(new String[currentIdWithNamespace.size()]));
ClientTimer timer = new MetricIdClientTimer(metric_name, currentId, Metrics.MetricType.FULL_RESPONSE); ClientTimer timer = new MetricIdClientTimer(metric_name, currentId, Metrics.MetricType.FULL_RESPONSE);
@ -62,16 +63,17 @@ public class TimerUtils {
} }
private static ArrayList<Timer.Context> startFullResponseUrlTimers(MetricRegistry registry, private static ArrayList<Timer.Context> startFullResponseUrlTimers(MetricRegistry registry,
HttpRequest request) { HttpRequest request,
String metricPrefix) {
ArrayList<Timer.Context> timerContexts = new ArrayList<>(); ArrayList<Timer.Context> timerContexts = new ArrayList<>();
try { try {
final RequestLine requestLine = request.getRequestLine(); final RequestLine requestLine = request.getRequestLine();
final String strippedUrl = Metrics.urlToMetricUrl(requestLine.getUri()); final String strippedUrl = Metrics.urlToMetricUrl(requestLine.getUri());
final String method = requestLine.getMethod(); final String method = requestLine.getMethod();
final String urlName = MetricRegistry.name(Metrics.NAMESPACE_PREFIX, Metrics.NAMESPACE_URL, final String urlName = MetricRegistry.name(metricPrefix, Metrics.NAMESPACE_URL,
strippedUrl, Metrics.NAMESPACE_FULL_RESPONSE); strippedUrl, Metrics.NAMESPACE_FULL_RESPONSE);
final String urlAndMethodName = MetricRegistry.name(Metrics.NAMESPACE_PREFIX, Metrics.NAMESPACE_URL_AND_METHOD, final String urlAndMethodName = MetricRegistry.name(metricPrefix, Metrics.NAMESPACE_URL_AND_METHOD,
strippedUrl, method, Metrics.NAMESPACE_FULL_RESPONSE); strippedUrl, method, Metrics.NAMESPACE_FULL_RESPONSE);
ClientTimer urlTimer = new UrlClientTimer(urlName, strippedUrl, Metrics.MetricType.FULL_RESPONSE); ClientTimer urlTimer = new UrlClientTimer(urlName, strippedUrl, Metrics.MetricType.FULL_RESPONSE);
@ -91,13 +93,14 @@ public class TimerUtils {
public static ArrayList<Timer.Context> startFullResponseTimers(MetricRegistry clientRegistry, public static ArrayList<Timer.Context> startFullResponseTimers(MetricRegistry clientRegistry,
HttpRequest request, HttpRequest request,
String[] metricId) { String[] metricId,
String metricNamespace) {
if (clientRegistry != null) { if (clientRegistry != null) {
ArrayList<Timer.Context> urlTimerContexts = startFullResponseUrlTimers(clientRegistry,request); ArrayList<Timer.Context> urlTimerContexts = startFullResponseUrlTimers(clientRegistry, request, metricNamespace);
ArrayList<Timer.Context> allTimerContexts = new ArrayList<>(urlTimerContexts); ArrayList<Timer.Context> allTimerContexts = new ArrayList<>(urlTimerContexts);
if (metricId != null) { if (metricId != null) {
ArrayList<Timer.Context> metricIdTimers = ArrayList<Timer.Context> metricIdTimers =
startFullResponseMetricIdTimers(clientRegistry, metricId); startFullResponseMetricIdTimers(clientRegistry, metricId, metricNamespace);
allTimerContexts.addAll(metricIdTimers); allTimerContexts.addAll(metricIdTimers);
} }
return allTimerContexts; return allTimerContexts;

View file

@ -8,6 +8,8 @@ import com.puppetlabs.http.client.impl.metrics.MetricIdClientTimerFilter;
import com.puppetlabs.http.client.impl.metrics.TimerMetricData; import com.puppetlabs.http.client.impl.metrics.TimerMetricData;
import com.puppetlabs.http.client.impl.metrics.UrlAndMethodClientTimerFilter; import com.puppetlabs.http.client.impl.metrics.UrlAndMethodClientTimerFilter;
import com.puppetlabs.http.client.impl.metrics.UrlClientTimerFilter; import com.puppetlabs.http.client.impl.metrics.UrlClientTimerFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@ -17,11 +19,31 @@ import java.util.List;
import java.util.Map; import java.util.Map;
public class Metrics { public class Metrics {
public static final String NAMESPACE_PREFIX = "puppetlabs.http-client.experimental"; public static final String PUPPETLABS_NAMESPACE_PREFIX = "puppetlabs";
public static final String HTTP_CLIENT_NAMESPACE_PREFIX = "http-client.experimental";
public static final String DEFAULT_NAMESPACE_PREFIX = PUPPETLABS_NAMESPACE_PREFIX +
"." + HTTP_CLIENT_NAMESPACE_PREFIX;
public static final String NAMESPACE_URL = "with-url"; public static final String NAMESPACE_URL = "with-url";
public static final String NAMESPACE_URL_AND_METHOD = "with-url-and-method"; public static final String NAMESPACE_URL_AND_METHOD = "with-url-and-method";
public static final String NAMESPACE_METRIC_ID = "with-metric-id"; public static final String NAMESPACE_METRIC_ID = "with-metric-id";
public static final String NAMESPACE_FULL_RESPONSE = "full-response"; public static final String NAMESPACE_FULL_RESPONSE = "full-response";
private static final Logger LOGGER = LoggerFactory.getLogger(Metrics.class);
public static String buildMetricNamespace(String metricPrefix, String serverId) {
if (metricPrefix != null) {
if (serverId != null) {
Metrics.LOGGER.warn("Metric prefix and server id both set. Using metric prefix '"
+ metricPrefix + "' for metric namespace.");
}
return metricPrefix + "." + HTTP_CLIENT_NAMESPACE_PREFIX;
} else if (serverId != null) {
return PUPPETLABS_NAMESPACE_PREFIX + "." + serverId + "."
+ HTTP_CLIENT_NAMESPACE_PREFIX;
} else {
return DEFAULT_NAMESPACE_PREFIX;
}
}
public enum MetricType { FULL_RESPONSE } public enum MetricType { FULL_RESPONSE }
public enum MetricCategory { URL, URL_AND_METHOD, METRIC_ID } public enum MetricCategory { URL, URL_AND_METHOD, METRIC_ID }

View file

@ -22,19 +22,22 @@
(let [metric-registry (MetricRegistry.)] (let [metric-registry (MetricRegistry.)]
(TimerUtils/startFullResponseTimers metric-registry (TimerUtils/startFullResponseTimers metric-registry
(BasicHttpRequest. "GET" "http://localhost/foo") (BasicHttpRequest. "GET" "http://localhost/foo")
nil) nil
Metrics/DEFAULT_NAMESPACE_PREFIX)
(is (= (set (list url-id url-method-id)) (set (keys (.getTimers metric-registry))))))) (is (= (set (list url-id url-method-id)) (set (keys (.getTimers metric-registry)))))))
(testing "metric id timers are not created for a request with an empty metric id" (testing "metric id timers are not created for a request with an empty metric id"
(let [metric-registry (MetricRegistry.)] (let [metric-registry (MetricRegistry.)]
(TimerUtils/startFullResponseTimers metric-registry (TimerUtils/startFullResponseTimers metric-registry
(BasicHttpRequest. "GET" "http://localhost/foo") (BasicHttpRequest. "GET" "http://localhost/foo")
(into-array String [])) (into-array String [])
Metrics/DEFAULT_NAMESPACE_PREFIX)
(is (= (set (list url-id url-method-id)) (set (keys (.getTimers metric-registry))))))) (is (= (set (list url-id url-method-id)) (set (keys (.getTimers metric-registry)))))))
(testing "metric id timers are created correctly for a request with a metric id" (testing "metric id timers are created correctly for a request with a metric id"
(let [metric-registry (MetricRegistry.)] (let [metric-registry (MetricRegistry.)]
(TimerUtils/startFullResponseTimers metric-registry (TimerUtils/startFullResponseTimers metric-registry
(BasicHttpRequest. "GET" "http://localhost/foo") (BasicHttpRequest. "GET" "http://localhost/foo")
(into-array ["foo" "bar" "baz"])) (into-array ["foo" "bar" "baz"])
Metrics/DEFAULT_NAMESPACE_PREFIX)
(is (= (set (list url-id url-method-id (is (= (set (list url-id url-method-id
(add-metric-ns "with-metric-id.foo.full-response") (add-metric-ns "with-metric-id.foo.full-response")
(add-metric-ns "with-metric-id.foo.bar.full-response") (add-metric-ns "with-metric-id.foo.bar.full-response")
@ -45,21 +48,25 @@
(TimerUtils/startFullResponseTimers (TimerUtils/startFullResponseTimers
metric-registry metric-registry
(BasicHttpRequest. "GET" "http://user:pwd@localhost:1234/foo%2cbar/baz?te%2cst=one") (BasicHttpRequest. "GET" "http://user:pwd@localhost:1234/foo%2cbar/baz?te%2cst=one")
nil) nil
Metrics/DEFAULT_NAMESPACE_PREFIX)
(TimerUtils/startFullResponseTimers (TimerUtils/startFullResponseTimers
metric-registry metric-registry
(BasicHttpRequest. "GET" "http://user:pwd@localhost:1234/foo%2cbar/baz#x%2cyz") (BasicHttpRequest. "GET" "http://user:pwd@localhost:1234/foo%2cbar/baz#x%2cyz")
nil) nil
Metrics/DEFAULT_NAMESPACE_PREFIX)
(TimerUtils/startFullResponseTimers (TimerUtils/startFullResponseTimers
metric-registry metric-registry
(BasicHttpRequest. (BasicHttpRequest.
"GET" "http://user:pwd@localhost:1234/foo%2cbar/baz?te%2cst=one#x%2cyz") "GET" "http://user:pwd@localhost:1234/foo%2cbar/baz?te%2cst=one#x%2cyz")
nil) nil
Metrics/DEFAULT_NAMESPACE_PREFIX)
(TimerUtils/startFullResponseTimers (TimerUtils/startFullResponseTimers
metric-registry metric-registry
(BasicHttpRequest. (BasicHttpRequest.
"GET" "http://user:pwd@localhost:1234/foo%2cbar/baz?#x%2cyz") "GET" "http://user:pwd@localhost:1234/foo%2cbar/baz?#x%2cyz")
nil) nil
Metrics/DEFAULT_NAMESPACE_PREFIX)
(is (= (set (list (is (= (set (list
(add-metric-ns (add-metric-ns
"with-url.http://localhost:1234/foo,bar/baz.full-response") "with-url.http://localhost:1234/foo,bar/baz.full-response")
@ -81,7 +88,8 @@
(doseq [timer (TimerUtils/startFullResponseTimers (doseq [timer (TimerUtils/startFullResponseTimers
registry registry
req req
id)] id
Metrics/DEFAULT_NAMESPACE_PREFIX)]
(.stop timer))) (.stop timer)))
(deftest get-client-metrics-data-test (deftest get-client-metrics-data-test

View file

@ -613,3 +613,127 @@
(catch TimeoutException e (catch TimeoutException e
;; Expected whenever a server-side failure is generated ;; Expected whenever a server-side failure is generated
)))))) ))))))
(deftest metric-namespace-test
(let [metric-prefix "my-metric-prefix"
server-id "my-server"
metric-name-with-prefix
(format "%s.http-client.experimental.with-url.%s.full-response" metric-prefix hello-url)
metric-name-with-server-id
(format "puppetlabs.%s.http-client.experimental.with-url.%s.full-response"
server-id hello-url)
get-metric-name (fn [metric-registry]
(.getMetricName (first (Metrics/getClientMetricsDataByUrl
metric-registry hello-url))))]
(testlogging/with-test-logging
(testutils/with-app-with-config
app
[jetty9/jetty9-service test-metric-web-service]
{:webserver {:port 10000}}
(testing "custom metric namespace works for java async client"
(testing "metric prefix works"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-metric-prefix (Async/createClient
(doto (ClientOptions.)
(.setMetricRegistry metric-registry)
(.setMetricPrefix metric-prefix)))]
(is (= (format "%s.http-client.experimental" metric-prefix)
(.getMetricNamespace client-with-metric-prefix)))
(-> client-with-metric-prefix (.get (RequestOptions. hello-url)))
(is (= metric-name-with-prefix (get-metric-name metric-registry))))))
(testing "server id works"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-server-id (Async/createClient
(doto (ClientOptions.)
(.setMetricRegistry metric-registry)
(.setServerId server-id)))]
(-> client-with-server-id (.get (RequestOptions. hello-url)))
(is (= metric-name-with-server-id (get-metric-name metric-registry))))))
(testing "metric prefix overrides server id if both are set"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-server-id (Async/createClient
(doto (ClientOptions.)
(.setMetricRegistry metric-registry)
(.setMetricPrefix metric-prefix)
(.setServerId server-id)))]
(-> client-with-server-id (.get (RequestOptions. hello-url)))
(is (= metric-name-with-prefix (get-metric-name metric-registry)))
(is (logged? #"Metric prefix and server id both set.*" :warn))))))
(testing "custom metric namespace works for clojure async client"
(testing "metric prefix works"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-metric-prefix (async/create-client
{:metric-registry metric-registry
:metric-prefix metric-prefix})]
(is (= (format "%s.http-client.experimental" metric-prefix)
(common/get-client-metric-namespace client-with-metric-prefix)))
(-> client-with-metric-prefix (common/get hello-url))
(is (= metric-name-with-prefix (get-metric-name metric-registry))))))
(testing "server id works"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-server-id (async/create-client
{:metric-registry metric-registry
:server-id server-id})]
(-> client-with-server-id (common/get hello-url))
(is (= metric-name-with-server-id (get-metric-name metric-registry))))))
(testing "metric prefix overrides server id if both are set"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-server-id (async/create-client
{:metric-registry metric-registry
:metric-prefix metric-prefix
:server-id server-id})]
(-> client-with-server-id (common/get hello-url))
(is (= metric-name-with-prefix (get-metric-name metric-registry)))))))
(testing "custom metric namespace works for Java sync client"
(testing "metric prefix works"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-metric-prefix (Sync/createClient
(doto (ClientOptions.)
(.setMetricRegistry metric-registry)
(.setMetricPrefix metric-prefix)))]
(is (= (format "%s.http-client.experimental" metric-prefix)
(.getMetricNamespace client-with-metric-prefix)))
(-> client-with-metric-prefix (.get (RequestOptions. hello-url)))
(is (= metric-name-with-prefix (get-metric-name metric-registry))))))
(testing "server id works"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-server-id (Sync/createClient
(doto (ClientOptions.)
(.setMetricRegistry metric-registry)
(.setServerId server-id)))]
(-> client-with-server-id (.get (RequestOptions. hello-url)))
(is (= metric-name-with-server-id (get-metric-name metric-registry))))))
(testing "metric prefix overrides server id if both are set"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-server-id (Sync/createClient
(doto (ClientOptions.)
(.setMetricRegistry metric-registry)
(.setMetricPrefix metric-prefix)
(.setServerId server-id)))]
(-> client-with-server-id (.get (RequestOptions. hello-url)))
(is (= metric-name-with-prefix (get-metric-name metric-registry)))))))
(testing "custom metric namespace works for clojure sync client"
(testing "metric prefix works"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-metric-prefix (sync/create-client
{:metric-registry metric-registry
:metric-prefix metric-prefix})]
(is (= (format "%s.http-client.experimental" metric-prefix)
(common/get-client-metric-namespace client-with-metric-prefix)))
(-> client-with-metric-prefix (common/get hello-url))
(is (= metric-name-with-prefix (get-metric-name metric-registry))))))
(testing "server id works"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-server-id (sync/create-client
{:metric-registry metric-registry
:server-id server-id})]
(-> client-with-server-id (common/get hello-url))
(is (= metric-name-with-server-id (get-metric-name metric-registry))))))
(testing "metric prefix overrides server id if both are set"
(let [metric-registry (MetricRegistry.)]
(with-open [client-with-server-id (sync/create-client
{:metric-registry metric-registry
:metric-prefix metric-prefix
:server-id server-id})]
(-> client-with-server-id (common/get hello-url))
(is (= metric-name-with-prefix (get-metric-name metric-registry)))))))))))