当前位置: 首页 > wzjs >正文

织梦做淘宝客网站视频电子商务平台的开发建设

织梦做淘宝客网站视频,电子商务平台的开发建设,ui人机界面设计,天津在线制作网站日常开发中,你如何快速定位问题的? a. 排除条件 特别是处理并发请求时,通过对每个方法生成traceId(自定义唯一标识符)用于快速定位目标日志,追踪整个系统中的请求流程。 b. 日志脱敏增强 返回值带着敏感…

日常开发中,你如何快速定位问题的?

a. 排除条件

特别是处理并发请求时,通过对每个方法生成traceId(自定义唯一标识符)用于快速定位目标日志,追踪整个系统中的请求流程。

b. 日志脱敏增强

返回值带着敏感信息,要么不返回,要么加密

请求参数带着敏感信息,要么不记录,要么加密

c. 解决方案

服务端入口处可以生成唯一的id,叫做:traceId

日志中均需要输出traceId的值

接口返回值中,添加一个通用的字段: traceld,将上面的traceld作为这个字段的值

这样前端发现接口有问题的时候,直接将这个traceld提供给我们,我们便可以在日志中快速查询出对应的日志。

MDC组件

使用Slf4j.MDC日志功能增强

org.slf4j.MDC是 SLF4J库中的一个组件,MDC提供了一种机制,允许在日志消息中插入上下文信息,这些信息可以跨多个方法调用和线程边界传播。这对于跟踪和调试分布式系统或多线程应用程序中的请求非常有用。

重点:**MDC 允许将键值对与当前线程关联起来(类似ThreadLocal)**然后,可以在我们的日志语句中引用这些值,从而能够更容易地识别和理解日志消息产生的上下文。

例如,你可能会在 Web 应用程序的每个请求开始时,将用户的 ID 或会话 ID 放入 MDC,然后在你的日志语句中引用这个值。这样,当你查看日志时,你可以很容易地看到哪个用户的哪个请求产生了哪些日志消息。

使用方式

Logback学习系列4(Pattern使用方法)https://www.jianshu.com/p/5cfc26caf50d

  • 设置值: MDC.put("userId", "12345");
  • 在日志语句中使用值: logger.info("Processing request for user: {}", MDC.get("userId"));
  • 清除值 MDC.remove("userId")
例子

假设我们正在开发一个电商网站,该网站由多个微服务组成,包括用户服务、订单服务、支付服务等。每个服务都运行在独立的服务器上,并通过REST API相互调用。为了方便调试和监控,我们需要在整个请求处理过程中跟踪每一个请求,并确保所有相关的日志条目都能关联起来。

  1. 当请求到达第一个服务(例如用户服务)时,TraceFilter生成一个唯一的traceId并将其放入MDC中。

  2. 当用户服务需要调用订单服务时,将当前线程中的traceId作为HTTP请求头的一部分传递给下游服务(订单服务)。在订单服务接收到请求时,从请求头中读取traceId并再次放入MDC中。

    // 客户端拦截器:将TraceId注入HTTP头
    public class OpenFeignRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate requestTemplate) {String traceId = MDC.get("traceId"); // 从当前线程上下文中获取TraceIdrequestTemplate.header("X-Trace-ID", traceId); // 注入到HTTP头}
    }// 服务端处理:从HTTP头提取TraceId
    public class TraceFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {String traceId = request.getParameter("X-Trace-ID"); // 从HTTP头获取TraceIdMDC.put("traceId", traceId); // 写入当前线程上下文chain.doFilter(request, response);}
    }
    
  3. 在每个服务的日志配置文件中,添加traceId到日志格式中,以便每条日志都包含这个信息。开发人员只需搜索traceId: xyz789即可快速定位问题根源。

    [订单服务] [traceId: xyz789] - 开始创建订单
    [库存服务] [traceId: xyz789] - 扣减库存成功
    [支付服务] [traceId: xyz789] - 支付失败,数据库连接超时
    
  4. 现在,无论请求跨越了多少个服务,只要查看日志,就可以通过traceId轻松地将所有相关的日志条目关联起来,从而快速定位问题或理解系统的运行情况。

核心代码
自定义注解

脱敏注解@NoLogAnnotation 相关方法加上注解后隐藏参数信息,脱敏前 密码没加密时会暴露用户密码等敏感信息

@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface NoLogAnnotation {//可以用在参数列表或者方法上,屏蔽不愿意记录的参数
}

日志配置logback.xml展示TraceId

<?xml version="1.0" encoding="UTF-8"?>
<configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><!-- 日志输出格式 将MDC中的traceId自动嵌入每条日志(唯一)--><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] [traceId:%X{traceId}] - %msg%n</pattern></encoder></appender><logger name="com.zr.study" level="info" /><root level="info"><appender-ref ref="STDOUT" /></root>
</configuration>
链路追踪组件

TraceConfiguration,配置类注入过滤器和切面类进ioc容器

