ThreadLocal 在记录操作日志中的应用

  ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量

  在HandlerInterceptor的preHandle 中可以截取crud等操作的一些url

 

public class PlatformLogInterceptor implements HandlerInterceptor {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    UserService userService;

    @Autowired
    PlatformLogService platformLogService;

    @Autowired
    FunctionService functionService;
    @Override
    public boolean preHandle(HttpServletRequest httpRequest, HttpServletResponse response, Object handler) throws Exception {
        try{
            String types = httpRequest.getHeader("Accept");
            String uri = httpRequest.getRequestURI();//请求uri
            PlatformLog platformLog = new PlatformLog();
            if(uri.toLowerCase().matches("[\\s\\S]*((del)|(update)|(insert)|(add)|(publish)|(export)|(import)|(upload)|(create)|(process)|(audit))[\\s\\S]*")) {
                Function function = getFunction(uri);  //获取uri对应功能详情
                String userId =(String) httpRequest.getAttribute("token_userId");//userId
                String ipAddr = getIpAddr(httpRequest);// 用户ip地址
                User user = userService.findByUesrId(Long.valueOf(""+userId));
                /*************************设置日志项**************************/
                platformLog.setParamJson(JSONObject.fromObject(httpRequest.getParameterMap()).toString());
                if(function!=null){
                    platformLog.setMethodName(function.getFuncName());
                }else{
                    platformLog.setMethodName(uri);
                }
                platformLog.setOptionIp(ipAddr);
                platformLog.setOptionTime(new Date());
                platformLog.setOptionUser(Long.valueOf(userId));
                platformLog.setOptionUserGroupName(user.getGroupName());
                platformLog.setOptionUserName(user.getName());
                platformLog.setRelatedId(user.getUserId());
                platformLog.setTenantId(user.getTenantId());
                platformLog.setOrgId(user.getGroupId());
                PlatformLog.setPlatformLog(platformLog);// put it into threadLocal

            }
            return true;
        }catch (Exception e){
            PlatformLog.removePlatformLog();
            return true;
        }

    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // do nothing
    }

    @Override
    public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) throws Exception {
        // do nothing
    }
    private String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
    private Function getFunction(String funcURl){
        try {
            List<Function> functions = functionService.findAll();
            for(Function f:functions){
                if(f.getFuncURl().equals(funcURl)){
                    return f;
                }
            }
        } catch (Exception e) {
           // e.printStackTrace();
            return null;
        }
        return null;
    }

 spring配置

<bean id="wsLogInterceptor" class="。。。。。。.interceptor.PlatformLogInterceptor"/>
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <ref bean="wsLogInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

在这里不能直接去记录操作的platformLog  定义的platformlog 将要记录的日志放入ThreadLocal中 ThreadLocal调用get方法前 必须调用set方法

