Skip to content

Commit 33305c0

Browse files
authored
Add support for Dropwizard Metrics 4.1.x (OpenFeign#1235)
* Add support for Dropwizard Metrics 4.1.x Dropwizard Metrics 5.x is currently unmaintained, so there should be the option to use the currently maintained Dropwizard Metrics version. * Reformat code * Add dropwizard-metrics4 to README.md
1 parent 835a963 commit 33305c0

13 files changed

Lines changed: 737 additions & 0 deletions

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,21 @@ But, it's possible to add metric collection capabilities to any feign client.
909909
910910
Metric Capabilities provide a first-class Metrics API that users can tap into to gain insight into the request/response lifecycle.
911911
912+
#### Dropwizard Metrics 4
913+
914+
```
915+
public class MyApp {
916+
public static void main(String[] args) {
917+
GitHub github = Feign.builder()
918+
.addCapability(new Metrics4Capability())
919+
.target(GitHub.class, "https://api.github.com");
920+
921+
github.contributors("OpenFeign", "feign");
922+
// metrics will be available from this point onwards
923+
}
924+
}
925+
```
926+
912927
#### Dropwizard Metrics 5
913928
914929
```

dropwizard-metrics4/pom.xml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright 2012-2020 The Feign Authors
5+
6+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7+
in compliance with the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software distributed under the License
12+
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13+
or implied. See the License for the specific language governing permissions and limitations under
14+
the License.
15+
16+
-->
17+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
18+
<modelVersion>4.0.0</modelVersion>
19+
<parent>
20+
<groupId>io.github.openfeign</groupId>
21+
<artifactId>parent</artifactId>
22+
<version>10.10.2-SNAPSHOT</version>
23+
</parent>
24+
<artifactId>feign-dropwizard-metrics4</artifactId>
25+
<name>Feign Dropwizard Metrics4</name>
26+
<description>Feign Dropwizard Metrics 4</description>
27+
28+
<properties>
29+
<main.basedir>${project.basedir}/..</main.basedir>
30+
</properties>
31+
32+
<dependencies>
33+
<dependency>
34+
<groupId>${project.groupId}</groupId>
35+
<artifactId>feign-core</artifactId>
36+
</dependency>
37+
<dependency>
38+
<groupId>${project.groupId}</groupId>
39+
<artifactId>feign-mock</artifactId>
40+
<scope>test</scope>
41+
</dependency>
42+
<dependency>
43+
<groupId>io.dropwizard.metrics</groupId>
44+
<artifactId>metrics-core</artifactId>
45+
<version>4.1.9</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>org.hamcrest</groupId>
49+
<artifactId>java-hamcrest</artifactId>
50+
<version>2.0.0.0</version>
51+
<scope>test</scope>
52+
</dependency>
53+
</dependencies>
54+
</project>
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* Copyright 2012-2020 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign.metrics4;
15+
16+
import java.io.FilterInputStream;
17+
import java.io.IOException;
18+
import java.io.InputStream;
19+
import feign.Util;
20+
21+
/**
22+
* Copy from guava CountingInputStream
23+
*
24+
* An {@link InputStream} that counts the number of bytes read.
25+
*
26+
* @author Chris Nokleberg
27+
* @since 1.0
28+
*/
29+
public final class CountingInputStream extends FilterInputStream {
30+
31+
private long count;
32+
private long mark = -1;
33+
34+
/**
35+
* Wraps another input stream, counting the number of bytes read.
36+
*
37+
* @param in the input stream to be wrapped
38+
*/
39+
public CountingInputStream(InputStream in) {
40+
super(Util.checkNotNull(in, "InputStream must not be null"));
41+
}
42+
43+
/** Returns the number of bytes read. */
44+
public long getCount() {
45+
return count;
46+
}
47+
48+
@Override
49+
public int read() throws IOException {
50+
final int result = in.read();
51+
if (result != -1) {
52+
count++;
53+
}
54+
return result;
55+
}
56+
57+
@Override
58+
public int read(byte[] b, int off, int len) throws IOException {
59+
final int result = in.read(b, off, len);
60+
if (result != -1) {
61+
count += result;
62+
}
63+
return result;
64+
}
65+
66+
@Override
67+
public long skip(long n) throws IOException {
68+
final long result = in.skip(n);
69+
count += result;
70+
return result;
71+
}
72+
73+
@Override
74+
public synchronized void mark(int readlimit) {
75+
in.mark(readlimit);
76+
mark = count;
77+
// it's okay to mark even if mark isn't supported, as reset won't work
78+
}
79+
80+
@Override
81+
public synchronized void reset() throws IOException {
82+
if (!in.markSupported()) {
83+
throw new IOException("Mark not supported");
84+
}
85+
if (mark == -1) {
86+
throw new IOException("Mark not set");
87+
}
88+
89+
in.reset();
90+
count = mark;
91+
}
92+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Copyright 2012-2020 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign.metrics4;
15+
16+
17+
import java.lang.reflect.Method;
18+
import java.net.URI;
19+
import java.net.URISyntaxException;
20+
import com.codahale.metrics.MetricRegistry;
21+
import feign.MethodMetadata;
22+
import feign.Target;
23+
24+
public final class FeignMetricName {
25+
26+
private final Class<?> meteredComponent;
27+
28+
29+
public FeignMetricName(Class<?> meteredComponent) {
30+
this.meteredComponent = meteredComponent;
31+
}
32+
33+
34+
public String metricName(MethodMetadata methodMetadata, Target<?> target, String suffix) {
35+
return MetricRegistry.name(metricName(methodMetadata, target), suffix);
36+
}
37+
38+
public String metricName(MethodMetadata methodMetadata, Target<?> target) {
39+
return metricName(methodMetadata.targetType(), methodMetadata.method(), target.url());
40+
}
41+
42+
public String metricName(Class<?> targetType, Method method, String url) {
43+
return MetricRegistry.name(meteredComponent, targetType.getName(), method.getName(),
44+
extractHost(url));
45+
}
46+
47+
private String extractHost(final String targetUrl) {
48+
try {
49+
return new URI(targetUrl).getHost();
50+
} catch (final URISyntaxException e) {
51+
// can't get the host, in that case, just read first 20 chars from url
52+
return targetUrl.length() <= 20
53+
? targetUrl
54+
: targetUrl.substring(0, 20);
55+
}
56+
}
57+
58+
59+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* Copyright 2012-2020 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign.metrics4;
15+
16+
import static feign.Util.UTF_8;
17+
import java.io.*;
18+
import java.nio.charset.Charset;
19+
import java.util.function.Supplier;
20+
import feign.Response.Body;
21+
22+
/**
23+
* {@link Body} implementation that keeps track of how many bytes are read.
24+
*/
25+
public final class MeteredBody implements Body {
26+
27+
private final Body delegate;
28+
private Supplier<Long> count;
29+
30+
public MeteredBody(Body body) {
31+
this.delegate = body;
32+
count = () -> 0L;
33+
}
34+
35+
@Override
36+
public void close() throws IOException {
37+
delegate.close();
38+
}
39+
40+
@Override
41+
public Integer length() {
42+
return delegate.length();
43+
}
44+
45+
@Override
46+
public boolean isRepeatable() {
47+
return delegate.isRepeatable();
48+
}
49+
50+
@Override
51+
public InputStream asInputStream() throws IOException {
52+
// TODO, ideally, would like not to bring guava just for this
53+
final CountingInputStream input = new CountingInputStream(delegate.asInputStream());
54+
count = input::getCount;
55+
return input;
56+
}
57+
58+
@Override
59+
public Reader asReader() throws IOException {
60+
return new InputStreamReader(asInputStream(), UTF_8);
61+
}
62+
63+
public long count() {
64+
return count.get();
65+
}
66+
67+
@Override
68+
public Reader asReader(Charset charset) throws IOException {
69+
return new InputStreamReader(asInputStream(), charset);
70+
}
71+
72+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Copyright 2012-2020 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign.metrics4;
15+
16+
17+
import java.io.IOException;
18+
import com.codahale.metrics.MetricRegistry;
19+
import com.codahale.metrics.Timer;
20+
import feign.*;
21+
import feign.Request.Options;
22+
23+
/**
24+
* Warp feign {@link Client} with metrics.
25+
*/
26+
public class MeteredClient implements Client {
27+
28+
private final Client client;
29+
private final MetricRegistry metricRegistry;
30+
private final FeignMetricName metricName;
31+
private final MetricSuppliers metricSuppliers;
32+
33+
public MeteredClient(Client client, MetricRegistry metricRegistry,
34+
MetricSuppliers metricSuppliers) {
35+
this.client = client;
36+
this.metricRegistry = metricRegistry;
37+
this.metricSuppliers = metricSuppliers;
38+
this.metricName = new FeignMetricName(Client.class);
39+
}
40+
41+
@Override
42+
public Response execute(Request request, Options options) throws IOException {
43+
final RequestTemplate template = request.requestTemplate();
44+
try (final Timer.Context classTimer =
45+
metricRegistry.timer(
46+
metricName.metricName(template.methodMetadata(), template.feignTarget()),
47+
metricSuppliers.timers()).time()) {
48+
return client.execute(request, options);
49+
}
50+
}
51+
52+
}

0 commit comments

Comments
 (0)