精华内容
下载资源
问答
  • JAVA 日志埋点

    千次阅读 2017-02-15 10:46:56
     JAVA 日志埋点使用总结: 环境:jdk1.7、tomcat7、win10、maven 3.2.5   一、 删除应用中所有的log jar包 二、 导入unslog jar包   <dependency> <groupId>com.uns.inf&...

     

     JAVA 日志埋点使用总结:

    环境:jdk1.7、tomcat7、win10、maven 3.2.5

     

    一、  删除应用中所有的log jar包

    二、  导入unslog jar包

      

    <dependency>
         <groupId>com.uns.inf</groupId>
         <artifactId>unslog</artifactId>
         <version>1.0.0</version>
    </dependency>

     

    三、  在resource目录下追加unslog.properties(可选)

    内容如下:

    #项目名称(工程化项目自动获取,无需修改)
    app_name=${projectName}
    #日志级别
    #log_level=info
    #日志存放天数
    #maxHistory=30
    #业务埋点是否输出( true输出 false关闭 默认是关闭)
    #bizlog=true
    #控制台日志关闭 (true 打印 false关闭 默认是关闭)
    #console=false

     

    日志文件存放路径(在unslog.jar包中写死的路径):

    windos在d://logs/应用名称/日期/日志文件

    linux在 在/opt/ci/logs/应用名称/日期/日志文件

     

    四、  日志打印实例

    // 一般日志打印

        logger.info("test start");

     

    // 日志规则ID(可统一编排)
         longbzId = 10000;
         // 创建日志对象
         UnsLogInfo uli = new UnsLogInfo(bzId);
         // 设置日志信息
         uli.put("userid", 12434);
         uli.put("userName", "name001");
         uli.put("startDt", new Date());
         // 打印业务日志
         logger.info(uli.toString());

     // 一般日志打印

     

         logger.info("test end");

    五、  说明

    日志规则ID:是业务埋点的唯一标示,不同的业务埋点不同,可以根据需要统一编制。

    后台进行业务统计分析时重要的标示(日志规则ID对应后台的报警规则ID一个或多个,监控平台并进行统计分析)

     

    注:如果日志采用slf4j的都可以灰度替换,其他的需要自己修改下LoggerFactory的获取方式(也是很简单的)

     

     

    六、实践

    1.导入unslog.jar包,在pom.xml文件中加入如下代码:

     

    <dependency>
         <groupId>com.uns.inf</groupId>
         <artifactId>unslog</artifactId>
         <version>1.0.0</version>
    </dependency>

    2. 使用slf4j的Logger,因为它可以在log4j和logback之间随意切换,根据你使用的logjar包,代码如下:

     

     

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    public final Logger log = LoggerFactory.getLogger(getClass());

     3.编写一个日志埋点的公用方法,代码如下:

     

     

    /**
    	 * 日志埋点
    	 * @param curtran
    	 * @param flag 1:成功,0:失败;bzId 日志规则ID
    	 * @return
    	 */
    	public static String monitoring(Long bzId, Curtran curtran,String flag) {
    	    // 创建日志对象
    	    UnsLogInfo uli = new UnsLogInfo(bzId);
    	    // 设置日志信息
    	    if (Constant.TRUE.equals(flag)) {
    	    	uli.setSuccess(1);
    		} else {
    			uli.setFailure(1);
    		}
    	    if (curtran != null) {
    	    	uli.put("merchantId", curtran.getMerchantid());
    	    	uli.put("terminalid", curtran.getTerminalid());
    	    	uli.put("orderId", curtran.getTrannumber());
    	    	uli.put("cardNo", curtran.getCard());
    	    	uli.put("amount", curtran.getAmount());
    		}
    	    return uli.toString();
    	}

     4.项目中应用,代码如下:

     

     

    // 日志埋点
    log.info("Monitoring unspay start");
    // 打印业务日志
    log.info(Common.monitoring(20005L, curtran, Constant.TRUE));
    log.info("Monitoring unspay end");

     

     

     5.如果是WEB项目,想将控制台的日志输出在catalina.out文件中,则需要新建一个配置文件unslog.properties

    在文件中添加如下内容:

    #项目名称(工程化项目自动获取,无需修改)  
    app_name=${projectName}
    #日志级别  
    #log_level=info  
    #日志存放天数  
    #maxHistory=30  
    #业务埋点是否输出( true输出 false关闭 默认是关闭)  
    bizlog=true
    #控制台日志关闭 (true 打印 false关闭 默认是关闭)  
    console=true

    6.如果部署到Linux服务器,需要确认服务器上,是否有预先建立目录/opt/ci/logs 目录

     

    7.注意要点:本次日志埋点使用的是logback,作为日志输出

     

    8.JAR包下载(见附件) 

     

     

     

     

     

     

     

     

    展开全文
  • 前段时间架构让我弄日志埋点,因为其他工作不断延期,而且到现在也没给明确的需求,这里自己手写一套简单的日志埋点:第一步:引入依赖org.springframework.bootspring-boot-starter-aoporg.apache.logging.log4jlog...

    前段时间架构让我弄日志埋点,因为其他工作不断延期,而且到现在也没给明确的需求,这里自己手写一套简单的日志埋点:

    第一步:引入依赖

    org.springframework.boot

    spring-boot-starter-aop

    org.apache.logging.log4j

    log4j-core

    2.3

    org.slf4j

    slf4j-log4j12

    1.7.25

    第二步:因为公司有自己的日志配置,这里我简单配置一条凑合用就行,在application.properties配置:

    #日志文件路径 默认生成文件名:spring.log 为了简单便于学习这里我使用默认的

    logging.path=F:/Log4j

    第三步:自定义注解:

    package com.javaliao.portal.annotations;

    import org.springframework.web.bind.annotation.ResponseBody;

    import java.lang.annotation.*;

    /**

    * app controller 统一包装注解类

    */

    @Target({ElementType.PARAMETER, ElementType.METHOD})//作用在参数和方法上

    @Retention(RetentionPolicy.RUNTIME)//运行时注解

    @Documented//表明这个注解应该被 javadoc工具记录

    @ResponseBody//响应时转JSON格式

    public @interface AppController {

    /**

    * 业务描述

    * @return

    */

    String description() default "";

    /**

    * 是否打日志 默认打

    */

    boolean isLog() default true;

    }

    第四步:拦截用户请求

    package com.javaliao.portal.aspect;

    import com.alibaba.fastjson.JSON;

    import com.javaliao.portal.annotations.AppController;

    import com.javaliao.portal.common.CommonResult;

    import com.javaliao.portal.model.TbLogVisit;

    import com.javaliao.portal.service.ActionService;

    import com.javaliao.portal.util.CollectionHelp;

    import com.javaliao.portal.util.TimeUtils;

    import org.aspectj.lang.JoinPoint;

    import org.aspectj.lang.ProceedingJoinPoint;

    import org.aspectj.lang.annotation.*;

    import org.aspectj.lang.reflect.MethodSignature;

    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.net.InetAddress;

    import java.util.*;

    import java.util.concurrent.atomic.AtomicLong;

    @Component

    @Aspect

    @SuppressWarnings("all") //@SuppressWarnings("all")抑制所有警告.@SuppressWarnings注解主要用在取消一些编译器产生的警告对代码左侧行列的遮挡,有时候这会挡住我们断点调试时打的断点

    public class AppControllerAspect {

    //注入Service用于把日志保存数据库,实际项目入库采用队列做异步

    @Resource

    private ActionService actionService;

    //日志工厂获取日志对象

    static Logger logger = LoggerFactory.getLogger(AppControllerAspect.class);

    /**

    * ThreadLocal多线程环境下,创建多个副本,各自执行,互不干扰

    */

    //startTime存放开始时间

    ThreadLocal> startTime = new ThreadLocal<>();

    //count存放方法被调用的次数O 使用volatile利用它的三大特性:保证可见性(遵守JMM的可见性),不保证原子性,禁止指令重排

    volatile ThreadLocal> count = new ThreadLocal<>();

    //timeConsuming存放方法总耗时

    ThreadLocal> timeConsuming = new ThreadLocal<>();

    //fromType存放渠道

    ThreadLocal> fromType = new ThreadLocal<>();

    //tbLogVisit日志访问对象

    ThreadLocal tbLogVisit = new ThreadLocal<>();

    //Controller层切点

    @Pointcut("@annotation(com.javaliao.portal.annotations.AppController)")

    public void controllerAspectse() {

    }

    //前置通知 用于拦截Controller层记录用户的操作

    @Before("controllerAspectse()")

    public void before(JoinPoint pjp) {

    //初始化

    TbLogVisit tbLogVisit = this.tbLogVisit.get();

    tbLogVisit = new TbLogVisit();

    Map countMap = this.count.get();

    countMap = new HashMap<>();

    this.count.set(countMap);

    Map timeConsumingMap = this.timeConsuming.get();

    timeConsumingMap = new HashMap<>();

    this.timeConsuming.set(timeConsumingMap);

    Map fromTypeMap = this.fromType.get();

    fromTypeMap = new HashMap<>();

    this.fromType.set(fromTypeMap);

    Map map = new HashMap<>();

    map.put("startTime",System.currentTimeMillis());

    this.startTime.set(map);

    logger.info("==============前置通知开始:记录用户的操作==============");

    String currentTime = TimeUtils.getCurrentTime("YYYY-MM-dd HH:mm:ss");

    logger.info("请求开始时间:" + currentTime);

    tbLogVisit.setVisitStartTime(new Date());

    String resultString = "";

    // 是否打日志 默认打

    boolean isLog = true;

    try {

    MethodSignature signature = (MethodSignature) pjp.getSignature();

    AppController appController = signature.getMethod().getAnnotation(AppController.class);

    //是否开启日志打印

    isLog = appController.isLog();

    if(isLog){

    //开始打印日志

    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

    HttpSession session = request.getSession();

    String api = pjp.getTarget().getClass().getName() + "." + pjp.getSignature().getName();

    logger.info("请求API:" + api);

    tbLogVisit.setVisitApi(api);

    String methodDescription = getControllerMethodDescription(pjp);

    logger.info("方法描述:" + methodDescription);

    tbLogVisit.setVisitDescription(methodDescription);

    String ipAddress = InetAddress.getLocalHost().toString().substring(InetAddress.getLocalHost().toString().lastIndexOf("/") + 1);

    logger.info("请求ip:"+ ipAddress);

    tbLogVisit.setVisitIpAddress(ipAddress);

    String hostName = InetAddress.getLocalHost().getHostName();

    logger.info("机器名:" + hostName);

    tbLogVisit.setVisitHostName(hostName);

    Enumeration> enu = request.getParameterNames();

    String params = "{";

    while (enu.hasMoreElements()) {

    String paraName = (String) enu.nextElement();

    params += "\"" + paraName + "\":\"" + request.getParameter(paraName) + "\",";

    }

    String methodParams = params + "}";

    String substring = methodParams.substring(0, methodParams.length() - 2);

    substring = substring + "}";

    logger.info("方法参数:" + substring);

    tbLogVisit.setVisitParams(substring);

    StringBuffer url = request.getRequestURL();

    logger.info("URL:" + url);

    tbLogVisit.setVisitUrl(String.valueOf(url));

    }

    } catch (Exception e) {

    StackTraceElement stackTraceElement2 = e.getStackTrace()[2];

    String reason = "异常:【"+

    "类名:"+stackTraceElement2.getClassName()+";"+

    "文件:"+stackTraceElement2.getFileName()+";"+"行:"+

    stackTraceElement2.getLineNumber()+";"+"方法:"

    +stackTraceElement2.getMethodName() + "】";

    //记录本地异常日志

    logger.error("==============前置通知异常:记录访问异常信息==============");

    String message = e.getMessage() + "|" + reason;

    logger.error("异常信息:",message);

    tbLogVisit.setVisitThrowingErro(message);

    tbLogVisit.setVisitResult("请求发生异常,异常信息:" + message);

    }finally {

    this.tbLogVisit.set(tbLogVisit);

    }

    }

    @Around("controllerAspectse()")

    public Object around(ProceedingJoinPoint pjp)throws Throwable {

    String result = JSON.toJSONString(pjp.proceed());

    logger.info("请求结果:" + result);

    TbLogVisit tbLogVisit = this.tbLogVisit.get();

    tbLogVisit.setVisitResult(result);

    this.tbLogVisit.set(tbLogVisit);

    return CommonResult.success("");

    }

    /**

    * 对Controller下面的方法执行后进行切入,统计方法执行的次数和耗时情况

    * 注意,这里的执行方法统计的数据不止包含Controller下面的方法,也包括环绕切入的所有方法的统计信息

    * @param jp

    */

    @AfterReturning("controllerAspectse()")

    public void afterMehhod(JoinPoint jp) {

    logger.info("==============方法执行完成==============");

    TbLogVisit tbLogVisit = this.tbLogVisit.get();

    try {

    //获取方法名

    String methodName = jp.getSignature().getName();

    //开始统计数量与耗时

    if(count.get().get(methodName) == null){

    //第一次赋值为0

    count.get().put(methodName,0L);

    }

    //使用原子整型进行增值

    AtomicLong atomicInteger = new AtomicLong(count.get().get(methodName));

    //加一 这里暂时不解决ABA问题,仅保证原子性 解决了volatile不保证原子性的问题 getAndIncrement()先返回再加1,incrementAndGet()先加1再返回

    long increment = atomicInteger.incrementAndGet();

    //然后增加新值

    count.get().replace(methodName,increment);

    Long end = System.currentTimeMillis();

    Long total = end - startTime.get().get("startTime");

    logger.info("执行总耗时为:" +total);

    if(timeConsuming.get().containsKey(methodName)){

    timeConsuming.get().replace(methodName, total);

    }else {

    timeConsuming.get().put(methodName, (total));

    }

    tbLogVisit = this.tbLogVisit.get();

    tbLogVisit.setVisitTimeConsuming(String.valueOf(total));

    String endTime = TimeUtils.getCurrentTime("YYYY-MM-dd HH:mm:ss");

    logger.info("请求结束时间:" + endTime);

    tbLogVisit.setVisitEndTime(new Date());

    /**

    * 从原来的map中将最后的连接点方法给移除了,替换成最终的,避免连接点方法多次进行叠加计算,

    * 由于原来的map受ThreadLocal的保护,这里不支持remove,因此,需要单开一个map进行数据交接

    */

    //重新new一个map

    Map map = new HashMap<>();

    for(Map.Entry entry:timeConsuming.get().entrySet()){

    if(entry.getKey().equals(methodName)){

    map.put(methodName, total);

    }else{

    map.put(entry.getKey(), entry.getValue());

    }

    }

    for (Map.Entry entry :count.get().entrySet()) {

    for(Map.Entry entry2 :map.entrySet()){

    if(entry.getKey().equals(entry2.getKey())){

    Long num = entry.getValue();

    logger.info("调用次数:" + num);

    tbLogVisit.setVisitNum(num);

    }

    }

    }

    //这里的渠道暂时写死

    Map stringMap = fromType.get();

    Map fromMap ;

    if(CollectionHelp.isMapNotEmpty(stringMap)){

    fromMap = stringMap;

    }else {

    fromMap = new HashMap<>();

    fromMap.put(methodName,"个人开发电商平台");

    }

    String channel = fromMap.get(methodName);

    logger.info("渠道:" + channel);

    tbLogVisit.setVisitChannel(channel);

    } catch (Exception e) {

    StackTraceElement stackTraceElement2 = e.getStackTrace()[2];

    String reason = "异常:【"+

    "类名:"+stackTraceElement2.getClassName()+";"+

    "文件:"+stackTraceElement2.getFileName()+";"+"行:"+

    stackTraceElement2.getLineNumber()+";"+"方法:"

    +stackTraceElement2.getMethodName() + "】";

    //记录本地异常日志

    logger.error("==============通知异常:记录访问异常信息==============");

    String message = e.getMessage() + "|" + reason;

    logger.error("异常信息:",message);

    tbLogVisit.setVisitThrowingErro(message);

    tbLogVisit.setVisitResult("请求发生异常!!!");

    } finally {

    this.tbLogVisit.set(tbLogVisit);

    //添加日志信息入库

    actionService.insertLogVisit(this.tbLogVisit.get());

    }

    }

    /**

    * 获取注解中对方法的描述信息 用于Controller层注解

    */

    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(AppController.class).description();

    break;

    }

    }

    }

    return description;

    }

    }

    第五步:业务层发消息:

    package com.javaliao.portal.service.impl;

    import com.javaliao.portal.mapper.TbLogVisitMapper;

    import com.javaliao.portal.model.TbLogVisit;

    import com.javaliao.portal.service.ActionService;

    import com.javaliao.portal.util.ActiveMQUtil;

    import net.sf.json.JSONObject;

    import org.apache.activemq.command.ActiveMQMapMessage;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.stereotype.Service;

    import javax.jms.*;

    @Service

    public class ActionServiceImpl implements ActionService {

    @Autowired

    TbLogVisitMapper tbLogVisitMapper;

    @Autowired

    ActiveMQUtil activeMQUtil;

    @Override

    public void insertLogVisit(TbLogVisit tbLogVisit) {

    try {

    // 连接消息服务器

    Connection connection = activeMQUtil.getConnection();

    connection.start();

    //第一个值表示是否使用事务,如果选择true,第二个值相当于选择0

    Session session = connection.createSession(true, Session.SESSION_TRANSACTED);

    // 发送消息

    Queue testqueue = session.createQueue("LOG_VISIT_QUEUE");

    MessageProducer producer = session.createProducer(testqueue);

    MapMessage mapMessage=new ActiveMQMapMessage();

    String toString = JSONObject.fromObject(tbLogVisit).toString();

    mapMessage.setString("tbLogVisit",toString);

    producer.setDeliveryMode(DeliveryMode.PERSISTENT);

    producer.send(mapMessage);

    session.commit();// 事务型消息,必须提交后才生效

    connection.close();

    } catch (JMSException e) {

    e.printStackTrace();

    }

    }

    }

    第六步:接收消息执行添加业务:

    package com.javaliao.portal.listener;

    import com.javaliao.portal.log4j.BaseLogger;

    import com.javaliao.portal.model.TbLogVisit;

    import com.javaliao.portal.service.ActionService;

    import net.sf.json.JSONObject;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.jms.annotation.JmsListener;

    import org.springframework.stereotype.Component;

    import javax.jms.JMSException;

    import javax.jms.MapMessage;

    @Component

    public class LogVisitListener {

    @Autowired

    ActionService actionService;

    @JmsListener(containerFactory = "jmsQueueListener" ,destination = "LOG_VISIT_QUEUE")

    public void consumeLogResult(MapMessage mapMessage){

    try {

    String object = mapMessage.getString("tbLogVisit");

    JSONObject jsonObject = new JSONObject().fromObject(object);

    TbLogVisit logVisit = (TbLogVisit) JSONObject.toBean(jsonObject, TbLogVisit.class);

    int count = actionService.insertLog(logVisit);

    if(count < 1){

    BaseLogger.info("日志更新失败");

    }

    } catch (JMSException e) {

    e.printStackTrace();

    }

    }

    }

    执行业务:

    package com.javaliao.portal.service.impl;

    import com.javaliao.portal.mapper.TbLogVisitMapper;

    import com.javaliao.portal.model.TbLogVisit;

    import com.javaliao.portal.model.TbLogVisitExample;

    import com.javaliao.portal.service.ActionService;

    import com.javaliao.portal.util.ActiveMQUtil;

    import com.javaliao.portal.util.CollectionHelp;

    import com.javaliao.portal.util.NumberUtils;

    import net.sf.json.JSONObject;

    import org.apache.activemq.command.ActiveMQMapMessage;

    import org.apache.commons.lang3.StringUtils;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.stereotype.Service;

    import javax.jms.*;

    import java.util.Date;

    import java.util.List;

    @Service

    public class ActionServiceImpl implements ActionService {

    @Autowired

    TbLogVisitMapper tbLogVisitMapper;

    //这里去掉了之前发消息的代码

    /**

    * 添加日志信息入库

    * @param tbLogVisit

    * @return

    */

    @Override

    public int insertLog(TbLogVisit tbLogVisit) {

    tbLogVisit.setUpdateTime(new Date());

    int count = 0;

    //如果有异常直接添加

    if(StringUtils.isNoneEmpty(tbLogVisit.getVisitThrowingErro())){

    tbLogVisit.setCreateTime(new Date());

    count = tbLogVisitMapper.insert(tbLogVisit);

    }else {

    String visitIpAddress = tbLogVisit.getVisitIpAddress();

    String visitApi = tbLogVisit.getVisitApi();

    TbLogVisitExample tbLogVisitExample = new TbLogVisitExample();

    TbLogVisitExample.Criteria criteria = tbLogVisitExample.createCriteria();

    criteria.andVisitIpAddressEqualTo(visitIpAddress);

    criteria.andVisitApiEqualTo(visitApi);

    List tbLogVisits = tbLogVisitMapper.selectByExample(tbLogVisitExample);

    if(CollectionHelp.isNotEmpty(tbLogVisits)){

    Long nums = 0L;

    Double sums = 0D;

    for (TbLogVisit logVisit : tbLogVisits) {

    //统计调用次数

    Long visitNum = logVisit.getVisitNum();

    nums = tbLogVisit.getVisitNum() + visitNum;

    //统计耗时

    Double visitTimeConsumingData = NumberUtils.Double(logVisit.getVisitTimeConsuming());

    Double visitTimeConsumingParam = NumberUtils.Double(tbLogVisit.getVisitTimeConsuming());

    Double sum = visitTimeConsumingData + visitTimeConsumingParam;

    sums = sums + sum;

    }

    Double numDouble = NumberUtils.Double(String.valueOf(nums));

    //统计平均耗时

    Double avg = sums / numDouble;

    tbLogVisit.setVisitTimeConsuming(avg.toString());

    tbLogVisit.setVisitNum(nums);

    count = tbLogVisitMapper.updateByExample(tbLogVisit,tbLogVisitExample);

    }else {

    tbLogVisit.setCreateTime(new Date());

    count = tbLogVisitMapper.insert(tbLogVisit);

    }

    }

    return count;

    }

    }

    一开始没有设计好,后面强迫改动,导致访客的开始时间和结束时间都是最近一次的,不过我把每次请求的耗时改为平均耗时,勉强达到效果(不过有些请求异常的耗时时间长的就比较影响耗时统计了,唉不说了,最开始没有设计好,也算勉强了),效率也不算太差,不会太影响性能,

    展开全文
  • 实战:第四章:java后端日志埋点实现

    千次阅读 多人点赞 2019-10-31 14:17:30
    前段时间架构让我弄日志埋点,因为其他工作不断延期,而且到现在也没给明确的需求,这里自己手写一套简单的日志埋点: 第一步:引入依赖 <!--aop--> <dependency> <groupId>org.spring...

    前段时间架构让我弄日志埋点,因为其他工作不断延期,而且到现在也没给明确的需求,这里自己手写一套简单的日志埋点:

    第一步:引入依赖

    <!--aop-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <!--log4j-->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.3</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.25</version>
    </dependency>

    第二步:因为公司有自己的日志配置,这里我简单配置一条凑合用就行,在application.properties配置:

    #日志文件路径 默认生成文件名:spring.log 为了简单便于学习这里我使用默认的
    logging.path=F:/Log4j

    第三步:自定义注解:

    package com.javaliao.portal.annotations;
    
    import org.springframework.web.bind.annotation.ResponseBody;
    import java.lang.annotation.*;
    
    /**
     *  app controller 统一包装注解类
     */
    @Target({ElementType.PARAMETER, ElementType.METHOD})//作用在参数和方法上
    @Retention(RetentionPolicy.RUNTIME)//运行时注解
    @Documented//表明这个注解应该被 javadoc工具记录
    @ResponseBody//响应时转JSON格式
    public @interface AppController {
    
    	/**
    	 * 业务描述
    	 * @return
    	 */
    	String description() default "";
    	
    	/**
    	 * 是否打日志 默认打
    	 */
    	boolean isLog() default true;
    }
    

    第四步:拦截用户请求

    package com.javaliao.portal.aspect;
    
    import com.alibaba.fastjson.JSON;
    import com.javaliao.portal.annotations.AppController;
    import com.javaliao.portal.common.CommonResult;
    import com.javaliao.portal.model.TbLogVisit;
    import com.javaliao.portal.service.ActionService;
    import com.javaliao.portal.util.CollectionHelp;
    import com.javaliao.portal.util.TimeUtils;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.aspectj.lang.reflect.MethodSignature;
    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.net.InetAddress;
    import java.util.*;
    import java.util.concurrent.atomic.AtomicLong;
    
    @Component
    @Aspect
    @SuppressWarnings("all") //@SuppressWarnings("all")抑制所有警告.@SuppressWarnings注解主要用在取消一些编译器产生的警告对代码左侧行列的遮挡,有时候这会挡住我们断点调试时打的断点
    public class AppControllerAspect {
    
        //注入Service用于把日志保存数据库,实际项目入库采用队列做异步
        @Resource
        private ActionService actionService;
        //日志工厂获取日志对象
        static Logger logger = LoggerFactory.getLogger(AppControllerAspect.class);
    
        /**
         * ThreadLocal多线程环境下,创建多个副本,各自执行,互不干扰
         */
    
        //startTime存放开始时间
        ThreadLocal<Map<String, Long >> startTime = new ThreadLocal<>();
        //count存放方法被调用的次数O 使用volatile利用它的三大特性:保证可见性(遵守JMM的可见性),不保证原子性,禁止指令重排
        volatile ThreadLocal<Map<String, Long>> count = new ThreadLocal<>();
        //timeConsuming存放方法总耗时
        ThreadLocal<Map<String, Long >> timeConsuming = new ThreadLocal<>();
        //fromType存放渠道
        ThreadLocal<Map<String, String >> fromType = new ThreadLocal<>();
        //tbLogVisit日志访问对象
        ThreadLocal<TbLogVisit> tbLogVisit = new ThreadLocal<>();
    
        //Controller层切点
        @Pointcut("@annotation(com.javaliao.portal.annotations.AppController)")
        public void controllerAspectse() {
        }
    
        //前置通知  用于拦截Controller层记录用户的操作
        @Before("controllerAspectse()")
        public void before(JoinPoint pjp) {
            //初始化
            TbLogVisit tbLogVisit = this.tbLogVisit.get();
            tbLogVisit = new TbLogVisit();
            Map<String, Long> countMap = this.count.get();
            countMap = new HashMap<>();
            this.count.set(countMap);
            Map<String, Long> timeConsumingMap = this.timeConsuming.get();
            timeConsumingMap = new HashMap<>();
            this.timeConsuming.set(timeConsumingMap);
            Map<String, String> fromTypeMap = this.fromType.get();
            fromTypeMap = new HashMap<>();
            this.fromType.set(fromTypeMap);
            Map<String, Long> map = new HashMap<>();
            map.put("startTime",System.currentTimeMillis());
            this.startTime.set(map);
            logger.info("==============前置通知开始:记录用户的操作==============");
            String currentTime = TimeUtils.getCurrentTime("YYYY-MM-dd HH:mm:ss");
            logger.info("请求开始时间:" + currentTime);
            tbLogVisit.setVisitStartTime(new Date());
            String resultString = "";
            // 是否打日志 默认打
            boolean isLog = true;
            try {
                MethodSignature signature = (MethodSignature) pjp.getSignature();
                AppController appController = signature.getMethod().getAnnotation(AppController.class);
                //是否开启日志打印
                isLog = appController.isLog();
                if(isLog){
                    //开始打印日志
                    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                    HttpSession session = request.getSession();
                    String api = pjp.getTarget().getClass().getName() + "." + pjp.getSignature().getName();
                    logger.info("请求API:" + api);
                    tbLogVisit.setVisitApi(api);
    
                    String methodDescription = getControllerMethodDescription(pjp);
                    logger.info("方法描述:" + methodDescription);
                    tbLogVisit.setVisitDescription(methodDescription);
    
                    String ipAddress = InetAddress.getLocalHost().toString().substring(InetAddress.getLocalHost().toString().lastIndexOf("/") + 1);
                    logger.info("请求ip:"+ ipAddress);
                    tbLogVisit.setVisitIpAddress(ipAddress);
    
                    String hostName = InetAddress.getLocalHost().getHostName();
                    logger.info("机器名:" + hostName);
                    tbLogVisit.setVisitHostName(hostName);
    
                    Enumeration<?> enu = request.getParameterNames();
                    String params = "{";
                    while (enu.hasMoreElements()) {
                        String paraName = (String) enu.nextElement();
                        params += "\"" + paraName + "\":\"" + request.getParameter(paraName) + "\",";
                    }
                    String methodParams = params + "}";
                    String substring = methodParams.substring(0, methodParams.length() - 2);
                    substring = substring + "}";
                    logger.info("方法参数:" + substring);
                    tbLogVisit.setVisitParams(substring);
    
                    StringBuffer url = request.getRequestURL();
                    logger.info("URL:" + url);
                    tbLogVisit.setVisitUrl(String.valueOf(url));
                }
            } catch (Exception e) {
                StackTraceElement stackTraceElement2 = e.getStackTrace()[2];
                String reason = "异常:【"+
                        "类名:"+stackTraceElement2.getClassName()+";"+
                        "文件:"+stackTraceElement2.getFileName()+";"+"行:"+
                        stackTraceElement2.getLineNumber()+";"+"方法:"
                        +stackTraceElement2.getMethodName() + "】";
                //记录本地异常日志
                logger.error("==============前置通知异常:记录访问异常信息==============");
                String message = e.getMessage() + "|" + reason;
                logger.error("异常信息:",message);
                tbLogVisit.setVisitThrowingErro(message);
                tbLogVisit.setVisitResult("请求发生异常,异常信息:" + message);
            }finally {
                this.tbLogVisit.set(tbLogVisit);
            }
        }
    
        @Around("controllerAspectse()")
        public Object around(ProceedingJoinPoint pjp)throws Throwable {
            String result = JSON.toJSONString(pjp.proceed());
            logger.info("请求结果:" + result);
            TbLogVisit tbLogVisit = this.tbLogVisit.get();
            tbLogVisit.setVisitResult(result);
            this.tbLogVisit.set(tbLogVisit);
            return CommonResult.success("");
        }
    
    
        /**
         * 对Controller下面的方法执行后进行切入,统计方法执行的次数和耗时情况
         *  注意,这里的执行方法统计的数据不止包含Controller下面的方法,也包括环绕切入的所有方法的统计信息
         * @param jp
         */
    
        @AfterReturning("controllerAspectse()")
        public void afterMehhod(JoinPoint jp) {
            logger.info("==============方法执行完成==============");
            TbLogVisit tbLogVisit = this.tbLogVisit.get();
            try {
                //获取方法名
                String methodName = jp.getSignature().getName();
                //开始统计数量与耗时
                if(count.get().get(methodName) == null){
                    //第一次赋值为0
                    count.get().put(methodName,0L);
                }
                //使用原子整型进行增值
                AtomicLong atomicInteger = new AtomicLong(count.get().get(methodName));
                //加一 这里暂时不解决ABA问题,仅保证原子性 解决了volatile不保证原子性的问题 getAndIncrement()先返回再加1,incrementAndGet()先加1再返回
                long increment = atomicInteger.incrementAndGet();
                //然后增加新值
                count.get().replace(methodName,increment);
                Long end = System.currentTimeMillis();
                Long total =  end - startTime.get().get("startTime");
                logger.info("执行总耗时为:" +total);
                if(timeConsuming.get().containsKey(methodName)){
                    timeConsuming.get().replace(methodName, total);
                }else {
                    timeConsuming.get().put(methodName, (total));
                }
                tbLogVisit = this.tbLogVisit.get();
                tbLogVisit.setVisitTimeConsuming(String.valueOf(total));
                String endTime = TimeUtils.getCurrentTime("YYYY-MM-dd HH:mm:ss");
                logger.info("请求结束时间:" + endTime);
                tbLogVisit.setVisitEndTime(new Date());
                /**
                 * 从原来的map中将最后的连接点方法给移除了,替换成最终的,避免连接点方法多次进行叠加计算,
                 * 由于原来的map受ThreadLocal的保护,这里不支持remove,因此,需要单开一个map进行数据交接
                 */
                //重新new一个map
                Map<String, Long> map = new HashMap<>();
                for(Map.Entry<String, Long> entry:timeConsuming.get().entrySet()){
                    if(entry.getKey().equals(methodName)){
                        map.put(methodName, total);
                    }else{
                        map.put(entry.getKey(), entry.getValue());
                    }
                }
                for (Map.Entry<String, Long> entry :count.get().entrySet()) {
                    for(Map.Entry<String, Long> entry2 :map.entrySet()){
                        if(entry.getKey().equals(entry2.getKey())){
                            Long num = entry.getValue();
                            logger.info("调用次数:" + num);
                            tbLogVisit.setVisitNum(num);
                        }
                    }
                }
                //这里的渠道暂时写死
                Map<String, String> stringMap = fromType.get();
                Map<String, String> fromMap ;
                if(CollectionHelp.isMapNotEmpty(stringMap)){
                    fromMap = stringMap;
                }else {
                    fromMap = new HashMap<>();
                    fromMap.put(methodName,"个人开发电商平台");
                }
                String channel = fromMap.get(methodName);
                logger.info("渠道:" + channel);
                tbLogVisit.setVisitChannel(channel);
            } catch (Exception e) {
                StackTraceElement stackTraceElement2 = e.getStackTrace()[2];
                String reason = "异常:【"+
                        "类名:"+stackTraceElement2.getClassName()+";"+
                        "文件:"+stackTraceElement2.getFileName()+";"+"行:"+
                        stackTraceElement2.getLineNumber()+";"+"方法:"
                        +stackTraceElement2.getMethodName() + "】";
                //记录本地异常日志
                logger.error("==============通知异常:记录访问异常信息==============");
                String message = e.getMessage() + "|" + reason;
                logger.error("异常信息:",message);
                tbLogVisit.setVisitThrowingErro(message);
                tbLogVisit.setVisitResult("请求发生异常!!!");
            } finally {
                this.tbLogVisit.set(tbLogVisit);
                //添加日志信息入库
                actionService.insertLogVisit(this.tbLogVisit.get());
            }
        }
    
    
    
        /**
         * 获取注解中对方法的描述信息 用于Controller层注解
         */
        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(AppController.class).description();
                        break;
                    }
                }
            }
            return description;
        }
    
    }
    

    第五步:业务层发消息:

    package com.javaliao.portal.service.impl;
    
    import com.javaliao.portal.mapper.TbLogVisitMapper;
    import com.javaliao.portal.model.TbLogVisit;
    import com.javaliao.portal.service.ActionService;
    import com.javaliao.portal.util.ActiveMQUtil;
    import net.sf.json.JSONObject;
    import org.apache.activemq.command.ActiveMQMapMessage;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import javax.jms.*;
    
    @Service
    public class ActionServiceImpl implements ActionService {
    
        @Autowired
        TbLogVisitMapper tbLogVisitMapper;
    
        @Autowired
        ActiveMQUtil activeMQUtil;
    
        @Override
        public void insertLogVisit(TbLogVisit tbLogVisit) {
            try {
                // 连接消息服务器
                Connection connection = activeMQUtil.getConnection();
                connection.start();
                //第一个值表示是否使用事务,如果选择true,第二个值相当于选择0
                Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
                // 发送消息
                Queue testqueue = session.createQueue("LOG_VISIT_QUEUE");
                MessageProducer producer = session.createProducer(testqueue);
                MapMessage mapMessage=new ActiveMQMapMessage();
                String toString = JSONObject.fromObject(tbLogVisit).toString();
                mapMessage.setString("tbLogVisit",toString);
                producer.setDeliveryMode(DeliveryMode.PERSISTENT);
                producer.send(mapMessage);
                session.commit();// 事务型消息,必须提交后才生效
                connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    
    }
    

    第六步:接收消息执行添加业务:

    package com.javaliao.portal.listener;
    
    import com.javaliao.portal.log4j.BaseLogger;
    import com.javaliao.portal.model.TbLogVisit;
    import com.javaliao.portal.service.ActionService;
    import net.sf.json.JSONObject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jms.annotation.JmsListener;
    import org.springframework.stereotype.Component;
    import javax.jms.JMSException;
    import javax.jms.MapMessage;
    
    @Component
    public class LogVisitListener {
    
        @Autowired
        ActionService actionService;
    
        @JmsListener(containerFactory = "jmsQueueListener" ,destination = "LOG_VISIT_QUEUE")
        public void consumeLogResult(MapMessage mapMessage){
            try {
                String object = mapMessage.getString("tbLogVisit");
                JSONObject jsonObject = new JSONObject().fromObject(object);
                TbLogVisit logVisit = (TbLogVisit) JSONObject.toBean(jsonObject, TbLogVisit.class);
                int count = actionService.insertLog(logVisit);
                if(count < 1){
                    BaseLogger.info("日志更新失败");
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    
    }
    

    执行业务:

    package com.javaliao.portal.service.impl;
    
    import com.javaliao.portal.mapper.TbLogVisitMapper;
    import com.javaliao.portal.model.TbLogVisit;
    import com.javaliao.portal.model.TbLogVisitExample;
    import com.javaliao.portal.service.ActionService;
    import com.javaliao.portal.util.ActiveMQUtil;
    import com.javaliao.portal.util.CollectionHelp;
    import com.javaliao.portal.util.NumberUtils;
    import net.sf.json.JSONObject;
    import org.apache.activemq.command.ActiveMQMapMessage;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import javax.jms.*;
    import java.util.Date;
    import java.util.List;
    
    @Service
    public class ActionServiceImpl implements ActionService {
    
        @Autowired
        TbLogVisitMapper tbLogVisitMapper;
    
        //这里去掉了之前发消息的代码
        /**
         * 添加日志信息入库
         * @param tbLogVisit
         * @return
         */
        @Override
        public int insertLog(TbLogVisit tbLogVisit) {
            tbLogVisit.setUpdateTime(new Date());
            int count = 0;
            //如果有异常直接添加
            if(StringUtils.isNoneEmpty(tbLogVisit.getVisitThrowingErro())){
                tbLogVisit.setCreateTime(new Date());
                count = tbLogVisitMapper.insert(tbLogVisit);
            }else {
                String visitIpAddress = tbLogVisit.getVisitIpAddress();
                String visitApi = tbLogVisit.getVisitApi();
                TbLogVisitExample tbLogVisitExample = new TbLogVisitExample();
                TbLogVisitExample.Criteria criteria = tbLogVisitExample.createCriteria();
                criteria.andVisitIpAddressEqualTo(visitIpAddress);
                criteria.andVisitApiEqualTo(visitApi);
                List<TbLogVisit> tbLogVisits = tbLogVisitMapper.selectByExample(tbLogVisitExample);
                if(CollectionHelp.isNotEmpty(tbLogVisits)){
                    Long nums = 0L;
                    Double sums = 0D;
                    for (TbLogVisit logVisit : tbLogVisits) {
                        //统计调用次数
                        Long visitNum = logVisit.getVisitNum();
                        nums = tbLogVisit.getVisitNum() + visitNum;
                        //统计耗时
                        Double visitTimeConsumingData = NumberUtils.Double(logVisit.getVisitTimeConsuming());
                        Double visitTimeConsumingParam = NumberUtils.Double(tbLogVisit.getVisitTimeConsuming());
                        Double sum = visitTimeConsumingData + visitTimeConsumingParam;
                        sums = sums + sum;
                    }
                    Double numDouble = NumberUtils.Double(String.valueOf(nums));
                    //统计平均耗时
                    Double avg = sums / numDouble;
                    tbLogVisit.setVisitTimeConsuming(avg.toString());
                    tbLogVisit.setVisitNum(nums);
                    count = tbLogVisitMapper.updateByExample(tbLogVisit,tbLogVisitExample);
                }else {
                    tbLogVisit.setCreateTime(new Date());
                    count = tbLogVisitMapper.insert(tbLogVisit);
                }
            }
    
            return count;
        }
    }
    

     

    这里提供我写的项目地址:https://github.com/javaliao/personalmall

    一开始没有设计好,后面强迫改动,导致访客的开始时间和结束时间都是最近一次的,不过我把每次请求的耗时改为平均耗时,勉强达到效果(不过有些请求异常的耗时时间长的就比较影响耗时统计了,唉不说了,最开始没有设计好,也算勉强了),效率也不算太差,不会太影响性能,

     

     

     

     

    展开全文
  • 一、说明互联网公司一般都会有专门的...日志埋点 就是通过程序打印 log 日志的方式进行业务/行为数据的记录二、总体架构通过 日志埋点 来实现业务监控和行为分析主要需要以下4个步骤数据生成(埋点)数据收集数据解...

    一、说明

    互联网公司一般都会有专门的数据团队对公司的一些业务指标负责;为了拿到这些基本的业务指标,一般也要工程团队去配合做一些数据采集工作,于是埋点诞生了。

    埋点的方式有很多种,本文主要介绍 日志埋点 这种方式以及实现思路和案例。

    日志埋点 就是通过程序打印 log 日志的方式进行业务/行为数据的记录

    二、总体架构

    f868c8e6ae56625bdee0c4f57f844c00.png

    通过 日志埋点 来实现业务监控和行为分析主要需要以下4个步骤

    1. 数据生成(埋点)
    2. 数据收集
    3. 数据解析(结构化)
    4. 数据落盘
    5. 数据使用(展示/分析)

    三、方案说明

    3.1. 数据生成

    日志数据的生成直接使用 Logback 等日志框架就可以了,可以自己封装公共方法、aop、注解等方式来生成指定的埋点日志

    但是为了便于后面的数据解析,日志数据需要规范先行

    1. 所有的埋点日志必需约定好统一的格式,例如:{时间}|{来源}|{对象id}|{类型}|{对象属性(以&分割)}按上面的格式生成的日志为:2019-11-07 10:32:01|api-gateway|1|request-statistics|ip=171.221.203.106&browser=CHROME&operatingSystem=WINDOWS_10
    2. 避免埋点的日志文件和系统本身输出的日志混淆埋点的日志输出的目录、文件等需要和应用本身的日志分离,通过 Logback 的配置就能实现

    埋点案例

    f3f4f5a9a10a2820ab8e226b37a6d000.png

    生成日志

    830f5123672d5ad7f2d67935601acda3.png

    网关埋点用户请求

    3.2. 数据收集

    关于日志数据的收集可选择的中间件比较多,除了图中的 FileBeat 之外还有 Flume、Fluentd、rsyslog 等;需要每台服务器都部署一个收集中间件。

    每台服务器部署一个就行了,就算一台服务器中启了多个微服务也是可以一齐收集

    PS:日志收集后面的 消息队列 并不是必需的可以去掉,但是增加 消息队列 后有以下两个优点

    1. 削峰填谷:减轻后面日志解析的压力
    2. 数据共享:日志数据除了提供给日志系统之外,可以增加消费端的同时提供给其他地方使用,如流计算等

    3.3. 数据解析

    使用 Logstash 的grok表达式解析日志数据并结构化,以上面的日志数据为例

    2019-11-07 10:32:01|api-gateway|1|request-statistics|ip=171.221.203.106&browser=CHROME&operatingSystem=WINDOWS_10

    结构化后的日志数据为:

    {    timestamp: '2019-11-07 10:32:01',    appName: 'api-gateway',    resouceid: '1',    type: 'request-statistics',    ip: '171.221.203.106',    browser: 'CHROME',    operatingSystem: 'WINDOWS_10'}

    3.4. 数据落盘

    通过 Logstash 能自动创建 Elasticsearch 索引并以天为单位分片

    fb619296d85d1d168c968eb25ec98a74.png

    可以通过索引模板来指定每个字段的类型和分词器等属性

    3.5. 数据使用

    日志数据落盘到 Elasticsearch 后,就可以通过聚合查询等方式实时显示监控数据或者分析日志数据

    监控案例

    0d651ba783562465f3b4b751071fa2a6.png

    聚合查询逻辑可参考 https://gitee.com/zlt2000/microservices-platform

    四、总结

    日志埋点 只是其中一种埋点手段而已,优点是系统无入侵且灵活;日志收集、解析、落盘等都可以灵活搭配选择不同的中间件,并且不需要修改源系统的代码;并且可以方便对接其他分析平台(例如: 大数据平台)

    PS:业务监控是否可以不做日志埋点,直接查询业务的数据库呢?(不建议这样做)

    1. 使用日志埋点能实现监控数据与业务数据分离,监控平台不会影响或增加业务数据库的压力
    2. 使用日志埋点能方便实现实时业务数据预警举个栗子:日志收集后面添加流计算中间件,计算某个时间窗口内优惠卷日志的数量或者金额大于某个阀值,则发出预警
    展开全文
  • hanboAuditjava编写的操作日志埋点插件,基于ORM框架的拦截器实现,目前兼容mybatis和hibernate。插件功能记录操作人、菜单、模块、功能以及数据库行级别操作前后的值,多用于审计日志展示。源码请移步这里插件使用...
  • (二)日志埋点

    2020-08-30 23:33:51
    1.日志埋点介绍 所谓埋点,就是在业务系统的程序中,植入一些收集事件数据的SDK(工具代码),进行各种事件的收集; 埋点分为两种 埋点代码可以植入到业务系统的后端程序中(比如java、php等) 也可以植入到业务...
  • 安卓日志埋点

    2020-11-09 14:41:23
    import android.app.Activity; import android.content.Context; import android.view....import java.util.ArrayList; import java.util.List; /** * 异常监控 * 使用: * 1.BaseActivity里重写该方法 * * @..
  • java抽象语法树处理,是基于sun的工具包进行的,所以先添加依赖 &lt;dependency&gt; &lt;groupId&gt;com.sun&lt;/groupId&gt; &lt;artifactId&gt;tools&lt;/artifactId&...
  • 第四篇, 日志埋点输出 & 动态配置支持前面基本上实现了一个非常简陋的爬虫框架模型,很多关键链路都没有日志,在分析问题时,就比较麻烦了,因此就有了这一篇博文其次就是解决前几篇遗留...
  • web日志埋点

    2014-06-23 17:46:59
    java"] [/code] 2、applicationContext-servlet.xml [code="java"] [/code] 3、com.youku.ddshow.cont...
  • 目前公司的方案是用mdc来实现一个请求的业务数据埋点记录,但是mdc是map方式,需要手动设置key,而且每次都要手动clear,一是不方便管理,再者如果忘记clear会造成业务埋点数据混乱。所以有了想要把埋点数据字段统一...
  • 1)定义注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { String value() default ""; } 2)定义entity 这里我用的jpa ... * @description 行为日志类 * @date 20...
  • 安卓埋点日志(简化)

    2020-11-09 15:48:09
    import android.app.Activity; import android.content.Context; import android.view.MotionEvent; import android.view.View;... * 可以做日志埋点 * 异常监控 * 使用: * 1.BaseActivity里重写..
  • 背景: 最近想使用openresty埋点日志的回收,埋点日志通过java代码AES/ECB/NoPadding加密的。关于AES加解密https://blog.csdn.net/e_Inch_Photo/article/details/80455399 这篇文章写得较为清晰,不在赘述。我的加密...
  • 待续。
  • http://blog.csdn.net/java2013liu/article/details/53380381 rules--automatic breakpoint --before response 在下方输入 bpafter myzaker.com,没筛选到时,输入go放行 request勾选inspect...
  • importjava.net.URLDecoderimportjava.sql.{Connection, DriverManager}importcom.spark.common.{EventLogConstants, LoggerUtil, Test, TimeUtil}importkafka.serializer.StringDecoderimportorg.apache.hadoop.hba...
  • 定义一个埋点接口 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface Metrics { boolean recordSuccessMetrics() default true; boolean ...
  • 此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为...
  • 数据的价值 APM  目标  对应用的性能、业务可靠性进行线上的监控和预警  采集内容  系统指标,应用性能指标,Crash,自定义日志等 用户行为   目标  精细化运营  采集内容 ...Java...
  • 此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为...
  • AspectJ是AOP思想的实现方式,将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的...
  • 很多时候,当我们需要做接口的 埋点,QPS统计的时候可能会依靠一些第三方,现在还需要吗,NO,我们自己来做 直接上代码!!!!!!!!!!!!!!!!!!!!!!! 首先定义一个实体类,来存放我们需要的...

空空如也

空空如也

1 2 3 4
收藏数 69
精华内容 27
关键字:

java日志埋点

java 订阅