package com.zr.study.trace;
import com.zr.study.aop.ResultTraceIdAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** proxyBeanMethods属性* 指定@Bean注解标注的方法是否使用代理,*  默认是true使用代理,直接从IOC容器之中取得对象;*  false:每次调用@Bean标注的方法获取到的对象和IOC容器中的都不一样,是一个新的对象,以此提高性能*/
@Configuration(proxyBeanMethods = false)
public class TraceConfiguration {//注册过滤器与切面@Beanpublic TraceFilter traceFilter() {return new TraceFilter();}@Beanpublic ResultTraceIdAspect fillRequestIdAspect() {return new ResultTraceIdAspect();}
}
链路工具类

TraceUtils工具类封装了ThreadLocal存储获取删除traceId和MDC增删traceId的方法

  • 存到ThreadLocal:每个请求都运行在其自己的线程上,通过将traceId绑定到线程本地存储(ThreadLocal)
    • 可以确保每个线程都有其独立的traceId,避免了多线程环境下数据混淆问题。
    • 在请求任意过程中都可以获取traceId
  • 存到MDC中:可以在日志条目中自动包含这个traceId(底层也是基于ThreadLocalMap实现)
import org.slf4j.MDC;public class TraceUtils {//结合线程本地存储和日志上下文,实现 traceId 的传递与日志关联public static final String TRACE_ID = "traceId";public static ThreadLocal<String> traceIdThreadLocal = new ThreadLocal<>();//设置 traceId 到当前线程的上下文中public static void setTraceId(String traceId) {traceIdThreadLocal.set(traceId);MDC.put(TRACE_ID, traceId); // 同步到 MDC}public static String getTraceId() {return traceIdThreadLocal.get();}public static void removeTraceId() {traceIdThreadLocal.remove();MDC.remove(TRACE_ID);}
}
链路过滤器

TraceFilter解耦业务,通过过滤器生成TraceId+插入TreadLocal和MDC中,并通过时间戳记录方法执行时间打印到控制台

import cn.hutool.core.util.IdUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;//确保该过滤器是第一个执行的,优先设置 traceId
@Order(Ordered.HIGHEST_PRECEDENCE)
//Web 过滤器,匹配所有请求路径
@WebFilter(urlPatterns = "/**", filterName = "TraceFilter")
public class TraceFilter extends OncePerRequestFilter {private Logger log = LoggerFactory.getLogger(this.getClass());@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {//尝试 从HTTP头获取TraceIdString traceID =request.getParameter("X-Trace-ID"); if (traceID == null) {traceID = IdUtil.fastSimpleUUID(); //生成唯一traceId}TraceUtils.setTraceId(traceID); //同步到线程上下文和MDC日志中long startTime = System.currentTimeMillis();try {filterChain.doFilter(request, response);} finally {//请求后续处理完成或异常,都会返回这里清理traceIdTraceUtils.removeTraceId();}long endTime = System.currentTimeMillis();  //记录请求耗时log.info("请求地址:{},耗时(毫秒):{}", request.getRequestURL().toString(), (endTime - startTime));}
}
切面类

ResultTraceIdAspect:环绕通知切点为Controller和全局异常处理器,目的是无论方法是否异常,给统一方法返回类插入traceId

🔁 执行流程如下:

