Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Micrometer metrics tags extension #1322

Merged
merged 13 commits into from
Jul 20, 2021
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2012-2020 The Feign Authors
* Copyright 2012-2021 The Feign 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
Expand Down
44 changes: 5 additions & 39 deletions micrometer/src/main/java/feign/micrometer/FeignMetricName.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2012-2020 The Feign Authors
* Copyright 2012-2021 The Feign 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
Expand All @@ -14,17 +14,6 @@
package feign.micrometer;


import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import feign.MethodMetadata;
import feign.Target;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;

public final class FeignMetricName implements MetricName {

private final Class<?> meteredComponent;
Expand All @@ -46,33 +35,10 @@ public String name() {
}

@Override
public Tags tag(MethodMetadata methodMetadata, Target<?> target, Tag... tags) {
return tag(methodMetadata.targetType(), methodMetadata.method(), target.url(), tags);
}

@Override
public Tags tag(Class<?> targetType, Method method, String url, Tag... extraTags) {
List<Tag> tags = new ArrayList<>();
tags.add(Tag.of("client", targetType.getName()));
tags.add(Tag.of("method", method.getName()));
tags.add(Tag.of("host", extractHost(url)));
tags.addAll(Arrays.asList(extraTags));
return Tags.of(tags);
}

private String extractHost(final String targetUrl) {
try {
String host = new URI(targetUrl).getHost();
if (host != null)
return host;
} catch (final URISyntaxException e) {
public String name(Throwable e) {
if (e == null) {
return name();
}

// can't get the host, in that case, just read first 20 chars from url
return targetUrl.length() <= 20
? targetUrl
: targetUrl.substring(0, 20);
return name("exception");
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Copyright 2012-2021 The Feign 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 feign.micrometer;

import feign.MethodMetadata;
import feign.Target;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class FeignMetricTagResolver implements MetricTagResolver {

@Override
public Tags tag(MethodMetadata methodMetadata, Target<?> target, Tag... tags) {
return tag(methodMetadata, target, null, tags);
}

@Override
public Tags tag(MethodMetadata methodMetadata, Target<?> target, Throwable e, Tag... tags) {
return tag(methodMetadata.targetType(), methodMetadata.method(), target.url(), tags);
}

@Override
public Tags tag(Class<?> targetType, Method method, String url, Tag... extraTags) {
return tag(targetType, method, url, null, extraTags);
}

@Override
public Tags tag(Class<?> targetType, Method method, String url, Throwable e, Tag... extraTags) {
List<Tag> tags = new ArrayList<>(defaultTags());
tags.add(Tag.of("client", targetType.getName()));
tags.add(Tag.of("method", method.getName()));
tags.add(Tag.of("host", extractHost(url)));
if (e != null) {
tags.add(Tag.of("exception_name", e.getClass().getSimpleName()));
}
tags.addAll(Arrays.asList(extraTags));
return Tags.of(tags);
}

protected List<Tag> defaultTags() {
return Collections.emptyList();
}

private String extractHost(final String targetUrl) {
try {
String host = new URI(targetUrl).getHost();
if (host != null)
return host;
} catch (final URISyntaxException e) {
}

// can't get the host, in that case, just read first 20 chars from url
return targetUrl.length() <= 20
? targetUrl
: targetUrl.substring(0, 20);
}
}
2 changes: 1 addition & 1 deletion micrometer/src/main/java/feign/micrometer/MeteredBody.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2012-2020 The Feign Authors
* Copyright 2012-2021 The Feign 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
Expand Down
97 changes: 64 additions & 33 deletions micrometer/src/main/java/feign/micrometer/MeteredClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@
*/
package feign.micrometer;

import java.io.IOException;
import feign.Client;
import feign.FeignException;
import feign.Request;
import feign.*;
import feign.Request.Options;
import feign.RequestTemplate;
import feign.Response;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.Timer;
import java.io.IOException;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be nice not to change import ordering... but, don't worry too much about it =)

import static feign.micrometer.MetricTagResolver.EMPTY_TAGS_ARRAY;

/**
* Warp feign {@link Client} with metrics.
Expand All @@ -31,52 +30,84 @@ public class MeteredClient implements Client {
private final Client client;
private final MeterRegistry meterRegistry;
private final MetricName metricName;
private final MetricTagResolver metricTagResolver;

public MeteredClient(Client client, MeterRegistry meterRegistry) {
this(client, meterRegistry, new FeignMetricName(Client.class));
this(client, meterRegistry, new FeignMetricName(Client.class), new FeignMetricTagResolver());
}

public MeteredClient(Client client, MeterRegistry meterRegistry, MetricName metricName) {
public MeteredClient(Client client,
MeterRegistry meterRegistry,
MetricName metricName,
MetricTagResolver metricTagResolver) {
this.client = client;
this.meterRegistry = meterRegistry;
this.metricName = metricName;
this.metricTagResolver = metricTagResolver;
}

@Override
public Response execute(Request request, Options options) throws IOException {
final RequestTemplate template = request.requestTemplate();

final Timer.Sample sample = Timer.start(meterRegistry);
Timer timer = null;
try {
return meterRegistry.timer(
metricName.name(),
metricName.tag(template.methodMetadata(), template.feignTarget()))
.recordCallable(() -> {
Response response = client.execute(request, options);
meterRegistry.counter(
metricName.name("http_response_code"),
metricName.tag(
template.methodMetadata(),
template.feignTarget(),
Tag.of("http_status", String.valueOf(response.status())),
Tag.of("status_group", response.status() / 100 + "xx")))
.increment();
return response;
});
final Response response = client.execute(request, options);
countResponseCode(request, response, options, response.status(), null);
timer = createTimer(request, response, options, null);
sample.stop(timer);
return response;
} catch (FeignException e) {
meterRegistry.counter(
metricName.name("http_response_code"),
metricName.tag(
template.methodMetadata(),
template.feignTarget(),
Tag.of("http_status", String.valueOf(e.status())),
Tag.of("status_group", e.status() / 100 + "xx")))
.increment();
timer = createTimer(request, null, options, e);
countResponseCode(request, null, options, e.status(), e);
throw e;
} catch (IOException | RuntimeException e) {
timer = createTimer(request, null, options, e);
throw e;
} catch (Exception e) {
timer = createTimer(request, null, options, e);
throw new IOException(e);
} finally {
if (timer == null) {
timer = createTimer(request, null, options, null);
}
sample.stop(timer);
}
}

protected void countResponseCode(Request request,
Response response,
Options options,
int responseStatus,
Exception e) {
final Tag[] extraTags = extraTags(request, response, options, e);
final RequestTemplate template = request.requestTemplate();
final Tags allTags = metricTagResolver
.tag(template.methodMetadata(), template.feignTarget(), e,
new Tag[] {Tag.of("http_status", String.valueOf(responseStatus)),
Tag.of("status_group", responseStatus / 100 + "xx"),
Tag.of("uri", template.path())})
.and(extraTags);
meterRegistry.counter(
metricName.name("http_response_code"),
allTags)
.increment();
}

protected Timer createTimer(Request request,
Response response,
Options options,
Exception e) {
final RequestTemplate template = request.requestTemplate();
final Tags allTags = metricTagResolver
.tag(template.methodMetadata(), template.feignTarget(), e, Tag.of("uri", template.path()))
.and(extraTags(request, response, options, e));
return meterRegistry.timer(metricName.name(e), allTags);
}

protected Tag[] extraTags(Request request,
Response response,
Options options,
Exception e) {
return EMPTY_TAGS_ARRAY;
}
}
Loading