public class PlatformLog implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 1L;
    private static final ThreadLocal<PlatformLog> PLATFORM_LOG_THREAD_LOCAL = new ThreadLocal<>();
    private Integer id;
    private String methodName;//方法名
    private String paramJson;//参数json
    private String resultJson;//修改后的对象json
    private Long   relatedId;//相关ID
    private String relatedChar;//相关字符
    private Long   optionUser;//操作用户
    private String   optionIp;//操作Ip
    private String dataCode;//返回的code码
    private Date   optionTime;//操作时间
    private Long tenantId;//日志租户(获取操作用户)
    private Long orgId;//orgId

    private String   optionUserName;//操作用户名
    private String   optionUserGroupName;//操作用户组
    private List<Object> thParamName=new ArrayList<Object>();//表头名称
    private List<Object> tdParamData=new ArrayList<Object>();//值
    private List<Object> thResultName=new ArrayList<Object>();
    private List<Object> tdResulData=new ArrayList<Object>();//值
    private List<LogValue> logValues=new ArrayList<LogValue>();//值
    public String getOptionIp() {
        return optionIp;
    }
    public void setOptionIp(String optionIp) {
        this.optionIp = optionIp;
    }

    public String getOptionUserName() {
        return optionUserName;
    }
    public void setOptionUserName(String optionUserName) {
        this.optionUserName = optionUserName;
    }
    public String getOptionUserGroupName() {
        return optionUserGroupName;
    }
    public void setOptionUserGroupName(String optionUserGroupName) {
        this.optionUserGroupName = optionUserGroupName;
    }
    public List<LogValue> getLogValues() {
        return logValues;
    }
    public void setLogValues(List<LogValue> logValues) {
        this.logValues = logValues;
    }
    public List<Object> getThParamName() {
        return thParamName;
    }
    public void setThParamName(List<Object> thParamName) {
        this.thParamName = thParamName;
    }
    public List<Object> getThResultName() {
        return thResultName;
    }
    public void setThResultName(List<Object> thResultName) {
        this.thResultName = thResultName;
    }
    public List<Object> getTdParamData() {
        return tdParamData;
    }
    public void setTdParamData(List<Object> tdParamData) {
        this.tdParamData = tdParamData;
    }
    public List<Object> getTdResulData() {
        return tdResulData;
    }
    public void setTdResulData(List<Object> tdResulData) {
        this.tdResulData = tdResulData;
    }
    public Long getRelatedId() {
        return relatedId;
    }
    public void setRelatedId(Long relatedId) {
        this.relatedId = relatedId;
    }
    public String getRelatedChar() {
        return relatedChar;
    }
    public void setRelatedChar(String relatedChar) {
        this.relatedChar = relatedChar;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }

    public String getMethodName() {
        return methodName;
    }
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    public String getParamJson() {
        try {
            if(StringUtils.isEmpty(this.paramJson)&&this.logValues.size()>0){
                ObjectMapper mapper = new ObjectMapper();
                this.paramJson=mapper.writeValueAsString(this.logValues);
            }
        } catch (JsonProcessingException e) {
            Logger logger = LoggerFactory.getLogger(this.getClass());
            logger.error("method {} execute error,param:{} Exception:{}","setTranslationParam",logValues,e);

        }
        return paramJson;
    }

    public String getResultJson() {
        return resultJson;
    }

    /**
     * 该方法不提供对外调用,只提供数据库封装回调,要设置日志保存参数使用
     * setTranslationParam,setUpdateParam方法
     * @param paramJson
     */
    @SuppressWarnings("unchecked")
    public void setParamJson(String paramJson) {
        try {

            if(StringUtils.isEmpty(paramJson)){
                return;
            }
            ObjectMapper mapper = new ObjectMapper();
            JavaType type = mapper.getTypeFactory().constructParametricType(ArrayList.class,
                    LogValue.class);
            List<LogValue>    logValues = (List<LogValue>) mapper.readValue(paramJson, type);
            for (LogValue logValue : logValues) {
                this.thParamName.add(logValue.getName());
                this.tdParamData.add(logValue.getValue());
            }
        } catch (Exception e) {
            Logger logger = LoggerFactory.getLogger(this.getClass());
            logger.error("method {} execute error,param:{} Exception:{}","setParamJson",paramJson,e);
            this.paramJson = paramJson;
        }
        this.paramJson = paramJson;
    }
    /**
     * 该方法不提供对外调用,只提供数据库封装回调,要设置日志保存参数使用
     * setTranslationParam,setUpdateParam方法
     * @param paramJson
     */
    public void setResultJson(String resultJson) {
        try {
            if(StringUtils.isEmpty(resultJson)){
                return;
            }
            ObjectMapper mapper = new ObjectMapper();
            JavaType type = mapper.getTypeFactory().constructParametricType(ArrayList.class,
                    LogValue.class);
            List<LogValue>    logValues = (List<LogValue>) mapper.readValue(resultJson, type);
            for (LogValue logValue : logValues) {
                this.thResultName.add(logValue.getName());
                this.tdResulData.add(logValue.getValue());
            }
        } catch (Exception e) {
            Logger logger = LoggerFactory.getLogger(this.getClass());
            logger.error("method {} execute error,param:{} Exception:{}","setParamJson",paramJson,e);
            this.resultJson = resultJson;
        }
        this.resultJson = resultJson;
    }
    public Long getOptionUser() {
        return optionUser;
    }
    public void setOptionUser(Long optionUser) {
        this.optionUser = optionUser;
    }
    public String getDataCode() {
        return dataCode;
    }
    /**
     * 该方法回调使用
     * 设置日志回调结果用
     * setOptionResult
     * @param dataCode
     */
    public void setDataCode(String dataCode) {
        this.dataCode = dataCode;
    }
    public Date getOptionTime() {
        return optionTime;
    }
    public void setOptionTime(Date optionTime) {
        this.optionTime = optionTime;
    }
    public Long getTenantId() {
        return tenantId;
    }
    public void setTenantId(Long tenantId) {
        this.tenantId = tenantId;
    }

    /**
     * 除去修改删除以外设置调用参数的时候都是调用这个方法
     * @param atribute 属性字段
     * @param name     属性名称中文
     * @param value    属性值
     */
    public void setTranslationParam(String atribute,String name,Object value){
            LogValue logValue=new LogValue();
            logValue.setAtribute(atribute);
            logValue.setName(name);
            logValue.setValue(value);
            logValues.add(logValue);
    }
    /**
     * 修改或者删除操作需要保留日志的对象
     * old 和news对象的属性获取方法一定要设置名称注解
     * {@link Excel}
     * @param old 修改删除前的对象
     * @param news 修改后的对象,删除是设置为null
     */
    public void setUpdateParam(Object old,Object news){
        try {
            ObjectMapper mapper = new ObjectMapper();
            if(old!=null){
                this.paramJson=mapper.writeValueAsString(ClassObjectValueUtile.getFiledName(old));
            }
            if(news!=null){
                this.resultJson=mapper.writeValueAsString(ClassObjectValueUtile.getFiledName(news));
            }

        } catch (JsonProcessingException e) {
            Logger logger = LoggerFactory.getLogger(this.getClass());
            logger.error("method {} execute error,old:{} news:{} Exception:{}","setUpdateParam",old,news,e);

        }
    }
    /**
     * 设置操作日志的返回结果
     *
     * @param code
     */
  public void setOptionResult(Integer code){
      String codeName =PlatformErrorCode.codes.get(code);
      if(StringUtils.isEmpty(codeName)){
          codeName ="请求失败";
      }
      this.dataCode=PlatformErrorCode.codes.get(code);
  }
    /**
     * @return the orgId
     */
    public Long getOrgId() {
        return orgId;
    }
    /**
     * @param orgId the orgId to set
     */
    public void setOrgId(Long orgId) {
        this.orgId = orgId;
    }

    public static PlatformLog getPlatformLog(){
        return PLATFORM_LOG_THREAD_LOCAL.get();
    }
    public static void setPlatformLog(PlatformLog platformLog){
        PLATFORM_LOG_THREAD_LOCAL.set(platformLog);
    }
    public  static void removePlatformLog(){
        PLATFORM_LOG_THREAD_LOCAL.remove();
    }
}

