国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

基于Spring-cloud-gateway實(shí)現(xiàn)全局日志記錄

這篇具有很好參考價值的文章主要介紹了基于Spring-cloud-gateway實(shí)現(xiàn)全局日志記錄。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

一 引言

最近項(xiàng)目在線上運(yùn)行出現(xiàn)了一些難以復(fù)現(xiàn)的bug需要定位相應(yīng)api的日志,通過nginx提供的api請求日志難以實(shí)現(xiàn),于是在gateway通過全局過濾器記錄api請求日志。

二 實(shí)現(xiàn)邏輯

基于Spring-cloud-gateway實(shí)現(xiàn)全局日志記錄

  1. 接受到用戶請求后,經(jīng)過全局過濾器,檢驗(yàn)是否開啟相應(yīng)的日志配置及相應(yīng)的黑白名單配置
  2. 在gateway前置處理如記錄當(dāng)前請求開始時間,請求url,請求方法等
  3. 在gateway后置處理中獲得相應(yīng)的請求結(jié)果,響應(yīng)狀態(tài)碼
  4. 記錄當(dāng)前請求日志,根據(jù)實(shí)際需求,通過mq異步處理工具持久化相應(yīng)的日志(本案列作處理)
  5. 診斷請求結(jié)果,對請求異常,慢api等推送相應(yīng)的消息給研發(fā)人人員

三 代碼實(shí)現(xiàn)

定義相應(yīng)的配置類

@Data
@RefreshScope
@Component
@ConfigurationProperties(prefix = LogProperties.PREFIX)
public class LogProperties {
   public static final String PREFIX = "config.gateway.log.access";
   /**
    * 是否開啟日志打印
    */
   private Boolean enabled = true;

   /**
    * 忽略的pattern
    */
   private List<String> ignoredPatterns;

   private ApiAlarmConfiguration fail = new ApiAlarmConfiguration();

   private SlowApiAlarmConfiguration slow = new SlowApiAlarmConfiguration();

   /**
    * 慢API報警配置
    */
   @Data
   public static class SlowApiAlarmConfiguration {

   	/**
   	 * 是否開啟API慢日志打印
   	 */
   	private boolean alarm = true;

   	/**
   	 * 報警閾值 (單位:毫秒)
   	 */
   	private long threshold = 500;
   }


   /**
    * API異常報警(根據(jù)http狀態(tài)碼判定)
    */
   @Data
   public static class ApiAlarmConfiguration {

   	/**
   	 * 是否開啟異常報警 默認(rèn)關(guān)閉
   	 */
   	private boolean alarm = false;

   	/**
   	 * 排除狀態(tài)碼
   	 */
   	private List<Integer> exclusion;
   }
}

定義log實(shí)體

@Data
public class GatewayLog implements Serializable {
  	private static final long serialVersionUID = -3205904134722576668L;

    /**
     * 訪問實(shí)例
	 */
	private String targetServer;

    /**
     * 請求路徑
	 */
	private String requestPath;

    /**
     * 請求與方法
	 */
	private String method;

    /**
     * 請求協(xié)議
	 */
	private String schema;

    /**
     * 請求ip
	 */
	private String ip;

    /**
	 * 請求時間
	 */
	private Date requestTime;

	/**
	 * 請求參數(shù)
	 */
	private Map<String,String> queryParams;

    /**
     * 請求體
	 */
	private String requestBody;

	/**
	 * 請求執(zhí)行時間
	 */
	private Long executeTime;

    /**
     * 請求類型
	 */
	private String requestContentType;

    /**
     * 相應(yīng)狀態(tài)碼
	 */
	private int code;
}

定義相應(yīng)日志工廠及常量

public interface GatewayLogType {
	/**
	 * 常規(guī)輸出
	 */
	String APPLICATION_JSON_REQUEST = "applicationJsonRequest";

	String FORM_DATA_REQUEST = "formDataRequest";

	String BASIC_REQUEST = "basicRequest";

	String NORMAL_REQUEST = "normalRequest";

