Skip to content

Commit

Permalink
Micrometer metrics tags extension (#1322)
Browse files Browse the repository at this point in the history
* Micrometer metrics tags extension

* Merge parent

* Fix license

* Add tags to response code counter metric

* Fix format

* Improve tags resolution for exceptions, decrease amount of similar methods

* Make MeteredEncoder timer and counter methods protected

* Fix formatting

* Add finally blocks

Co-authored-by: Nikolay Fadin <nikolay.fadin@sperasoft.com>
Co-authored-by: Kuvaldis <a1N9u8t9I1k>
Co-authored-by: Marvin Froeder <velo@users.noreply.github.com>
  • Loading branch information
3 people committed Jul 20, 2021
1 parent aa96dda commit 5ecd704
Show file tree
Hide file tree
Showing 11 changed files with 328 additions and 164 deletions.
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;
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;
}
}

0 comments on commit 5ecd704

Please sign in to comment.