最后在ResponseBodyAdvice 的实现类中记录操作日志

public class LogResponseBodyAdvice implements ResponseBodyAdvice<ResultDTO> {
    private Logger log = LoggerFactory.getLogger(this.getClass());
    @Autowired
    PlatformLogService platformLogService;
    static ObjectMapper mapper = new ObjectMapper();
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;//do nothing
    }

    @Override
    public ResultDTO beforeBodyWrite(ResultDTO body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        PlatformLog platformLog = PlatformLog.getPlatformLog();
        if (platformLog==null){
            return body;
        }
        try {
            platformLog.setResultJson(mapper.writeValueAsString(body));
            platformLog.setDataCode(""+body.getCode());
            PlatformLog.removePlatformLog();
            platformLogService.insert(platformLog);
        } catch (Exception e) {
            e.printStackTrace();
            PlatformLog.removePlatformLog();
            log.error("err method: wsLogResponseBodyAdvice.beforeBodyWrite, Exception ", e);
            return body;
        }
        return body;
    }
}

就这样吧 第一次写 好渣

时间: 2024-10-07 18:58:53

ThreadLocal 在记录操作日志中的应用的相关文章

Tomcat会话超时时怎样记录操作日志,满足安全审计要求

众所周知.在实际的Web应用程序中,会话管理一般都採用Web容器会话管理功能. 使用Tomcat做Webserver也是如此,并且从安全的角度考虑,尽量避免去更改和干预Web容器的会话管理功能. Tomcat会话管理功能肯定比我们自己做出来要全面和可靠,况且Tomcat是主流开源社区维护的.有专门的团队来开发和维护.一旦爆出安全漏洞,也能非常快被修复. 在实际开发中,为了满足安全审计的要求.Web应用程序一旦有会话注销.就应该记录操作日志.注销一般分为操作者主动注销.应用程序检測到异常攻击主动注

MVC 记录操作日志与过滤特殊字符

