pom引入
yaml配置
management.endpoints.web.exposure.include=*
management.endpoints.web.base-path=/actuator
management.metrics.export.prometheus.enabled=true
management.metrics.tags.application: ${spring.application.name}
management:
endpoints:
web:
base-path: /actuator
exposure:
include: '*'
metrics:
export:
prometheus:
enabled: true
tags:
application: ${spring.application.name}
点击查看代码
/**
* 描述 工作JobMetrics
* */
@Component
@Getter
@Slf4j
public class JobMetrics implements MeterBinder {
private MeterRegistry meterRegistry;
@Override
public void bindTo(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
/**
* 采集Prometheus指标信息的次数counter
*
*/
public void counter(String metricName, String[] tags) {
try {
meterRegistry.counter(metricName, tags).increment();
} catch (Exception e) {
//log.error("采集summary出现异常: {}", e.getMessage());
}
}
/**
* 采集Prometheus指标信息的次数timer
*
*/
public void timer(String metricName, long val, String[] tags) {
try {
Timer.builder(metricName)
.tags(tags)
.publishPercentiles(0.5, 0.95, 0.99) // 发布分位数
.description("ck summery")
.register(meterRegistry)
.record(val, TimeUnit.MILLISECONDS);
} catch (Exception e) {
//log.error("采集summary出现异常: {}", e.getMessage());
}
}
}
点击查看代码
@Slf4j
@RefreshScope
@Component
public class CheckResponseFilter implements GlobalFilter, Ordered {
private static final String APP_INVOKE_COST_TIME = "app_invoke_cost_time";
private static final String APP_INVOKE_URL_ERROR = "app_invoke_url_error";
@Resource
private JobMetrics jobMetrics;
@Value("${country_code}")
private String countryCode;
@Value("${spring.profiles.active:test}")
private String env;
@Value("${shared.stateWhiteList:255}")
private String stateWhiteList;
private static final DataBufferFactory bufferFactory = new DefaultDataBufferFactory();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long start = System.currentTimeMillis();
URI uri = exchange.getRequest().getURI();
//请求路径
String url = uri.getPath();
String appName = url.split("/")[2];
ServerHttpResponse originalResponse = exchange.getResponse();
DataBufferFactory wrappedBufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
private final ArrayList<DataBuffer> bufferList = new ArrayList<DataBuffer>();
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = Flux.from(body);
return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
// 合并多个流集合,解决返回体分段传输
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer buff = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[buff.readableByteCount()];
buff.read(content);
DataBufferUtils.release(buff);//释放掉内存
//排除Excel导出,不是application/json不打印。若请求是上传图片则在最上面判断。
MediaType contentType = originalResponse.getHeaders().getContentType();
if (!MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
return bufferFactory.wrap(content);
}
// 构建返回
String joinData = new String(content);
//统计错误
countResponseError(appName, url, joinData);
return bufferFactory.wrap(content);
}));
} else {
log.error("gateway_status_code_error", getStatusCode());
}
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build())
.then(Mono.fromRunnable(() -> {
long duration = System.currentTimeMillis() - start;
//统计耗时
summeryCostTime(appName, url, duration);
}));
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
private void summeryCostTime(String appName, String url, long timeCost) {
String[] tagArr = new String[]{
"env", env,
"countryCode", countryCode,
"appName", appName,
"url", url
};
// 执行耗时
jobMetrics.timer(APP_INVOKE_COST_TIME, timeCost, tagArr);
//log
//log.error(APP_INVOKE_COST_TIME + " timeCost:" + timeCost);
}
private void countResponseError(String appName, String url, String responseBody) {
if (responseBody != null && !responseBody.isEmpty()) {
JSONObject jsonObject = JSONObject.parseObject(responseBody);
// 尝试解析JSON响应体
String state = jsonObject.getString("state");
//统计失败
//state 返回代码,200==成功,255==失败,267==需要特殊处理,400==请求无效,403==禁止访问,404==请求地址不存在,503==服务器异常
if (StringUtils.isNotBlank(state) && !StringUtils.equals(state, "200")) {
String[] tagArr = new String[]{
"env", env,
"countryCode", countryCode,
"appName", appName,
"url", url,
"state", state
};
//如果不在白名单内
if (!StringUtils.containsIgnoreCase(stateWhiteList,state)) {
//错误统计
jobMetrics.counter(APP_INVOKE_URL_ERROR, tagArr);
}
//log
log.error(APP_INVOKE_URL_ERROR + " url:" + url + " body:" + responseBody);
}
}
}
}