精华内容
下载资源
问答
  • java spring 记录用户增删改操作日志

    千次阅读 2017-01-06 18:09:19
    在数据库中建立操作记录(方式一)  建立操作记录(方法二)  使用LOG4J,通过配置LOG4J来获取业务日志(Apache Log4j)  用触发器生成SQL Server2000数据表的操作日志  基于拦截器的操作日志保存方式  ...
    在数据库中建立操作记录(方式一) 
    
    建立操作记录(方法二) 
    使用LOG4J,通过配置LOG4J来获取业务日志(Apache Log4j) 
    用触发器生成SQL Server2000数据表的操作日志 
    基于拦截器的操作日志保存方式 
    Struts2拦截器(自定义拦截器) 
    Spring结合注解和反射利用拦截器实现对日志记录 
    Hibernate3拦截器 

    在数据库中建立操作记录(方式一)  




    这种方式主要是在每个方法中设置静态常量String 相当于对方法的注释,并建立数据库日志表,用户登陆ERP系统并进行操作时,读取用户登录信息Session,同方法中的静态常量,和时间一并写入数据库的日志表中,由此来达到建立ERP系统操作日志的目的 

    缺点:此方法较为比较麻烦需要在每个方法中定义静态常量,并且影响ERP系统的工作效率,当一个操作执行多次时ERP多次读取用户信息Sessions,为TOMCAT增加较高的负荷,但思路简单,代码量大,实现方便。也可以考虑将信息写入到XML或者TXT文档中去。 

    建立操作记录(方法二) 

    在SSH环境下,将数据库操作事务交给Spring管理 
    1、尽量使用注解声明事务; 

    2、写一个类扫描使用了事务的方法。根据楼主提出的技术需求分析,只有写入动作才需要记录,同样数据库只有写入才需要事务,读取不需要,所以在不需要事务的方法上面标注@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),这样可以精确过滤出需要记录日志的方法; 

    3、利用AOP编程实现日志记录功能。 
    时间:AOP切入点处取系统时间 
    操作员和IP:控制层在session作用域里取得用户对象和request取IP地址传给切入点 

    操作:可以在操作数据库的DAO组件上(方法)用自定义注解标上,例如:@Operation=INSERT|DELETE|UPDATE...只要读取到这个配置就知道操作类型。当然也可以利用Hibernate来得知,得要看Hibernate的源代码。 

    结果:事务成功即成功,事务回滚即失败。 

    业务数据ID:这个有两种解决方法,一种笨拙的办法是所有的数据模型层的实体对象都抽取ID到父类;二是实体映射的配置方法采用注解方式,这样可以写一个类扫描出实体的ID字段和类型,自然能记录之。 

    粒度问题:首先只要应用到事务的地方都可以记录之,批量操作数据行实际是循环调用DAO组件,也就实现了批量记录。当然,如果送批量SQL语句到数据库,由DBMS来做那就无奈了。 

    这种方案设计可以一次编写,终身使用,不受项目的模块增减影响。代码量小,维护容易。 

    使用LOG4J,通过配置LOG4J来获取业务日志(Apache Log4j) 

    用触发器生成SQL Server2000数据表的操作日志 
    有时,我们想知道登录到数据库的用户做了什么,于是,记录用户执行的SQL语句就非常有必要,这将是重要的参考依据。我们先建一张日志表(DBLoger)用于保存用户执行的SQL语句: 


    程序代码 
    Create TABLE DBLoger( 
        LoginName nvarchar(50), 
        HostName nvarchar(50), 
        EventInfo nvarchar(500), 
        Parameters int, 
        EventType nvarchar(100) 


    接着再建一个触发器,在用户对表进行增/删/改时触发,将执行的SQL语句记录到日志表中: 


    程序代码 
    Create TRIGGER Loger ON student 
    FOR Insert, Update, Delete 
    AS 
        SET NOCOUNT ON 
        
        Create TABLE #T(EventType nvarchar(100),Parameters int,EventInfo nvarchar(500)) 
        Insert #T exec('dbcc inputbuffer(' + @@spid + ')') 
        --记录到日志表 
        Insert INTO DBLoger(LoginName,HostName,EventInfo,Parameters,EventType) Select suser_sname(),host_name(),EventInfo,Parameters,EventType FROM #T 

    缺点:由于dbcc inputbuffer的EventInfo最多只能保存255个字符,所以一旦执行的SQL过长,日志表中将无法看到完整的SQL语句! 

    基于拦截器的操作日志保存方式 
    Struts2拦截器(自定义拦截器) 
    Struts2的内置拦截器只能完成一些通用功能,若要使用拦截器捕获业务信息,则需要自定义拦截器进行操作 
    例:Log日志的数据库保存。即使用Struts2拦截器的intercept方法,在方法里直接把操作记录保存到数据库中,而这时计算出的查询时间则是整个查询过程的时间,即读取用户输入后,进行判断,并进行查询的时间,这种计算方法比之以前更加合理,因为查询本身就包含判断,如果仅仅只是查询数据库那一个动作,在复杂的查询里并不能体现出整个查询所花的时间,而用拦截器,则相对轻松的解决了此问题。在随后显示的时间里,查询耗时确定比以前有了很大的延长。 
    如果达到与具体action无关,只与用户有关的话,那么这个拦截器很容易实扩展到在项目中的任何地方保存用户操作记录。 

    Spring结合注解和反射利用拦截器实现对日志记录 
    现今的框架都很灵活,对于日志的记录也有很多种方式,关键看很么样的方式更高效,更能体现系统的灵活和松散耦合。之前日志记录采用log4j的JDBCAppender,虽然配置一样很灵活,但是相对后来基于spring aop上的实现觉得还是比较复杂。比如我的表名是动态的,字段内容或则字段名字是动态的,这些配置对于log4j来说多了很多难度,而且还要管理缓存中的属性值。再则log4j没有实现jdbc的pool管理,虽然提供了相应接口。最终决定才用spring拦截器,又高效又灵活。对于数据库的操作同样还是使用自己的底层。 
    源码和配置记录: 

    package com.cmcc.common.controller.interceptor; 
    import java.lang.reflect.Method; 
    import java.util.Date; 
    import org.apache.log4j.Logger; 
    import org.aspectj.lang.ProceedingJoinPoint; 
    import com.cmcc.common.Global; 
    import com.cmcc.common.cache.PoolConfigInfoMap; 
    import com.cmcc.common.controller.interceptor.annotations.GenericLogger; 
    import com.cmcc.common.util.UserSessionObj; 
    import com.cmcc.framework.business.interfaces.corporation.ICompanyInfoManager; 
    import com.cmcc.framework.business.interfaces.log.IOperLogManager; 
    import com.cmcc.framework.model.log.OperateLog; 
    /** 

    * 记录系统日志的拦截器 

    * @author <a href="mailto:sun128837@126.com">conntsing</a> 

    * @version $Revision: 1.5 $ 

    * @since 2009-3-23 
    */ 
    public class GenericLoggerInterceptor { 
        /** 
         * Logger for this class 
         */ 
        private static final Logger logger = Logger 
                .getLogger(GenericLoggerInterceptor.class); 
        @SuppressWarnings("unchecked") 
        public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable { 
            if (logger.isDebugEnabled()) { 
                logger.debug("invoke(ProceedingJoinPoint) - start"); //$NON-NLS-1$ 
            } 
            try { 
                Object result = joinPoint.proceed(); 
                Class cl = joinPoint.getTarget().getClass(); 
                Method[] methods = cl.getMethods(); 
                GenericLogger genericLogger = null; 
                UserSessionObj userSessionObj = null; 
                String departmentNames = ""; 
                String employeeNames = ""; 
                for (Method m : methods) { 
                    if (m.getName().equals(joinPoint.getSignature().getName())) { 
                        genericLogger = m.getAnnotation(GenericLogger.class); 
                    } 
                    if (m.getName().equalsIgnoreCase("getusersessioninfo")) { 
                        userSessionObj = (UserSessionObj) m.invoke(joinPoint 
                                .getTarget()); 
                    } 
                } 
                OperateLog opLog = new OperateLog(); 
                if (null != genericLogger) { 
                    if (genericLogger.isOperateDepartment()) { 
                        for (Method m : methods) { 
                            if (m.getName().equalsIgnoreCase("getdepartmentnames")) { 
                                departmentNames = (String) m.invoke(joinPoint 
                                        .getTarget()); 
                                break; 
                            } 
                        } 
                    } 
                    if (genericLogger.isOperateEmployee()) { 
                        for (Method m : methods) { 
                            if (m.getName().equalsIgnoreCase("getemployeenames")) { 
                                employeeNames = (String) m.invoke(joinPoint 
                                        .getTarget()); 
                                break; 
                            } 
                        } 
                    } 
                    ICompanyInfoManager companyservice = (ICompanyInfoManager) Global._ctx 
                            .getBean("companyservice"); 
                    opLog.setEid(userSessionObj.getEid()); 
                    opLog.setAdminId(userSessionObj.getId()); 
                    opLog.setAdminName(userSessionObj.getLoginId()); 
                    opLog.setC0(userSessionObj.getCode().toString()); 
                    opLog.setOperateDesc(genericLogger.operateDescription()); 
                    opLog.setOperateTime(new Date(System.currentTimeMillis())); 
                    opLog.setShortName(companyservice.get( 
                            userSessionObj.getEid(), 
                            PoolConfigInfoMap.get(userSessionObj.getEid()) 
                                    .getPhysical_pool_id()).getShortName()); 
                    if (null != employeeNames && null != departmentNames) { 
                        if (employeeNames.equals("") && departmentNames.equals("")) { 
                            opLog.setDeptName(opLog.getShortName()); 
                        } 
                        else { 
                            opLog.setEmployeeName(employeeNames); 
                            opLog.setDeptName(departmentNames); 
                        } 
                    } 
                    else { 
                        opLog.setDeptName(opLog.getShortName()); 
                    } 
                    IOperLogManager logmanage = (IOperLogManager) Global._ctx 
                            .getBean("operLogManager"); 
                    logmanage.saveOperateLog(opLog); 
                } 
                if (logger.isInfoEnabled()) { 
                    logger.info("OperateLog------adminid:    " + opLog.getAdminId()); 
                    logger.info("OperateLog--operatedesc:    " + opLog.getOperateDesc()); 
                    logger.info("OperateLog--operatetime:    " + opLog.getOperateTime()); 
                    logger.info("OperateLog----shortName:    " + opLog.getShortName()); 
                } 
                return result; 
            } 
            catch (Exception e) { 
                e.printStackTrace(); 
                logger.warn("invoke(ProceedingJoinPoint) - exception ignored", e); //$NON-NLS-1$  
            } 
            finally { 
            } 
            if (logger.isDebugEnabled()) { 
                logger.debug("invoke(ProceedingJoinPoint) - end"); //$NON-NLS-1$ 
            } 
            return null; 
        } 

    <aop:config  proxy-target-class="true"> 
          <aop:pointcut id="loggerService" expression="execution(* com.cmcc.framework.controller.action..*.*(..)) and 
                        @annotation(com.cmcc.common.controller.interceptor.annotations.GenericLogger)"/> 
          <aop:aspect ref="genericLoggerInterceptor"> 
              <aop:around pointcut-ref="loggerService" method="invoke"/> 
          </aop:aspect> 
        </aop:config> 
        <bean id="genericLoggerInterceptor" class="com.cmcc.common.controller.interceptor.GenericLoggerInterceptor"/> 
    像我这里会有个分表分pool区的配置处理,如果是之前我还得交给log4j去动态的传入表名参数,而且还得重新实现appender。现在我只需要拦截器拦截我匹配表达式的方法,并使用注解传入相应描述,再依赖注入反射获取相应的描述字段值,一切都很灵活配置也很简单。 
    实际上,我拦截的方法只在controller层,所以同样可以通过实现struts2拦截器(同样支持注解)来拦截方法记录日志,不过使用spring拦截器更灵活易于扩展。 

        
    Hibernate3拦截器 
        技术选型: 
    最土的,在所有的Dao方法中显示的编写日志记录代码 
    该项目以前是用.net这么干的,这种做法重复工作量太大,维护性差,并且也没实现字段级变更的记录,根本不予考虑。 
    数据库触发器 - 与数据库耦合 
    与数据库耦合,违背了使用hibernate的初衷,也不予考虑 
    原生的Hibernate Interceptor 
    优点:可以在hibernate对象操作的时候获取最为详细的运行期信息,字段名,原始值,修改后值等等。 
    缺点:在JPA  API的封装下很难获取到hibernate的session,不能进行持久化操作。 
    JPA callback / event-listener 
    优点:JPA规范,最为优雅简单 
    缺点:功能太弱,不能满足需求 
    很自然地,干这种事AOP似乎比较合适 
    优点:灵活,在spring容器中,可以访问所有spring bean 
    缺点:不能获取详细的运行期信息(字段名,原始值,等等),无法感知hibernate的事务执行,即使dao事务rollback,仍然会插入一条操作历史记录,破坏了“操作”和“历史”的一致性。 
    采用Hibernate 3的新特性 Event-listener 
    可以解决以上所有问题 
    能够取得运行期详细信息,除了能记录粗粒度的实体的保存删除操作外,还能精确追踪对实体字段修改、实体关联/级联关系的变更,能记录更新前的值、更新后的值,可以生成详细日志。 
    灵活解耦,跨数据库,不影响原有代码。 
        Hibernate3 新特性事件处理框架是hibernate 2拦截器的一个补充或者替代,由拦截器被动拦截操作事件变成事件源的主动驱动,这是一个进步。Hibernate 事件框架官方文档. 
        Hibernate3中定义了很多的事件,涵盖了持久化过程中不同的生命周期。简单说Session的一个方法(load, flush...)分别对应一个事件,当该方法被调用时,就会触发一个相应的事件,这个事件会被我们预先定义的事件监听器收到,再进行相应的处理。这种方式来做审计日志是再适合不过。 
       但也有个缺点就是这样的Event-listener是脱离主容器(比如Spring IoC环境)单独实例化的,无法访问主容器的资源(比如要取得当前登录的用户信息就会比较麻烦)。这个暂时还没解决。 

    在这里我们选取PostInsertEventListener(插入后事件),PostUpdateEventListener(更新后事件),PostDeleteEventListener(删除后事件)接口作为CRUD方法的监听接口。hibernate3中事件是分为pre和post,表示该发生事件前、后。这里我们全部用Post,因为PostEvent只有在数据实际改变后才会触发,假如CRUD事务因为异常回滚,则不会触发事件。 

    首先定义一个mark接口Historiazable,实现该接口的entity类表明是需要做审计日志的。 
    然后编写我们自定义的EventListener类,实现上述的事件接口。 
    在事件接口实现方法里在根据不同的事件编写审计日志的代码。 
    Java代码 
    public class HistoryListener implements PostInsertEventListener,    
            PostUpdateEventListener, PostDeleteEventListener {    
          
        @Override   
        public void onPostInsert(PostInsertEvent event) {    
            if (event.getEntity() instanceof Historizable) {    
            //  保存 插入日志    
            }    
        }    
       
        @Override   
        public void onPostUpdate(PostUpdateEvent event) {    
            if (event.getEntity() instanceof Historizable) {    
            // 保存 修改日志    
            }    
        }    
       
        @Override   
        public void onPostDelete(PostDeleteEvent event) {    
            if (event.getEntity() instanceof Historizable) {    
            // 保存 删除日志    
            }    
        }    
    }   



    配置EventListener 
    编辑hibernate.cfg.xml,配置监听器 
    Xml代码 
    <session-factory>   
        <listener type="post-insert" class="net.jeffrey.hibernate.history.HistoryListener"/>   
        <listener type="post-update" class="net.jeffrey.hibernate.history.HistoryListener"/>   
        <listener type="post-delete" class="net.jeffrey.hibernate.history.HistoryListener"/>   
    </session-factory>   

    配置持久化单元 
    在persistence.xml中加入 
    <property name="hibernate.ejb.cfgfile" value="hibernate.cfg.xml"/> 
    这样JPA环境启动后,就会正确装载初始化自定义的事件监听器。 
    个人认为:第一种方式有效率问题但是实现简单,第二种方式实现上和效率上都比较优秀,第三种基于触发器的操作日志实现方案由SQLServer2000触发处理不影响页面响应效率,这三种方案较为提倡,LOG4J有待了解具体没有实用过。
    展开全文
  • springboot项目使用切面记录用户操作日志

    万次阅读 多人点赞 2019-01-09 16:55:59
    * @Description 前置通知 用于拦截Controller层记录用户操作 * @date 2018年9月3日 10:38 */ @Before("controllerAspect()") public void doBefore(JoinPoint joinPoint){ HttpServletRequest request = (...

    1、引入springboot-aop集成jar

              Spring-boot-start-aop

    2、application.yml中启用声明

    #spring配置
    spring:
      #切面启用
      aop:
        proxy-target-class: true
        auto: true

    3、自定义一个拦截controller的注解

    package cn.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * Title: SystemControllerLog
     * @date 2018年8月31日
     * @version V1.0
     * Description:  自定义注解,拦截controller
     */
    
    @Target({ElementType.PARAMETER, ElementType.METHOD})//作用在参数和方法上
    @Retention(RetentionPolicy.RUNTIME)//运行时注解
    @Documented//表明这个注解应该被 javadoc工具记录
    public @interface SystemControllerLog {
        String description() default "";
    }
    
    4、自定义一个拦截service的注解
    
    package cn.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * Title: SystemControllerLog
     * @date 2018年8月31日
     * @version V1.0
     * Description:  自定义注解,拦截service
     */
    @Target({ElementType.PARAMETER, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface SystemServiceLog {
        String description() default "";
    }

     

    5、定义日志记录切面

    package cn.annotation;
    
    import cn.pojo.Action;
    import cn.pojo.User;
    import cn.service.ActionService;
    import cn.utils.IpUtils;
    import cn.utils.JsonUtils;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    import java.lang.reflect.Method;
    import java.util.Date;
    
    /**
     * Title: SystemControllerLog
     * @date 2018年8月31日
     * @version V1.0
     * Description: 切点类
     */
    @Aspect
    @Component
    @SuppressWarnings("all")
    public class SystemLogAspect {
        //注入Service用于把日志保存数据库,实际项目入库采用队列做异步
        @Resource
        private ActionService actionService;
        //本地异常日志记录对象
        private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
        //Service层切点
        @Pointcut("@annotation(cn.annotation.SystemServiceLog)")
        public void serviceAspect(){
        }
    
        //Controller层切点
        @Pointcut("@annotation(cn.oa.annotation.SystemControllerLog)")
        public void controllerAspect(){
        }
    
        /**
         * @Description  前置通知  用于拦截Controller层记录用户的操作
         * @date 2018年9月3日 10:38
         */
    
        @Before("controllerAspect()")
        public void doBefore(JoinPoint joinPoint){
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            HttpSession session = request.getSession();
            //读取session中的用户
            User user = (User) session.getAttribute("user");
    
            String ip = IpUtils.getIpAddr(request);
    
            try {
                //*========控制台输出=========*//
                System.out.println("==============前置通知开始==============");
                System.out.println("请求方法" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName()));
                System.out.println("方法描述:" + getControllerMethodDescription(joinPoint));
                System.out.println("请求人:"+user.getUsername());
                System.out.println("请求ip:"+ip);
    
                //*========数据库日志=========*//
                Action action = new Action();
                action.setActionDes(getControllerMethodDescription(joinPoint));
                action.setActionType("0");
                action.setActionIp(ip);
                action.setUserId(user.getId());
                action.setActionTime(new Date());
                //保存数据库
                actionService.add(action);
    
            }catch (Exception e){
                //记录本地异常日志
                logger.error("==前置通知异常==");
                logger.error("异常信息:{}",e.getMessage());
            }
        }
    
        /**
         * @Description  异常通知 用于拦截service层记录异常日志
         * @date 2018年9月3日 下午5:43
         */
        @AfterThrowing(pointcut = "serviceAspect()",throwing = "e")
        public void doAfterThrowing(JoinPoint joinPoint,Throwable e){
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            HttpSession session = request.getSession();
            //读取session中的用户
            User user = (User) session.getAttribute("user");
            //获取请求ip
            String ip = IpUtils.getIpAddr(request);
            //获取用户请求方法的参数并序列化为JSON格式字符串
            String params = "";
            if (joinPoint.getArgs()!=null&&joinPoint.getArgs().length>0){
                for (int i = 0; i < joinPoint.getArgs().length; i++) {
                    params+= JsonUtils.objectToJson(joinPoint.getArgs()[i])+";";
                }
            }
            try{
                /*========控制台输出=========*/
                System.out.println("=====异常通知开始=====");
                System.out.println("异常代码:" + e.getClass().getName());
                System.out.println("异常信息:" + e.getMessage());
                System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
                System.out.println("方法描述:" + getServiceMethodDescription(joinPoint));
                System.out.println("请求人:" + user.getUsername());
                System.out.println("请求IP:" + ip);
                System.out.println("请求参数:" + params);
                /*==========数据库日志=========*/
                Action action = new Action();
                action.setActionDes(getServiceMethodDescription(joinPoint));
                action.setActionType("1");
                action.setUserId(user.getId());
                action.setActionIp(ip);
                action.setActionTime(new Date());
                //保存到数据库
                actionService.add(action);
            }catch (Exception ex){
                //记录本地异常日志
                logger.error("==异常通知异常==");
                logger.error("异常信息:{}", ex.getMessage());
            }
        }
    
    
        /**
         * @Description  获取注解中对方法的描述信息 用于service层注解
         * @date 2018年9月3日 下午5:05
         */
        public static String getServiceMethodDescription(JoinPoint joinPoint)throws Exception{
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = joinPoint.getArgs();
    		Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            String description = "";
            for (Method method:methods) {
                if (method.getName().equals(methodName)){
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length==arguments.length){
                        description = method.getAnnotation(SystemServiceLog.class).description();
                        break;
                    }
                }
            }
            return description;
        }
    
    
    
        /**
         * @Description  获取注解中对方法的描述信息 用于Controller层注解
         * @date 2018年9月3日 上午12:01
         */
        public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();//目标方法名
            Object[] arguments = joinPoint.getArgs();
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            String description = "";
            for (Method method:methods) {
                if (method.getName().equals(methodName)){
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length==arguments.length){
                        description = method.getAnnotation(SystemControllerLog.class).description();
                        break;
                    }
                }
            }
            return description;
        }
    }

    6、开始使用

        1)@SystemControllerLog(description = "")

              注解加在控制器中方法上面,括号里写上操作描述

     

        2)用于监控service异常,可以不使用

            @SystemServiceLog(description = "")

            注解加在service层方法上面,括号里写上操作描述

    (此处为AOP拦截Service记录异常信息。方法不需要加try-catch)

    展开全文
  • java记录日志

    2014-01-22 10:26:43
    java多线程异步的记录日志(可以精确到具体哪个字段发生了改变,可以记录到提交的所有参数,也可以将抛出的异常和提交参数记录到文件)
  • 之前要做一个记录用户操作日志记录,找了很多方法,最后选择使用spring AOP来实现。由于是要记录用户操作日志,所以我使用的是返回通知(@AfterReturning),只有在前端调用了我后端的接口并成功返回,才调用我...

    之前要做一个记录用户操作的日志记录,找了很多方法,最后选择使用spring AOP来实现。由于是要记录用户操作的日志,所以我使用的是返回通知(@AfterReturning),只有在前端调用了我后端的接口并成功返回,才调用我的切面方法记录用户的操作存储到数据库中。

    LogAnnotation.java

    import java.lang.annotation.ElementType;  
    import java.lang.annotation.Retention;  
    import java.lang.annotation.RetentionPolicy;  
    import java.lang.annotation.Target;
    
    import org.springframework.data.mongodb.core.mapping.Document; 
    
    /**
     * 日志
     * @author **
     *
     */
    @Target({ElementType.PARAMETER,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Document
    public @interface LogAnnotation {
    	
        /** 日志描述 */
        String description() default "";
     
        /** 业务类型 */
        int bizID();
    } 

    切面类

    LogAspect.java

    @Aspect
    @Component
    @SuppressWarnings({ "unchecked", "unused", "rawtypes" })
    public class LogAspect {
    	// 注入service,用来将日志信息保存在数据库 这是我的service,你只需要注入你自己的service就行
    	@Resource
    	private OprLogDaoService oprLogDaoService;
    
        //在**处填入你的LogAnnotation所在的包
        //此代码是拦截所有使用了LogAnnotation注解的接口
    	@Pointcut("@annotation(**.LogAnnotation)")
    	// 定义一个切点
    	private void accAspect() {
    	}
    
        //返回通知
        @AfterReturning(pointcut= "accAspect()",returning="result")
    	public void around(JoinPoint joinPoint, Object result) throws Throwable {
    
    		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
    				.getRequest();
    		String classType = joinPoint.getTarget().getClass().getName();
    		Class<?> clazz = Class.forName(classType);
    		String clazzName = clazz.getName();
    		// 拦截的方法名称。当前正在执行的方法
    		String methodName = joinPoint.getSignature().getName();
    		// 获取方法的参数
    		Object[] args = joinPoint.getArgs();
    		
    
    		// 获取传入参数的键值对
    		Map<String, Object> map = (Map<String, Object>)         
            LogUtil.getFieldsName(this.getClass(), clazzName, methodName,
    				args);
    		// 将request中的参数转化为键值对,方便取出
    		Map<String, Object> map2 = (Map<String, Object>) map.get("request");
    		String resultArgs = result.toString();
    		// 获取返回方法的参数
    		JSONObject jasonObject = JSONObject.fromObject(resultArgs);
    		Map mapResult = (Map) jasonObject;
    		//将返回的result参数取出
    		Map<String, Object> res = (Map<String, Object>) mapResult.get("result");
            //这样传入的参数和返回的参数都已变成Map对象
            //通过直接 .get("字段名")的方式将对应的字段值取出
            //比如取出用户的用户名ID
            Integer userID = (Integer) mapResult.get("userID");
    		
    			// 常见日志实体对象
    			OprLog oprLog = new OprLog();
                
                //根据自己定义的实体类的属性将数据填入
                OprLog.setUserID(userID);
                OprLog.set...();        
    			// 保存进数据库
                //开头定义的service,这边使用你自己的service就行了
    			oprLogDaoService.addLog(oprLog);
    		
    	}

    接下来就是在controller上添加注解了

    只需要在接口上添加注解@LogAnnotation(description = "日志记录", bizID = 1)  bizID是我自己定义的你也可以定义成String类型,写上方法名。

    @LogAnnotation(description = "日志记录", bizID = 1)
    @RequestMapping(value = "/Test", method = RequestMethod.POST)

    获取自定义注解内容的方法

    可以直接在注解上写用户的操作内容(修改、添加之类的)

    /**
    	 * 获取注解内容
    	 * @param joinPoint
    	 * @return
    	 * @throws Exception
    	 */
    	public static Integer getMthodRemark(JoinPoint joinPoint)  
                throws Exception {  
            String targetName = joinPoint.getTarget().getClass().getName();  
            String methodName = joinPoint.getSignature().getName();  
            Object[] arguments = joinPoint.getArgs();  
      
    		Class targetClass = Class.forName(targetName);
    		Method[] method = targetClass.getMethods();
    		Integer bizId = null;//此处可以根据你想要的类型来修改
    		for (Method m : method) {  
                if (m.getName().equals(methodName)) {  
                    Class[] tmpCs = m.getParameterTypes();  
                    if (tmpCs.length == arguments.length) {  
                        ControllerLogAnnotation cla = m.getAnnotation(ControllerLogAnnotation.class);  
                        if (cla != null) {  
                            bizId = cla.bizID();  
                        }  
                        break;  
                    }  
                }  
            }  
            return bizId;  
        }  
    }

    另外还有一些工具类的方法

    这个是转换成map对象的方法

    	public static Map<String, Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args)throws Exception {
    		Map<String, Object> map = new HashMap<String, Object>();
    
    		ClassPool pool = ClassPool.getDefault();
    		ClassClassPath classPath = new ClassClassPath(cls);
    		pool.insertClassPath(classPath);
    
    		CtClass cc = pool.get(clazzName);
    		CtMethod cm = cc.getDeclaredMethod(methodName);
    		MethodInfo methodInfo = cm.getMethodInfo();
    		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
    		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
    		if (attr == null) {
    			// exception
    		}
    		int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
    		for (int i = 0; i < cm.getParameterTypes().length; i++) {
    			map.put(attr.variableName(i + pos), args[i]);// paramNames即参数名
    		}
    		return map;
    	}

    最后在springMVCxml中加入

    <context:component-scan
            //LogAspect 在aop中
    		base-package="切面所在的包例如(a.b.aop)"></context:component-scan>
    
    	<aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy>

    另外还有一些依赖的jar包,需要的jar包都可以在百度中搜索到,用的时候没有可以在百度查询jar包依赖,放在maven项目中的pom.xml文件中。

     

     

    PS:后续发现使用事物对接口进行处理更加方便,直接在操作的时候添加日志,接口报错事物自动回滚即可。

    展开全文
  • 项目中需要对用户的各种操作做详细的操作日志记录,需要记录用户操作操作模块、具体操作以及操作的数据记录ID等。 若写一个方法去保存操作,则需要每次手动去调用。由于是非业务性的操作,并且大量的重复操作,...

    一、背景

        项目中需要对用户的各种操作做详细的操作日志记录,需要记录用户操作的操作模块、具体操作以及操作的数据记录ID等。
        若写一个方法去保存操作,则需要每次手动去调用。由于是非业务性的操作,并且大量的重复操作,Spring AOP就能很好的解决这个问题。
         由于用户操作的实现方法并不在同一个类中,而且每个操作的说明也不一样,所以用自定义注解作为切入点,来记录用户不同操作及其操作说明。

     二、配置

    2.1、导入jar包

    • spring-aop.jar
    • aspectjrt.jar
    • aspectjweaver.jar
    • aopalliance-1.0.jar

    Spring AOP相关jar包下载

    2.2、Spring的ApplicationContext.xml配置文件

        2.2.1 配置头文件

    xmlns:aop="http://www.springframework.org/schema/aop"
    <!-- 在xsi:schemaLocation中添加 -->
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"

    注:若没添加则会报 ==》
        java错误-The prefix "aop" for element "aop:aspectj-autoproxy" is not bound.


        2.2.2 配置注解扫描和AOP自动代理

    <!-- 配置组件扫描功能 -->
    <context:component-scan base-package="com.test"/>
    <!-- 配置自动代理功能 -->
    <aop:aspectj-autoproxy />
    <aop:config proxy-target-class="true"></aop:config>
    

    三、创建自定义注解

    package com.test.common.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    
    /**
     * 品目操作日志注解
     * @author zhoujin
     */
    @Target(ElementType.METHOD)  
    @Retention(RetentionPolicy.RUNTIME) 
    public @interface OperationLogAnno {
    	
    	/** 模块 */
    	String module();
    	
    	/** 具体操作 */
    	String operate();
    	
    	/** 品目编号 */
    	String pmbh();
    	
    	/** 备注说明 */
    	String remarks() default "";
    
    }

    注:注解中定义的方法若没有给默认值,则写该注解的时候必须给该方法赋值!

    四、Spring AOP切面类

    package com.test.common.aop;
    
    import java.lang.reflect.Method;
    import java.util.Date;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import com.test.common.annotation.OperationLogAnno;
    import com.test.modules.sys.entity.User;
    import com.test.modules.sys.utils.UserUtils;
    import com.test.modules.zxztb.entity.operationLog.OperationLog;
    import com.test.modules.zxztb.service.operationLog.OperationLogService;
    
    /**
     * 品目操作日志切面类
     * @author zhoujin
     * @date 2018-10-23
     */
    @Aspect
    @Component("operationLogAspect")
    public class OperationLogAspect {
    	
        private static final Logger log = LoggerFactory.getLogger(OperationLogAspect.class);
        
        @Autowired
        private OperationLogService operationLogService;
    	
        // 配置织入点(以OperationLog注解为标志)
        @Pointcut("@annotation(com.test.common.annotation.OperationLogAnno)")
        public void logPointCut() {
        }
     
        /**
         * 后置通知 用于拦截操作,在方法返回后执行
         * @param joinPoint 切点
         */
        @AfterReturning(pointcut = "logPointCut()")
        public void doAfterReturn(JoinPoint joinPoint) {
        	handleLog(joinPoint, null);
        }
     
        /**
         * 拦截异常操作,有异常时执行
         * @param joinPoint
         * @param e
         */
        @AfterThrowing(value = "logPointCut()", throwing = "e")
        public void doAfterThrow(JoinPoint joinPoint, Exception e) {
        	handleLog(joinPoint, e);
        }
     
        private void handleLog(JoinPoint joinPoint, Exception e) {
    	try {
    	    // 获得注解
    		OperationLogAnno controllerLog = getAnnotationLog(joinPoint);
    	    if (controllerLog == null) {
    	    	return;
    	    }
    	    // 品目编号
    	    Object pmbh = controllerLog.pmbh();
    	    // 模块详情
    	    Object module = controllerLog.module();
    	    // 具体操作
    	    Object operate = controllerLog.operate();
    	    // 备注说明
    	    Object remarks = controllerLog.remarks();
    	    // 操作用户信息
    	    User currentUser = UserUtils.getUser();
    	    // 访问类名
    	    String className = joinPoint.getTarget().getClass().getName();
    	    // 访问方法名
    	    String methodName = joinPoint.getSignature().getName();
    	    // 保存日志
    	    log.info("\n====================================== 保存操作日志  ======================================");
    	    OperationLog operationLog = new OperationLog();
    	    operationLog.setPmbh(pmbh.toString());
    	    operationLog.setModule(module.toString());
    	    operationLog.setOperate(operate.toString());
    	    operationLog.setOperate(remarks.toString());
    	    operationLog.setOperatorLoginname(currentUser.getLoginName());
    	    operationLog.setOperatorName(currentUser.getName());
    	    operationLog.setOperateDate(new Date());
    	    operationLog.setClassname(className);
    	    operationLog.setMethodname(methodName);
    	    
    	    operationLogService.save(operationLog);
    	    log.info("\n=====================================================================================\n");
    	} catch (Exception exp) {
    	    // 记录本地异常日志
    	    log.error("\n====================================== 异常信息通知 ======================================");
    	    log.error("异常信息:{}", exp.getMessage());
    	    log.error("\n====================================================================================\n");
    	    exp.printStackTrace();
    	}
        }
     
        /**
         * 是否存在注解,如果存在就获取
         */
        private static OperationLogAnno getAnnotationLog(JoinPoint joinPoint) throws Exception {
    	Signature signature = joinPoint.getSignature();
    	MethodSignature methodSignature = (MethodSignature) signature;
    	Method method = methodSignature.getMethod();
    	if (method != null) {
                return method.getAnnotation(OperationLogAnno.class);
    	}
    	return null;
        }
        
    }
    

    五、Controller上使用

    @ResponseBody
    @RequestMapping(value = "kaiQicb")
    @RequiresPermissions("zxztb:dljgKbkzt:kqcb")
    @OperationLogAnno(module="控制台", operate="开启流程", pmbh="1111111")
    public AjaxJson kaiQicb(String id) {
        AjaxJson j = new AjaxJson();
        String message = "开启流程成功";
        Zfcgpmbxm zfcgpmbxm = zfcgpmbxmService.get(id);
        try {
            zfcgpmbxm.setFlowstatus("7");
            zfcgpmbxmService.save(zfcgpmbxm);
        } catch (Exception e) {
            e.printStackTrace();
            j.setSuccess(false);
            message = "开启流程失败";
        }
        j.setMsg(message);
        return j;
    }

    注:此处注解上的pmbh是写死的,没有什么作用。但实际情况是,此处pmbh应取前端页面传来的参数id的值,那么怎么才能使注解中能获取到参数的值呢?

    java在注解中绑定方法参数的解决方案


    六、下载上面node2017博客中的AnnotationResolver,便可获取前端页面传来的参数值

    6.1 Controller中

    @OperationLogAnno(module="控制台", operate="开启流程", pmbh="#{id}")
    public AjaxJson kaiQiKaiBiao(String id){
        ...
    }
    // 这样也可以
    @OperationLogAnno(module="控制台", operate="开启流程", pmbh="#{pmxx.id}")
    public AjaxJson kaiQiKaiBiao(Pmxx pmxx){
        ...
    }

    6.2 Spring AOP切面类中

     private void handleLog(JoinPoint joinPoint, Exception e) {
        try {
            // 获得注解
            OperationLogAnno controllerLog = getAnnotationLog(joinPoint);
            if (controllerLog == null) {
                return;
            }
            AnnotationResolver annotationResolver = AnnotationResolver.newInstance();
            // 品目编号
            Object pmbh = annotationResolver.resolver(joinPoint, controllerLog.pmbh());
            // 模块详情
            Object module = annotationResolver.resolver(joinPoint, controllerLog.module());
            // 具体操作
            Object operate = annotationResolver.resolver(joinPoint, controllerLog.operate());
            // 备注说明
            Object remarks = annotationResolver.resolver(joinPoint, controllerLog.remarks());
            // 操作用户信息
            User currentUser = UserUtils.getUser();
            // 访问类名
            String className = joinPoint.getTarget().getClass().getName();
            // 访问方法名
            String methodName = joinPoint.getSignature().getName();
            // 保存日志
            ...
    
       } catch (Exception exp) {
            exp.printStackTrace();
    }

    6.4 AnnotationResolver代码(来自:node2017 ==》java在注解中绑定方法参数的解决方案

    package com.jeeplus.common.annotation;
    
    import java.lang.reflect.Method;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.reflect.MethodSignature;
    
    /**
     * 该类的作用可以把方法上的参数绑定到注解的变量中,注解的语法#{变量名}
     * 能解析类似#{user}或者#{user.id}或者{user.createBy.id}
     */
    public class AnnotationResolver {
    
        private static AnnotationResolver resolver ;
    	
    	
        public static AnnotationResolver newInstance(){
    		
            if (resolver == null) {
                return resolver = new AnnotationResolver();
            }else{
                return resolver;
            }
    		
        }
    	
    	
        /**
         * 解析注解上的值
         * @param joinPoint
         * @param str 需要解析的字符串
         * @return
         */
        public Object resolver(JoinPoint joinPoint, String str) {
    
            if (str == null) return null ;
    		
            Object value = null;
            if (str.matches("#\\{\\D*\\}")) {// 如果name匹配上了#{},则把内容当作变量
                String newStr = str.replaceAll("#\\{", "").replaceAll("\\}", "");
                if (newStr.contains(".")) { // 复杂类型
                    try {
                        value = complexResolver(joinPoint, newStr);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    value = simpleResolver(joinPoint, newStr);
                }
            } else { //非变量
                value = str;
            }
            return value;
        }
    
    	
        private Object complexResolver(JoinPoint joinPoint, String str) throws Exception {
    
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    
            String[] names = methodSignature.getParameterNames();
            Object[] args = joinPoint.getArgs();
            String[] strs = str.split("\\.");
    
            for (int i = 0; i < names.length; i++) {
                if (strs[0].equals(names[i])) {
                    Object obj = args[i];
                    Method dmethod = obj.getClass().getDeclaredMethod(getMethodName(strs[1]), null);
                    Object value = dmethod.invoke(args[i]);
                    return getValue(value, 1, strs);
                }
            }
    
            return null;
    
        }
    
        private Object getValue(Object obj, int index, String[] strs) {
    
            try {
                if (obj != null && index < strs.length - 1) {
                    Method method = obj.getClass().getDeclaredMethod(getMethodName(strs[index + 1]), null);
                    obj = method.invoke(obj);
                    getValue(obj, index + 1, strs);
                }
    
                return obj;
    
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        private String getMethodName(String name) {
            return "get" + name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
        }
    
    	
        private Object simpleResolver(JoinPoint joinPoint, String str) {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            String[] names = methodSignature.getParameterNames();
            Object[] args = joinPoint.getArgs();
    
            for (int i = 0; i < names.length; i++) {
                if (str.equals(names[i])) {
                    return args[i];
                }
            }
            return null;
        }
    	
    }
    

     

    展开全文
  • 日志系统记录用户行为有以下的作用: 从系统用户角度看:它展示了用户自身的操作历史和具体对象的变动历史,便于用户进行梳理 从系统管理员角度看:它可以记录了所有用户操作,便于我们定位异常行为 例如,在git...
  • SpringBoot+AOP实现用户操作日志记录

    千次阅读 2020-09-27 16:57:05
    任何一个项目都会有一个用户操作日志(也叫行为日志)的模块,它主要用来记录某个用户做了某个操作,当出现操作失败时,通过日志就可以快速的查找是哪个用户在哪个模块出现了错误,以便于开发人员快速定位问题所在。
  • 一 、添加日志记录的作用是:  为了后期更好的维护代码,缩小范围,更好更快的寻找代码错误的地方。  二、添加日志的方法:    1、在项目的src下创建一个名叫log4j.properties的file文件,内容如下:...
  • SpringMVC 记录用户操作日志

    万次阅读 2017-10-18 11:03:16
    用spring mvc 做用户操作日志记录基本有两种方式 1.利用aop做(推荐) 2.利用拦截器做(不推荐) AOP做操作日志记录基本步骤  1.数据库创建日志表(定义UserOperateLog实体类) @Entity @Table(name = ...
  • java实现记录日志

    千次阅读 热门讨论 2018-07-29 17:16:37
    Java 中实现记录日志的方式有很多种, 1. 最简单的方式,就是system.println.out(error) ,这样直接在控制台打印消息了。 2. Java.util.logging ; 在JDK 1.4 版本之后,提供了日志的API ,可以往文件中写日志了。...
  • 用户操作日志.zip

    2019-09-11 11:25:02
    java aop 用户操作日志 在每个action\controller 上加上注解 就可以了 例如 @Log(“添加用户”) 日志包含(请求时间,请求ip,请求类名 ,请求方法,请求参数,请求异常)
  • java自定义注解annotation记录操作日志

    千次阅读 2018-12-06 10:24:24
    编写controller代码, 登录方法加了我自己定义的注解 @IgnoreToken aop在拦截的时候判断有我们自己定义注解就不会去校验token啦,获取密码的方法则会进行token的校验 @RestController public class ...
  • 记录用户操作日志,使用的是返回通知(@AfterReturning),只有在前端调用了我后端的接口并成功返回,才调用我的切面方法记录用户操作存储到数据库中。 注解类: LogAnnotation.java import java.lang....
  • 如何实现操作操作日志记录

    万次阅读 多人点赞 2018-12-26 11:16:45
    如何实现操作操作日志记录 为什么要记录操作日志? 项目中的业务需求,需要针对用户的一些业务操作操作记录, 也就是标题中的操场日志记录,最近做的项目也有这个需求, 我也是第一次写,相信有很多开发者也有遇到这个...
  • 自定义注解的用法, 好多人不知道, 在这里, 代码的注释中, 我已经详细的介绍了, ...案例: 记录系统操作日志 首先是定义注解: package cn.wxy.ssm.myAnnotation; import java.lang.annotation.Documented; impo
  • 今天搞了一个AOP的管理日志,蛋疼的很....老规矩贴代码吧 首先除了aop的包以外需要这三个包。自行度娘。(如果想切入controllers,请将这个代码写入你的mvc配置中,纠结了一上午切不进去就是这个原因.)(method为你...
  • Java 代码里的日志输出优化

    千次阅读 2019-07-01 10:59:45
    使用门面模式的日志框架,有利于维护和各个2类的日志处理方式统一。 实现方式统一使用: Logback框架 打日志的正确方式 什么时候应该打日志 当你遇到问题的时候,只能通过debug功能来确定问题,你应该考虑打日志,...
  • 重要申明:本文转自https://www.imooc.com/article/21178本文介绍了一下自己在Java项目中使用log的一些总结,从日志的作用、日志的选用、日志级别介绍、日志记录的一些最佳实践几个方面阐述。日志的作用主要作用包括...
  • 一文搞懂Java日志级别,重复记录、丢日志问题

    万次阅读 多人点赞 2020-12-06 21:33:46
    今天,我和你分享的是,记录日志可能会踩的坑。 一些同学可能要说了,记录日志还不简单,无非是几个常用的API方法,比如debug、info、warn、error;但我就见过不少坑都是记录日志引起的,容易出错主要在于三个方面:...
  • Java日志记录的5条规则

    千次阅读 2016-01-20 11:45:14
    日志记录是在软件开发过程中常常需要考虑的关键因素。...以下5条日志规则,让我们可以检查和改进在代码操作日志记录的方式。 同时也请注意,我们既不会讨论怎么配置一个日志引擎,也不会相互比较。
  • 日志记录简化,只需要在action或者controller的方法上加logging注解就可以实现记录日志功能方便快捷。注解:@Logging(description = "{username}登录")
  • java日志记录示例

    千次阅读 2018-05-10 20:02:08
    代码如下所示:import java.io.IOException; import java.util.logging.*; public class LoggerTest { public static void main(String args[]) { final Logger myLogger = Logger.getLogger("mylooger&...
  • Java项目日志记录方案

    万次阅读 2016-12-31 21:49:44
    1、采用slf4j作为日志API,采用logback作为日志输出工具,用slf4j桥接方式替换掉log4j和commons-logging。 2、采用trace(追踪)、debug(调试)、info(信息)、warn(警告)、error(错误)、fatal(致命)共6...
  • java代码实现mysql分表(用户行为记录)

    千次阅读 2019-09-25 16:44:04
    设置项目气动执行次方法(每天检查一次表记录) public class DayInterval implements ServletContextListener{ private static SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); ...
  • Java中使用log4j记录日志

    千次阅读 2016-02-04 09:35:03
    java中,记录日志,有很多种方式。 比如,自己实现。 自己写类,将日志数据,以io操作方式,写数据到文本文件。或者是写到数据库中。 比如,使用log4j。 log4j,这也是此文要记录的。log4j,以前在用...
  • JAVA日志记录方法

    万次阅读 2016-09-01 11:22:29
    引入loggerg类和logger工厂类声明logger记录日志 下面看一个例子 //1. 引入slf4j接口的Logger和LoggerFactory import org.slf4j.Logger; import org.slf4j.LoggerFactory; public cl
  • 传统的项目开发中业务流程以串行方式,执行了模块1—》模块2–》模块3 ...(3)程序的代码上造成冗余,模块与模块需要进行强通信以及数据的交互,出现问题时难以定位与维护。耦合度过高! 因此需要进行优化,将强...
  • Java Logger(java日志)

    万次阅读 2009-10-16 10:29:00
    log4j维护嵌入在程序代码中的日志记录语句。通过规范日志记录的处理过程,一些人认为应该鼓励更多的使用日志记录并且获得更高程度的效率。 2.安装 为了使用我们即将要安装的日志记录工具,还必须要设置操作环境,...
  • java记录日志(异常记录)

    千次阅读 2013-10-16 18:27:03
    /**记录操作历史**/ public static void setRecord(String record){ String recordFilePath = "D://record/record"+getTypeDate("yyyy-MM-dd", new Date())+".txt"; System.out.println(recordFilePath); ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 232,342
精华内容 92,936
关键字:

java记录用户操作日志的代码

java 订阅