    /**
     * 慢查詢
	 */
	String SLOW = "slow";

    /**
     * 非200響應(yīng)
	 */
	String FAIL = "fail";
}


@Slf4j
public class GatewayLogInfoFactory {
	public static void log(String type, GatewayLog gatewayLog){
		switch (type){
			case GatewayLogType.APPLICATION_JSON_REQUEST:
				log.info("[{}] {} {},route: {},status: {},excute: {} mills,requestBody: {}"
						,gatewayLog.getIp()
						,gatewayLog.getMethod()
						,gatewayLog.getRequestPath()
						,gatewayLog.getTargetServer()
						,gatewayLog.getCode()
						,gatewayLog.getExecuteTime()
						,StrUtil.replace(gatewayLog.getRequestBody(), StrPool.LF,"")
				);
				break;
			case GatewayLogType.FORM_DATA_REQUEST:
				log.info("[{}] {} {},route: {},status: {},excute: {} mills,requestBody: {}"
						,gatewayLog.getIp()
						,gatewayLog.getMethod()
						,gatewayLog.getRequestPath()
						,gatewayLog.getTargetServer()
						,gatewayLog.getCode()
						,gatewayLog.getExecuteTime()
						,StrUtil.replace(gatewayLog.getRequestBody(), StrPool.LF,"")
				);
				break;
			case GatewayLogType.BASIC_REQUEST:
				log.info("[{}] {} {},route: {},status: {},excute: {} mills,requestBody: {}"
						,gatewayLog.getIp()
						,gatewayLog.getMethod()
						,gatewayLog.getRequestPath()
						,gatewayLog.getTargetServer()
						,gatewayLog.getCode()
						,gatewayLog.getExecuteTime()
						,StrUtil.replace(gatewayLog.getRequestBody(), StrPool.LF,"")
				);
				break;
			case GatewayLogType.NORMAL_REQUEST:
				log.info("[{}] {} {},route: {},status: {},excute: {} mills,queryParams: {}"
						,gatewayLog.getIp()
						,gatewayLog.getMethod()
						,gatewayLog.getRequestPath()
						,gatewayLog.getTargetServer()
						,gatewayLog.getCode()
						,gatewayLog.getExecuteTime()
						,gatewayLog.getQueryParams()
				);
				break;
			default:
				break;
		}
	}
}

定義日志全局過濾器文章來源地址http://www.zghlxwxcb.cn/news/detail-506659.html

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;
import com.zkjc.xlcp.common.notifier.core.Notifier;
import com.zkjc.xlcp.gateway.log.GatewayLogInfoFactory;
import com.zkjc.xlcp.gateway.log.LogProperties;
import com.zkjc.xlcp.gateway.log.constant.GatewayLogType;
import com.zkjc.xlcp.gateway.log.entity.GatewayLog;
import com.zkjc.xlcp.gateway.util.IpUtil;
import io.netty.util.internal.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.multipart.FormFieldPart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.*;