  1. 进入切面
  2. 调用 proceed() 方法 → 实际上就是调用目标方法(Controller 或 ExceptionHandler 的方法)
  3. 获取返回值 result
  4. 判断是否是 ResultData 类型(自定义统一返回包装类)
  5. 如果是,则设置 traceId
  6. 返回增强后的结果
@Order
@Aspect
@Component
public class ResultTraceIdAspect {//拦截所有 Controller 方法和全局异常处理器方法//对所有控制器层和异常处理器的返回结果进行统一增强处理@Pointcut("execution(* com.zr..*Controller.*(..)) || execution(* com.zr.study.exp.GlobalExceptionHandler.*(..))")public void pointCut() {}@Around("pointCut()")  //环绕通知,如针对controller方法的前后进行增强public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {Object result = null;result = proceedingJoinPoint.proceed(); //放行if (result instanceof ResultData) {((ResultData<?>) result).setTraceId(TraceUtils.getTraceId());}return result;}
}

ResultData是统一返回类,这块相比于之前的,多了traceId链路追踪ID字段给前端

@Data
@Accessors(chain = true)
public class ResultData<T> {private String code;/** 结果状态 ,具体状态码参见枚举类ReturnCodeEnum.java*/private String message;private T data;private long timestamp ;private String traceId;public ResultData (){this.timestamp = System.currentTimeMillis();}public static <T> ResultData<T> success(T data) {ResultData<T> resultData = new ResultData<>();resultData.setCode(ReturnCodeEnum.RC200.getCode());resultData.setMessage(ReturnCodeEnum.RC200.getMessage());resultData.setData(data);return resultData;}public static <T> ResultData<T> fail(String code, String message) {ResultData<T> resultData = new ResultData<>();resultData.setCode(code);resultData.setMessage(message);return resultData;}}
增强Controller

ControllerLogAspect:环绕通知切点为Controller对日志参数做封装并打印

//增强接口的日志监控能力,提升调试和排查问题的效率,同时提供灵活的控制开关(如脱敏、禁用日志等)
@Order(value = Ordered.HIGHEST_PRECEDENCE)  //最高优先级
@Aspect
@Component
public class ControllerLogAspect {private Logger log = LoggerFactory.getLogger(this.getClass());private static final String SALT = "zr"; // 可以使用随机盐或固定盐//拦截以 Controller 结尾的所有方法@Around("execution(* com.zr..*Controller.*(..))")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {Object result = null;MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();try {//打印处理当前请求的完整类名和方法名称//例如:  接口方法:UserController.loginlog.info("接口方法:{}.{}", methodSignature.getDeclaringTypeName(), methodSignature.getName());//获取所有要打印的参数,丢到map中,key为参数名称,value为参数的值,然后会将这个map以json格式输出Map<String, Object> logParamsMap = new LinkedHashMap<>();String[] parameters = methodSignature.getParameterNames();Object[] args = joinPoint.getArgs();if(!ArrayUtil.isEmpty(parameters)){for (int i = 0; i < args.length; i++) {//判断是否需要脱敏if (parameterIsLog(methodSignature, i)) {//参数名称String parameterName = parameters[i];//参数值Object parameterValue = args[i];//将其放入到map中,稍后会以json格式输出logParamsMap.put(parameterName, parameterValue);}}}//输出示例:  方法参数列表:{"username":"admin","password":"secret"}log.info("方法参数列表:{}", JSONUtil.toJsonStr(logParamsMap));result = joinPoint.proceed();   //程序放行......return result;} finally {//判断方法的返回值是否需要打印?方法上有 @NoLogAnnotation 注解的,表示结果不打印方法返回值if (this.resultIsLog(methodSignature)) {log.info("方法返回值:{}", JSONUtil.toJsonStr(result));}}}/*** 指定位置的参数是否需要打印出来?(脱敏操作)*/private boolean parameterIsLog(MethodSignature methodSignature, int paramIndex) {if (methodSignature.getMethod().getParameterCount() == 0) {return false;}// 参数上有 @NoLogAnnotation注解的不会打印Annotation[] parameterAnnotation = methodSignature.getMethod().getParameterAnnotations()[paramIndex];if (parameterAnnotation != null && parameterAnnotation.length > 0) {for (Annotation annotation : parameterAnnotation) {if (annotation.annotationType() == NoLogAnnotation.class) {return false;}}}//参数类型是 ServletRequest / ServletResponse / 其他配置的敏感类型?→ 不打印Class parameterType = methodSignature.getParameterTypes()[paramIndex];for (Class<?> type : noLogTypes) {if (type.isAssignableFrom(parameterType)) {return false;}}return true;}// 参数类型是下面这些类型的,也不会打印,比如:ServletRequest、ServletResponse,大家可以扩展private static List<Class<?>> noLogTypes = Arrays.asList(ServletRequest.class, ServletResponse.class);/*** 判断方法的返回值是否需要打印?方法上有 @NoLogAnnotation 注解的,表示结果不打印方法返回值*/private boolean resultIsLog(MethodSignature methodSignature) {return methodSignature.getMethod().getAnnotation(NoLogAnnotation.class) == null;}
}
测试

查询数据效果
在这里插入图片描述

http://www.dtcms.com/wzjs/801970.html

相关文章:

  • 网站框架指的是什么网页设计主题推荐
  • 惠州有没有做网站o2o网站建设好么
  • 洪梅镇做网站网站建设中图片
  • 大连做公司网站的公司做当地门户网站多少钱
  • 百度大数据分析新乡seo推广
  • 山东省建设厅官方网站怎么样男女做某事网站
  • 电子商务网站建设需要哪种语言乐至县建设局网站
  • 搭建公司介绍网站wordpress付费才能看
  • 环球资源网的网站特色app推广接单渠道
  • 汉中做网站的公司c 网站开发平台
  • lamp网站开发 pdf网站后台管理代码
  • asp网站检查公司怎么开网站
  • 网站商城运营模式安装wordpress 403
  • 物流公司 网站模板泉州营销型网站建设
  • 网站建设需要集齐哪5份资料做网站建设公司crm在线的提升服务
  • 中学生做网站的软件深圳企业网站制作公司介绍
  • 一个ip可以做几个网站怎么获取网站的图片
  • 青岛网站建设微信群铁路项目建设 网站
  • 企业网站推广方法有哪些调查网站做调查不容易过
  • 网站源码下载免费东莞网站建设效果
  • 微信小程序开发网站建设网站开发电商项目的成本管理怎么写
  • 有没有做试题的网站山东济宁网站建设设计
  • 网站报价方案 模板厦门百城建设有限公司网站
  • 公司建设网站带来什么湖南省建设信息网
  • 芜湖建设路小学网站为什么说能进中交不进中建
  • php做门户网站电子商务的网站开发
  • 如何避免网站被攻击中文网站建设计划书
  • 网站制作哪些分类北京网站建设w亿玛酷1订制
  • 闵行网站开发定制app软件开发
  • 成都购物网站建设杭州网站建设专家