最近进行的MVC系统需要用到记录操作日志和过滤特殊字符的功能,如果每个action中都调用记录日志的方法就太麻烦了,所以根据需要结合mvc的过滤机制 写了个特殊字符验证与记录操作日志的公用类: 1 public class CustomFilterAttribute : ActionFilterAttribute 2 { 3 public CustomFilterAttribute() 4 { 5 IsLog = false; 6 FilterSpecialChar = true; 7 } 8

Tomcat会话超时时如何记录操作日志,满足安全审计要求

众所周知,在实际的Web应用程序中,会话管理一般都采用Web容器会话管理功能. 使用Tomcat做Web服务器也是如此,而且从安全的角度考虑,尽量避免去更改和干预Web容器的会话管理功能. Tomcat会话管理功能肯定比我们自己做出来要全面和可靠,况且Tomcat是主流开源社区维护的,有专门的团队来开发和维护,一旦爆出安全漏洞,也能很快被修复. 在实际开发中,为了满足安全审计的要求,Web应用程序一旦有会话注销,就应该记录操作日志,注销一般分为操作者主动注销.应用程序检测到异常攻击主动注销会话.

Spring boot学习(六)Spring boot实现AOP记录操作日志

前言 在实际的项目中,特别是管理系统中,对于那些重要的操作我们通常都会记录操作日志.比如对数据库的CRUD操作,我们都会对每一次重要的操作进行记录,通常的做法是向数据库指定的日志表中插入一条记录.这里就产生了一个问题,难道要我们每次在 CRUD的时候都手动的插入日志记录吗?这肯定是不合适的,这样的操作无疑是加大了开发量,而且不易维护,所以实际项目中总是利用AOP(Aspect Oriented Programming)即面向切面编程这一技术来记录系统中的操作日志. 日志分类 这里我把日志按照面向

记录到日志中的异常栈缺失

项目中有这样一个场景: 使用反射处理所有业务调用,在反射调用点使用try-catch集中处理异常,并将异常信息记录到日志.其中日志记录是异步的. 问题: 记录到日志中的异常的StackTrace和有时候和Debug时抛出的异常的StackTrace不一样. 原因: 由于记录日志是异步的,如果记录日志发生在throw之前,记录到日志中的异常的StackTrace就是正确的(异常真正发生点到throw点的所有StackTrace):如果记录日志发生在throw之后,记录到日志中的异常的StackTr

Spring aop 记录操作日志 Aspect

前几天做系统日志记录的功能,一个操作调一次记录方法,每次还得去收集参数等等,太尼玛烦了.在程序员的世界里,当你的一个功能重复出现多次,就应该想想肯定有更简单的实现方法.于是果断搜索各种资料,终于搞定了,现在上代码 环境: SpringMvc + myBatis jar包 :      (aspect.jar也行,我原来项目中有,便没有替换了) 1.自定义注解类   ArchivesLog.java(获取Controller描述用的) package com.noahwm.uomp.archive

自定义日志注解 + AOP实现记录操作日志

需求:系统中经常需要记录员工的操作日志和用户的活动日志,简单的做法在每个需要的方法中进行日志保存操作, 但这样对业务代码入侵性太大,下面就结合AOP和自定义日志注解实现更方便的日志记录 首先看下一个简单的操作日志表 action_log id subject(日志主题) content(日志内容) create_by create_time 日志主题可以用下面的枚举类来实现 package cn.bounter.common.model; /** * 应用日志主题枚举类 * @author si

通用权限管理系统记录登录日志中的一个问题

最近,在使用通用权限管理系统开发一个项目,数据库是MSSQL,登录时出现一个错误,发现是记录登录日志时出现的错误. 由于每次登录,都会记录登录日志,就是在记录日志时出现了错误. 先把错误截图 可以看出与记录登录日志的表BaseLoginLog的ID有关系,打开该表 发现该表的ID是nvarchar(50)类型,查询一下该表的数据,截图如下 可看到,ID实际存储的是GUID类型数据,然后我在调试底层代码,找到三个地方: 1 /// <summary> 2 /// 记录登录日志 3 /// <

salt-api return mysql返回的使用,记录操作日志

说在前面 折腾这个搞了半天,现做下记录 安装依赖(操作只在master端) yum install mysql-python or pip install mysql-python master端本地数据库中创建对应的表结构 CREATE DATABASE `salt` DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci; USE `salt`; -- -- Table structure for table `jids` --