/**
 * @author likun
 * @date 2022年10月24日 9:52
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class AccessLogFilter implements GlobalFilter, Ordered {

	private final LogProperties logProperties;

	private final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();

	/**
	 * default HttpMessageReader.
	 */
	private static final List<HttpMessageReader<?>> MESSAGE_READERS = HandlerStrategies.withDefaults().messageReaders();

	private final AntPathMatcher antPathMatcher = new AntPathMatcher();

	private final Notifier notifier;
	/*
	*  在CncloudRequestGlobalFilter后面執(zhí)行 先清洗url在進(jìn)行路徑的日志的打印
	* */
	@Override
	public int getOrder() {
		return -100;
	}

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		// 判斷是否打開相應(yīng)是日志配置 ingore配置校驗(yàn)
		if (!logProperties.getEnabled()||hasIgnoredFlag(exchange,logProperties)){
			return chain.filter(exchange);
		}
		// 獲得請求上下文
		GatewayLog gatewayLog = parseGateway(exchange);
		ServerHttpRequest request = exchange.getRequest();
		MediaType mediaType = request.getHeaders().getContentType();
		if (Objects.isNull(mediaType)){
			return writeNormalLog(exchange,chain,gatewayLog);
		}
		gatewayLog.setRequestContentType(mediaType.getType() + "/" + mediaType.getSubtype());
		// 對不同的請求類型做相應(yīng)的處理
		if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)){
			return writeBodyLog(exchange,chain,gatewayLog);
		}else if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(mediaType) || MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)){
			return readFormData(exchange,chain,gatewayLog);
		}else {
			return writeBasicLog(exchange,chain,gatewayLog);
		}
	}

	/**
	 * 校驗(yàn)白名單
	 * @param exchange
	 * @param logProperties
	 * @return
	 */
	private Boolean hasIgnoredFlag(ServerWebExchange exchange,LogProperties logProperties){
		List<String> ignoredPatterns = logProperties.getIgnoredPatterns();
		if (CollectionUtil.isEmpty(ignoredPatterns)){
			return Boolean.FALSE;
		}
		ServerHttpRequest request = exchange.getRequest();
		URI uri = request.getURI();
		for (String pattern : ignoredPatterns) {
			if (antPathMatcher.match(pattern,uri.getPath())){
				return Boolean.TRUE;
			}
		}
		return Boolean.FALSE;
	}

	/**
	 * 生成相應(yīng)的報告并推送qq郵箱消息
	 */
	private void report(GatewayLog gatewayLog){
		if (notifier==null){
			return;
		}
		boolean reported = exceptionReport(gatewayLog);
		if (!reported){
			slowApiReport(gatewayLog);
		}
	}

	/**
	 * 異常報警
	 * @param gatewayLog
	 * @return
	 */
	private Boolean exceptionReport(GatewayLog gatewayLog){
		int code = gatewayLog.getCode();
		if (code==HttpStatus.OK.value()){
			return Boolean.FALSE;
		}
		LogProperties.ApiAlarmConfiguration apiAlarmConfiguration = logProperties.getFail();
		if (!apiAlarmConfiguration.isAlarm()){
			log.debug("api exception alarm disabled.");
			return Boolean.FALSE;
		}
		if (!CollectionUtils.isEmpty(apiAlarmConfiguration.getExclusion()) && apiAlarmConfiguration.getExclusion().contains(code)) {
			log.debug("status [{}] excluded.", code);
			return Boolean.FALSE;
		}
		String alarmContent = String.format("【API異常】 請求ip:[{%s}],請求路由:[{%s}],請求地址:[{%s}],返回狀態(tài)碼:[{%d}],執(zhí)行時間:%d ms",gatewayLog.getIp(),gatewayLog.getTargetServer(),gatewayLog.getRequestPath(),code,gatewayLog.getExecuteTime());
		notifier.notify(alarmContent);
		return Boolean.TRUE;
	}

	private Boolean slowApiReport(GatewayLog gatewayLog){
		LogProperties.SlowApiAlarmConfiguration slowApiAlarmConfiguration = logProperties.getSlow();
		long threshold = slowApiAlarmConfiguration.getThreshold();
		if (gatewayLog.getExecuteTime()<threshold){
			return Boolean.FALSE;
		}
		if (!slowApiAlarmConfiguration.isAlarm()) {
			log.debug("slow api alarm disabled.");
			return Boolean.FALSE;
		}
		String slowContent = String.format("【API執(zhí)行時間過長,超過設(shè)定閾值】 請求ip:[{%s}],請求路由:[{%s}],請求地址:[{%s}],執(zhí)行時間:%d ms",gatewayLog.getIp(),gatewayLog.getTargetServer(),gatewayLog.getRequestPath(),gatewayLog.getExecuteTime());
		notifier.notify(slowContent);
		return Boolean.TRUE;
	}


	/**
	 * 獲得當(dāng)前請求分發(fā)的路由
	 * @param exchange
	 * @return
	 */
	private Route getGatewayRoute(ServerWebExchange exchange) {
		return exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
	}

	private GatewayLog parseGateway(ServerWebExchange exchange){
		ServerHttpRequest request = exchange.getRequest();
		String requestPath = request.getPath().pathWithinApplication().value();
		Route route = getGatewayRoute(exchange);
		String ip = IpUtil.getIpAddress(request);
		GatewayLog gatewayLog = new GatewayLog();
		gatewayLog.setSchema(request.getURI().getScheme());
		gatewayLog.setMethod(request.getMethodValue());
		gatewayLog.setRequestPath(requestPath);
		gatewayLog.setTargetServer(route.getId());
		gatewayLog.setIp(ip);
		gatewayLog.setRequestTime(new Date());
		return gatewayLog;
	}

	private Mono writeNormalLog(ServerWebExchange exchange, GatewayFilterChain chain, GatewayLog gatewayLog){
		return chain.filter(exchange).then(Mono.fromRunnable(()->{
			ServerHttpResponse response = exchange.getResponse();
			int value = response.getStatusCode().value();
			gatewayLog.setCode(value);
			long executeTime = DateUtil.between(gatewayLog.getRequestTime(), new Date(), DateUnit.MS);
			gatewayLog.setExecuteTime(executeTime);
			ServerHttpRequest request = exchange.getRequest();
			MultiValueMap<String, String> queryParams = request.getQueryParams();
			Map<String, String> paramsMap = new HashMap<>();
			if (CollectionUtil.isNotEmpty(queryParams)) {
				for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
					paramsMap.put(entry.getKey(), StrUtil.join(StrPool.COMMA,entry.getValue()));
				}
			}
			gatewayLog.setQueryParams(paramsMap);
			GatewayLogInfoFactory.log(GatewayLogType.NORMAL_REQUEST,gatewayLog);
			// 推送相應(yīng)的報告
			report(gatewayLog);
		}));
	}

	/**
	 * 解決 request body 只能讀取一次問題,
	 * 參考: org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory
	 * @param exchange
	 * @param chain
	 * @param gatewayLog
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Mono writeBodyLog(ServerWebExchange exchange, GatewayFilterChain chain, GatewayLog gatewayLog) {
		ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders);
		Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
			gatewayLog.setRequestBody(body);
			return Mono.just(body);
		});
		// 通過 BodyInserter 插入 body(支持修改body), 避免 request body 只能獲取一次
		BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
		HttpHeaders headers = new HttpHeaders();
		headers.putAll(exchange.getRequest().getHeaders());
		// the new content type will be computed by bodyInserter
		// and then set in the request decorator
		headers.remove(HttpHeaders.CONTENT_LENGTH);
		CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
		return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {                    // 重新封裝請求
			ServerHttpRequest decoratedRequest = requestDecorate(exchange, headers, outputMessage);                    // 記錄響應(yīng)日志
			ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, gatewayLog);                    // 記錄普通的
			return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build()).then(Mono.fromRunnable(() -> {                                // 打印日志
				GatewayLogInfoFactory.log(GatewayLogType.APPLICATION_JSON_REQUEST,gatewayLog);
				// 推送相應(yīng)的報告
				report(gatewayLog);
			}));
		}));
	}


	/**
	 * 讀取form-data數(shù)據(jù)
	 * @param exchange
	 * @param chain
	 * @param accessLog
	 * @return
	 */
	private Mono<Void> readFormData(ServerWebExchange exchange, GatewayFilterChain chain, GatewayLog accessLog) {
		return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
			DataBufferUtils.retain(dataBuffer);
			final Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
			final ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
				@Override
				public Flux<DataBuffer> getBody() {
					return cachedFlux;
				}
				@Override
				public MultiValueMap<String, String> getQueryParams() {
					return UriComponentsBuilder.fromUri(exchange.getRequest().getURI()).build().getQueryParams();
				}
			};
			final HttpHeaders headers = exchange.getRequest().getHeaders();
			if (headers.getContentLength() == 0) {
				return chain.filter(exchange);
			}
			ResolvableType resolvableType;
			if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(headers.getContentType())) {
				resolvableType = ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, Part.class);
			} else {
				//解析 application/x-www-form-urlencoded
				resolvableType = ResolvableType.forClass(String.class);
			}

			return MESSAGE_READERS.stream().filter(reader -> reader.canRead(resolvableType, mutatedRequest.getHeaders().getContentType())).findFirst().orElseThrow(() -> new IllegalStateException("no suitable HttpMessageReader.")).readMono(resolvableType, mutatedRequest, Collections.emptyMap()).flatMap(resolvedBody -> {
				if (resolvedBody instanceof MultiValueMap) {
					LinkedMultiValueMap map = (LinkedMultiValueMap) resolvedBody;
					if (CollectionUtil.isNotEmpty(map)) {
						StringBuilder builder = new StringBuilder();
						final Part bodyPartInfo = (Part) ((MultiValueMap) resolvedBody).getFirst("body");
						if (bodyPartInfo instanceof FormFieldPart) {
							String body = ((FormFieldPart) bodyPartInfo).value();
							builder.append("body=").append(body);
						}
						accessLog.setRequestBody(builder.toString());
					}
				} else {
					accessLog.setRequestBody((String) resolvedBody);
				}

				//獲取響應(yīng)體
				ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, accessLog);
				return chain.filter(exchange.mutate().request(mutatedRequest).response(decoratedResponse).build()).then(Mono.fromRunnable(() -> {                                    // 打印日志
					// 打印響應(yīng)的日志
					GatewayLogInfoFactory.log(GatewayLogType.FORM_DATA_REQUEST,accessLog);
					// 推送相應(yīng)的報告
					report(accessLog);
				}));
			});
		});
	}


	private Mono<Void> writeBasicLog(ServerWebExchange exchange, GatewayFilterChain chain, GatewayLog accessLog) {
		return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
			DataBufferUtils.retain(dataBuffer);
			final Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
			final ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
				@Override
				public Flux<DataBuffer> getBody() {
					return cachedFlux;
				}

				@Override
				public MultiValueMap<String, String> getQueryParams() {
					return UriComponentsBuilder.fromUri(exchange.getRequest().getURI()).build().getQueryParams();
				}
			};
			StringBuilder builder = new StringBuilder();
			MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
			if (CollectionUtil.isNotEmpty(queryParams)) {
				for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
					builder.append(entry.getKey()).append("=").append(entry.getValue()).append(StrPool.COMMA);
				}
			}
			accessLog.setRequestBody(builder.toString());            //獲取響應(yīng)體
			ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, accessLog);
			return chain.filter(exchange.mutate().request(mutatedRequest).response(decoratedResponse).build()).then(Mono.fromRunnable(() -> {                        // 打印日志
				GatewayLogInfoFactory.log(GatewayLogType.BASIC_REQUEST,accessLog);
				// 推送相應(yīng)的報告
				report(accessLog);
			}));
		});
	}

	/**
	 * 請求裝飾器,重新計算 headers
	 * @param exchange
	 * @param headers
	 * @param outputMessage
	 * @return
	 */
	private ServerHttpRequestDecorator requestDecorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {
		return new ServerHttpRequestDecorator(exchange.getRequest()) {
			@Override
			public HttpHeaders getHeaders() {
				long contentLength = headers.getContentLength();
				HttpHeaders httpHeaders = new HttpHeaders();
				httpHeaders.putAll(super.getHeaders());
				if (contentLength > 0) {
					httpHeaders.setContentLength(contentLength);
				} else {
					httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
				}
				return httpHeaders;
			}
			@Override
			public Flux<DataBuffer> getBody() {
				return outputMessage.getBody();
			}
		};
	}

	/**
	 * 記錄響應(yīng)日志
	 * 通過 DataBufferFactory 解決響應(yīng)體分段傳輸問題。
	 */
	private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange, GatewayLog gatewayLog) {
		ServerHttpResponse response = exchange.getResponse();
		DataBufferFactory bufferFactory = response.bufferFactory();
		return new ServerHttpResponseDecorator(response) {
			@Override
			public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
				if (body instanceof Flux) {
					// 計算執(zhí)行時間
					long executeTime = DateUtil.between(gatewayLog.getRequestTime(), new Date(), DateUnit.MS);
					gatewayLog.setExecuteTime(executeTime);
					// 獲取響應(yīng)類型,如果是 json 就打印
					String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);//
					gatewayLog.setCode(this.getStatusCode().value());
					//
					if (Objects.equals(this.getStatusCode(), HttpStatus.OK)
							&& !StringUtil.isNullOrEmpty(originalResponseContentType)
							&& originalResponseContentType.contains("application/json")) {
						Flux<? extends DataBuffer> fluxBody = Flux.from(body);
						return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
							// 合并多個流集合,解決返回體分段傳輸
							DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
							DataBuffer join = dataBufferFactory.join(dataBuffers);
							byte[] content = new byte[join.readableByteCount()];
							// 釋放掉內(nèi)存
							join.read(content);
							DataBufferUtils.release(join);
							return bufferFactory.wrap(content);
						}));
					}else {

					}
				}
				return super.writeWith(body);
			}
		};
	}
}

到了這里,關(guān)于基于Spring-cloud-gateway實(shí)現(xiàn)全局日志記錄的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Spring-Cloud-Gateway如何自定義路由過濾器?

    遇到這么一個面試題:自定義網(wǎng)關(guān)過濾器實(shí)現(xiàn)把url中的請求參數(shù)放到http的header中傳遞給微服務(wù)。 我們知道網(wǎng)關(guān)的一個重要的作用就是路由轉(zhuǎn)發(fā),路由表的配置大概是這個樣子: 其中的filters就是配置的路由過濾器,Spring已經(jīng)內(nèi)置了31個路由的過濾器,這些過濾器都是 org.spring

    2024年02月16日
    瀏覽(23)
  • 芝法醬躺平攻略(14)——Nginx與Spring-Cloud-Gateway

    芝法醬躺平攻略(14)——Nginx與Spring-Cloud-Gateway

    上一章芝法醬躺平攻略(12)展望了微服務(wù)下常見的技術(shù)需求與常見解決方案,本期來講解第一部分,Nginx與SpringCloud-Gateway。 本章將實(shí)踐在nginx和spring-cloud-gateway的簡單使用。 首先,去官網(wǎng)查找最新穩(wěn)定版,把下載的nginx包放到DOWNLOAD文件夾下。 而后把該包解壓到SOFTWARE文件夾

    2024年02月07日
    瀏覽(32)
  • Spring-Cloud-Gateway修改請求(json,form帶文件請求)參數(shù),返回值參數(shù)

    新項(xiàng)目需要在getway統(tǒng)一做入?yún)?、出參加解密,記錄日志。記錄一下form,x-www-form-urlencoded , json 這幾種修改數(shù)據(jù)的方式。 gateway做攔截器是實(shí)現(xiàn)GlobalFilter接口,修改json方式網(wǎng)上有很多文章,后來又想研究研究能不能實(shí)現(xiàn)修改form-data參數(shù),以及文件請求,后者文章不多大部分是怎

    2024年02月16日
    瀏覽(28)
  • Spring Cloud GateWay 全局過濾器

    這是一個自定義的 Spring Cloud Gateway 全局過濾器(Global Filter)。在 Spring Cloud Gateway 中,全局過濾器可以在請求被路由到目標(biāo)服務(wù)之前或之后執(zhí)行一些操作。這個過濾器實(shí)現(xiàn)了 GlobalFilter 接口和 Ordered 接口,這兩個接口的作用如下: GlobalFilter 接口: 這是一個 Spring Cloud Gateway 提

    2024年02月11日
    瀏覽(20)
  • Spring Cloud Gateway GlobalFilter(全局過濾器)詳解(官方原版)

    GlobalFilter接口具有與GatewayFilter相同的簽名。這些是有條件地應(yīng)用于所有路由的特殊過濾器。 當(dāng)請求與路由匹配時,過濾web處理程序會將GlobalFilter的所有實(shí)例和GatewayFilter的所有路由特定實(shí)例添加到過濾器鏈中。這個組合過濾器鏈由org.springframework.core.Ordered接口排序,您可以通

    2024年02月09日
    瀏覽(16)
  • 【Spring Cloud】深入探索統(tǒng)一網(wǎng)關(guān) Gateway 的搭建,斷言工廠,過濾器工廠,全局過濾器以及跨域問題

    【Spring Cloud】深入探索統(tǒng)一網(wǎng)關(guān) Gateway 的搭建,斷言工廠,過濾器工廠,全局過濾器以及跨域問題

    在微服務(wù)架構(gòu)中,網(wǎng)關(guān)是至關(guān)重要的組件,具有多重職責(zé),為整個系統(tǒng)提供了一系列關(guān)鍵功能。從下面的微服務(wù)結(jié)構(gòu)圖中,我們可以明確網(wǎng)關(guān)的幾項(xiàng)主要作用: 微服務(wù)結(jié)構(gòu)圖: 請求過濾與安全: 用戶的所有請求首先經(jīng)過網(wǎng)關(guān),這使得網(wǎng)關(guān)成為系統(tǒng)的第一道防線。通過對傳入

    2024年02月07日
    瀏覽(23)
  • Spring Cloud Gateway 實(shí)現(xiàn)原理

    Spring Cloud Gateway是Spring Cloud生態(tài)系統(tǒng)中的一個組件,用于構(gòu)建基于Spring Boot的微服務(wù)架構(gòu)中的網(wǎng)關(guān)服務(wù)。它的主要目的是提供一種靈活的方式來路由、過濾和轉(zhuǎn)換HTTP請求,從而允許您構(gòu)建強(qiáng)大、高性能的微服務(wù)應(yīng)用程序。 以下是Spring Cloud Gateway的一些核心原理和功能: 路由(

    2024年02月10日
    瀏覽(26)
  • Spring Cloud GateWay實(shí)現(xiàn)熔斷降級

    當(dāng)分布式系統(tǒng)中的網(wǎng)關(guān)接收到大量請求并向后端遠(yuǎn)程系統(tǒng)或服務(wù)發(fā)起調(diào)用時,后端服務(wù)可能會產(chǎn)生調(diào)用失敗(如超時或異常)。這時,如果讓請求繼續(xù)堆積在網(wǎng)關(guān)上,可能會導(dǎo)致整個系統(tǒng)的癱瘓。因此,需要快速失敗并返回請求,這就是所謂的熔斷。 降級是指在系統(tǒng)資源不足

    2024年02月02日
    瀏覽(26)
  • Spring Cloud Gateway實(shí)現(xiàn)灰度發(fā)布功能

    Spring Cloud Gateway實(shí)現(xiàn)灰度發(fā)布功能

    灰度發(fā)布又名金絲雀發(fā)布,在微服務(wù)中的表現(xiàn)為同一服務(wù)同時上線不同版本,讓一部分用戶使用新版本來驗(yàn)證新特性,如果驗(yàn)證沒有問題,則將所有用戶都遷移到新版本上。 在微服務(wù)架構(gòu)中,網(wǎng)關(guān)負(fù)責(zé)請求的統(tǒng)一入口,主要功能之一是請求路由。而灰度發(fā)布實(shí)質(zhì)就是讓指定用

    2024年02月21日
    瀏覽(25)
  • Spring Cloud Gateway 網(wǎng)關(guān)實(shí)現(xiàn)白名單功能

    1 摘要 對于微服務(wù)后臺而言,網(wǎng)關(guān)層作為所有網(wǎng)絡(luò)請求的入口。一般基于安全考慮,會在網(wǎng)關(guān)層做權(quán)限認(rèn)證,但是對于一些例如登錄、注冊等接口以及一些資源數(shù)據(jù),這些是不需要有認(rèn)證信息,因此需要在網(wǎng)關(guān)層設(shè)計一個白名單的功能。本文將基于 Spring Cloud Gateway 2.X 實(shí)現(xiàn)白

    2023年04月08日
    瀏覽(29)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包