打印Spring Cloud Feign请求响应日志

本篇主要基于consul注册中心来实现的

consul官网下载:https://www.consul.io/downloads.html

cmd 命令窗口执行:consul agent -dev

consul 自带 UI 界面,打开网址:http://localhost:8500 ,可以看到当前注册的服务界面

一、feign接口提供者

1、pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

  	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
	  <java.version>1.8</java.version>
	  <spring-cloud.version>Hoxton.SR6</spring-cloud.version>
	</properties>

    <groupId>com.zhanghan</groupId>
    <artifactId>zh-monitor</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>zh-monitor</name>
    <description>zhanghan monitor for Spring Boot</description>
	
	<!--全局管理springcloud版本,并不会引入具体依赖-->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${spring-cloud.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
		
		<!--引入consul依赖-->
		<dependency>
		  <groupId>org.springframework.cloud</groupId>
		  <artifactId>spring-cloud-starter-consul-discovery</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-config</artifactId>
		</dependency>
       <!-- spring boot admin begin -->
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-client</artifactId>
            <version>2.2.4</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
         <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.41</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        
        <dependency>  
		    <groupId>cn.shuibo</groupId>  
		    <artifactId>rsa-encrypt-body-spring-boot</artifactId>  
		    <version>1.0.1.RELEASE</version>
		</dependency>

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency> 
    </dependencies>


    <build>
        <finalName>zh-monitor</finalName>
    </build>


</project>

2、application.properties

server.port=8081
spring.application.name=zhouzy
#spring.boot.admin.client.url=http://localhost:8081

#****************************Security***************************
#spring.boot.admin.client.username=admin
#spring.boot.admin.client.password=admin
#management.endpoints.web.base-path = /
#Open all page nodes by default only two nodes of health and info are enabled.
#management.endpoints.web.exposure.include = *
#Display health specific information No details are displayed by default
#management.endpoint.health.show-details = always
# Redis\u6570\u636E\u5E93\u7D22\u5F15\uFF08\u9ED8\u8BA4\u4E3A0\uFF09

# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=20
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=1000

spring.cloud.gateway.routes.filters.name=RequestRateLimiter
spring.cloud.gateway.routes.filters.args.key-resolver='#{@hostAddrKeyResolver}'
spring.cloud.gateway.routes.filters.args.redis-rate-limiter.replenishRate=1
spring.cloud.gateway.routes.filters.args.redis-rate-limiter.burstCapacity=3


spring.cloud.consul.discovery.enabled=true
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.serviceName=zhouzy

3、http接口

package com.zhouzy.boot.zhouzyBoot.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;
import com.zhouzy.boot.zhouzyBoot.model.User;

@RestController
public class ApiController {
	private static Logger logger = LoggerFactory.getLogger(ApiController.class);
	@Autowired
	RedisTemplate redisTemplate;

	@SuppressWarnings("unchecked")
	@RequestMapping(value = "/api/miaosha",method =RequestMethod.POST)
    public String miaosha(@RequestBody User user,HttpServletRequest request) throws Exception{
		//每个用户只能抢一次,
		String userId = user.getUserId();
		int shopNum = Integer.valueOf(redisTemplate.opsForValue().get("shopNum").toString());
		logger.info("当前商品数量:{}",shopNum);
		if(Integer.valueOf(redisTemplate.opsForValue().get("shopNum").toString()) <= 0){
			 logger.info("商品数量不够了,还有:{}",redisTemplate.opsForValue().get("shopNum"));
			 return "fail";
		}
		Integer num = redisTemplate.opsForValue().get(userId) == null ? 0 : Integer.valueOf(redisTemplate.opsForValue().get(userId).toString());
        if(num > 0){
        	logger.info("用户:{},已经抢过了,当前数量:{}",userId,num);
        	return null;
        }
        // 开启事务支持,在同一个 Connection 中执行命令
    	
		redisTemplate.execute((RedisOperations operations) -> {
			//特别注意,在事务方法块里都用operations操作数据,而不是用redisTemplate
			//这个是没有考虑并发的问题场景,可以试试看结果如何
	    	List<Object> result = null;
			do{
    			operations.watch("shopNum");
    			int shopNum2 = Integer.valueOf(String.valueOf(operations.opsForValue().get("shopNum")));
    			int num2 = operations.opsForValue().get(userId) == null ? 0 : Integer.valueOf(operations.opsForValue().get(userId).toString());
	        	if(num2 > 0){
	        		logger.info("用户:{},已经抢过了,当前数量:{}",userId,num2);
	            	break;
	        	}
	            //shopNum = shopNum - 1;
	        	operations.multi();//开启事务
	        	operations.opsForValue().set(userId, num2+1);
	        	operations.opsForValue().set("shopNum", shopNum2-1);
	        	result = operations.exec();//执行
	        	logger.info(JSONObject.toJSONString(result));
	        	try {
					Thread.sleep(RandomUtils.nextInt(1, 100));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}while (CollectionUtils.isEmpty(result)); //如果失败则重试,注意不是null,而是判断非空,包括长度为0
			
			return result;
        });
		shopNum = Integer.valueOf(redisTemplate.opsForValue().get("shopNum").toString());
		logger.info("执行后当前商品数量:{}",shopNum);
        return "success";
    }
	
	 
}

4、启动

package com.zhouzy.boot.zhouzyBoot;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.DispatcherServlet;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhouzy.boot.zhouzyBoot.config.AxinDispatcherServlet;


@EnableDiscoveryClient
@EnableAutoConfiguration
@SpringBootApplication
class WebApplication{
	public static void main(String[] args) {
		SpringApplication.run(WebApplication.class, args);
	}
	
	 @Bean(name="remoteRestTemplate")
	 public RestTemplate restTemplate() {
	        return new RestTemplate();
	 }
	 
	 @Bean
	    @Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
	    public DispatcherServlet dispatcherServlet() {
	        return new AxinDispatcherServlet();
	    }
	 
	 @Bean(name = "template")
	 public RedisTemplate<String, Object> template(RedisConnectionFactory factory) {
	        // 创建RedisTemplate<String, Object>对象
	        RedisTemplate<String, Object> template = new RedisTemplate<>();
	        // 配置连接工厂
	        template.setConnectionFactory(factory);
	        // 定义Jackson2JsonRedisSerializer序列化对象
	        Jackson2JsonRedisSerializer<Object> jacksonSeial = new Jackson2JsonRedisSerializer<>(Object.class);
	        ObjectMapper om = new ObjectMapper();
	        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
	        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
	        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会报异常
	        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
	        jacksonSeial.setObjectMapper(om);
	        StringRedisSerializer stringSerial = new StringRedisSerializer();
	        // redis key 序列化方式使用stringSerial
	        template.setKeySerializer(stringSerial);
	        // redis value 序列化方式使用jackson
	        template.setValueSerializer(jacksonSeial);
	        // redis hash key 序列化方式使用stringSerial
	        template.setHashKeySerializer(stringSerial);
	        // redis hash value 序列化方式使用jackson
	        template.setHashValueSerializer(jacksonSeial);
	        template.afterPropertiesSet();
	        template.setEnableTransactionSupport(true);
	        return template;
	    }
	 
}

二、Feign接口消费者

1、pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

  	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
	  <java.version>1.8</java.version>
	  <spring-cloud.version>Hoxton.SR6</spring-cloud.version>
	</properties>

    <groupId>com.zhanghan</groupId>
    <artifactId>zh-monitor</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>zh-monitor</name>
    <description>zhanghan monitor for Spring Boot</description>
	
	<!--全局管理springcloud版本,并不会引入具体依赖-->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${spring-cloud.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
		
		<!--引入consul依赖-->
		<dependency>
		  <groupId>org.springframework.cloud</groupId>
		  <artifactId>spring-cloud-starter-consul-discovery</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-config</artifactId>
		</dependency>
		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
       <!-- spring boot admin begin -->
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-client</artifactId>
            <version>2.2.4</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
         <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.41</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        
        <dependency>  
		    <groupId>cn.shuibo</groupId>  
		    <artifactId>rsa-encrypt-body-spring-boot</artifactId>  
		    <version>1.0.1.RELEASE</version>
		</dependency>

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency> 
    </dependencies>


    <build>
        <finalName>zh-monitor</finalName>
    </build>


</project>

2、application.properties

server.port=8083
spring.application.name=zhouzy2
#spring.boot.admin.client.url=http://localhost:8081

#****************************Security***************************
#spring.boot.admin.client.username=admin
#spring.boot.admin.client.password=admin
#management.endpoints.web.base-path = /
#Open all page nodes by default only two nodes of health and info are enabled.
#management.endpoints.web.exposure.include = *
#Display health specific information No details are displayed by default
#management.endpoint.health.show-details = always
# Redis\u6570\u636E\u5E93\u7D22\u5F15\uFF08\u9ED8\u8BA4\u4E3A0\uFF09

# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=20
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=1000

spring.cloud.consul.discovery.enabled=true
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.serviceName=zhouzy2


logging.level.com.zhouzy.boot.zhouzyBoot.service:debug

3、配置日志

package com.zhouzy.boot.zhouzyBoot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import feign.Logger;


@Configuration
public class FeignLogConfiguration {
	 @Bean
	    Logger.Level feignLoggerLevel() {
	 
	        return Logger.Level.FULL;
	 
	    }
}

Logger.Level的值有以下选择:

NONE,无记录(DEFAULT)。

BASIC,只记录请求方法和URL以及响应状态代码和执行时间。

HEADERS,记录基本信息以及请求和响应标头。

FULL,记录请求和响应的头文件,正文和元数据。

4、配置feign接口

package com.zhouzy.boot.zhouzyBoot.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;

import com.zhouzy.boot.zhouzyBoot.model.User;


@FeignClient(value="zhouzy",fallback = FeignApiFallBack.class)
public interface ApiService {
	   @PostMapping("/api/miaosha")
	   public String miaosha(User user); 
}

5、接口调用

package com.zhouzy.boot.zhouzyBoot.controller;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.zhouzy.boot.zhouzyBoot.model.User;
import com.zhouzy.boot.zhouzyBoot.service.ApiService;

@RestController
public class ApiController {
	private static Logger logger = LoggerFactory.getLogger(ApiController.class);
	@Autowired
	ApiService apiService;

	@RequestMapping(value = "/api/test",method =RequestMethod.GET)
    public String test(HttpServletRequest request) throws Exception{
		User user = new User();
		user.setName("name01");
		user.setUserId("1001");
		return apiService.miaosha(user);
    }
	
	 
}

6、启动

package com.zhouzy.boot.zhouzyBoot;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.DispatcherServlet;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhouzy.boot.zhouzyBoot.config.AxinDispatcherServlet;

import feign.Logger;


@EnableDiscoveryClient
@EnableAutoConfiguration
@SpringBootApplication
@EnableFeignClients(basePackages = { "com.zhouzy.boot.zhouzyBoot.service" })
class WebApplication{
	public static void main(String[] args) {
		SpringApplication.run(WebApplication.class, args);
	}
	
	 @Bean(name="remoteRestTemplate")
	 public RestTemplate restTemplate() {
	        return new RestTemplate();
	 }
	 
	 @Bean
	    @Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
	    public DispatcherServlet dispatcherServlet() {
	        return new AxinDispatcherServlet();
	    }
	 
	 @Bean(name = "template")
	 public RedisTemplate<String, Object> template(RedisConnectionFactory factory) {
	        // 创建RedisTemplate<String, Object>对象
	        RedisTemplate<String, Object> template = new RedisTemplate<>();
	        // 配置连接工厂
	        template.setConnectionFactory(factory);
	        // 定义Jackson2JsonRedisSerializer序列化对象
	        Jackson2JsonRedisSerializer<Object> jacksonSeial = new Jackson2JsonRedisSerializer<>(Object.class);
	        ObjectMapper om = new ObjectMapper();
	        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
	        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
	        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会报异常
	        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
	        jacksonSeial.setObjectMapper(om);
	        StringRedisSerializer stringSerial = new StringRedisSerializer();
	        // redis key 序列化方式使用stringSerial
	        template.setKeySerializer(stringSerial);
	        // redis value 序列化方式使用jackson
	        template.setValueSerializer(jacksonSeial);
	        // redis hash key 序列化方式使用stringSerial
	        template.setHashKeySerializer(stringSerial);
	        // redis hash value 序列化方式使用jackson
	        template.setHashValueSerializer(jacksonSeial);
	        template.afterPropertiesSet();
	        template.setEnableTransactionSupport(true);
	        return template;
	    }
	 
	 
}

7、结果

一般比较复杂的接口最好打印下日志,便于出问题是分析! 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wwwzhouzy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值