org.zodiac.actuate.feign.FeignClientMetrics Maven / Gradle / Ivy
package org.zodiac.actuate.feign;
import feign.Client;
import feign.Request;
import feign.Response;
import io.micrometer.core.instrument.Clock;
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 javax.annotation.Nullable;
import org.zodiac.commons.util.Colls;
import org.zodiac.feign.core.constants.FeignConstants;
import org.zodiac.feign.core.util.FeignUtil;
import org.zodiac.sdk.nio.http.common.HttpHeaders;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class FeignClientMetrics {
private MeterRegistry registry;
private final String requestsMetricName;
private final Iterable extraTags;
public FeignClientMetrics(String requestsMetricName, Iterable extraTags) {
this(null, requestsMetricName, extraTags);
}
public FeignClientMetrics(MeterRegistry registry, String requestsMetricName, Iterable extraTags) {
this.registry = registry;
this.requestsMetricName = requestsMetricName;
this.extraTags = extraTags;
}
public void setRegistry(MeterRegistry registry) {
this.registry = registry;
}
public static Builder builder(String name) {
return new Builder(name);
}
/**
* 代理FeignClient。
*
* @param client 客户端对象
* @return 被代理的客户端对象
*/
public Client delegate(Client client) {
return ((request, options) -> {
Clock clock = this.registry.config().clock();
long startTime = clock.monotonicTime();
String originalUri = getOriginalUri(request);
if (originalUri != null) {
Map> newHeaders = Colls.map(request.headers());
newHeaders.remove(FeignConstants.FEIGN_ANNOTATION_PATH);
request = Request.create(request.httpMethod(), request.url(), Collections.unmodifiableMap(newHeaders),
request.body(), request.charset(), null);
}
Response response = null;
Exception exception = null;
try {
response = client.execute(request, options);
return response;
} catch (Exception ex) {
exception = ex;
throw ex;
} finally {
record(clock.monotonicTime() - startTime, originalUri, request, response, exception);
}
});
}
/**
* 从请求对象中取得原始URI。
*
* @param request 请求对象
* @return 原始URI分片
*/
@Nullable
private String getOriginalUri(Request request) {
/*取得原始URI模板。*/
Collection originalUrlHeaders = request.headers().get(FeignConstants.FEIGN_ANNOTATION_PATH);
if (originalUrlHeaders != null && !originalUrlHeaders.isEmpty()) {
return FeignUtil.transferToRaw(originalUrlHeaders.iterator().next());
}
return null;
}
/**
* 从请求对象中取得Host。
*
* @param request 请求对象
* @return Host信息
*/
@Nullable
private String getHostFromHeader(Request request) {
/*尝试从头中获取Host。*/
Collection hostHeaders = request.headers().get(HttpHeaders.HOST);
if (hostHeaders != null && !hostHeaders.isEmpty()) {
return hostHeaders.iterator().next();
}
return null;
}
/**
* 记录指标项。
*
* @param costTime 响应时间,单位毫秒
* @param originalUri 原始请求路径
* @param request 请求对象
* @param response 应答对象
* @param exception 异常对象
*/
private void record(long costTime, String originalUri, Request request, Response response, Exception exception) {
try {
URL uri = new URL(request.url());
Iterable tags = Tags.concat(extraTags, Tags.of(
"host", Optional.ofNullable(getHostFromHeader(request)).orElse(uri.getHost()),
"remote", uri.getHost() + (uri.getPort() > 0 ? ":" + uri.getPort() : ""),
"method", request.httpMethod().name(),
"uri", originalUri != null ? originalUri : "Unknown",
"status", response != null ? String.format("%d", response.status()) : "None",
"exception", exception != null ? exception.getClass().getSimpleName() : "None"
));
Timer.builder(this.requestsMetricName)
.tags(tags)
.description("Timer of Feign client")
.register(registry)
.record(costTime, TimeUnit.NANOSECONDS);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
public static class Builder {
private String name;
private Iterable tags = Collections.emptyList();
Builder(String name) {
this.name = name;
}
public Builder tags(Iterable tags) {
this.tags = tags;
return this;
}
public FeignClientMetrics build() {
return new FeignClientMetrics(name, tags);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy