线上日志操作

文章介绍了Log4j的日志级别,包括ALL,FATAL,ERROR,WARN,INFO,DEBUG,TRACE和OFF,强调了在生产环境中的使用策略。此外,讨论了如何在代码中写入日志,如标记接口名、事件错误和警告。文章还提到了利用AOP进行日志增强的方法,并给出了具体的Java代码示例。最后,讨论了线上日志的排查技巧,包括使用`tail`,`less`命令以及阿里云日志服务SLS进行问题定位。
摘要由CSDN通过智能技术生成

1:日志级别:

Log4j 共八个日志级别:

ALL :用于打开所有日志记录。

FATAL :严重,严重的错误事件,会导致程序退出,需要运维马上介入,慎用。

ERROR :错误,错误事件,影响正常使用,但不影响系统的继续运行。

WARN :警告,预期之外的运行状况,可能引起潜在的错误情况,比如大量时延过大;一般是系统资源等技术原因触发。

INFO :提示,粗粒度记录程序的正常运行过程中的关键信息。

DEBUG :调试,细粒度记录程序的正常运行过程中的信息,帮助调试和诊断。生产环境中删除绝大部分。

TRACE :跟踪,细粒度记录应用程序的正常运行过程中的信息,帮助调试和诊断应用程序,比DEBUG更细粒度。生成环境中删除绝大部分。

OFF:用于关闭所有日志记录。

忽略All和OFF也可以说是六个日志级别;优先级从上到下递增

如果将log level设置在某一个级别上,那么比此级别优先级高的log都能打印出来。例如,如果设置优先级为WARN,那么OFF、FATAL、ERROR、WARN 4个级别的log能正常输出,而INFO、DEBUG、TRACE、 ALL级别的log则会被忽略

常用的有 4 个 INFO 、 WARN 、 ERROR 、 FATAL ,生产系统一般只打印 INFO 级别以上的日志信息(需要部署和运维时保证不会出现存储空间不足等问题),对于 DEBUG 级别的日志,只在测试环境中打印,代 码稳定后建议删除。

2:日志写哪里:

1:入参与结果:

sendVerifyCode标识出接口名方便查找

例:

if (redisFlag) {
            boolean result = RedisManager.getRedisManager(Constant.REDIS_COMMON_CONFIG).setEx(Constant.BIND_KEY_PREFIX + bindId, littlecConfig.getConfigAsInt("bind.odas.expiredDuration", 100),
                    bindData.toJSONString());
            log.info("saveBindData in redis success, bindId={}, bindData={}, result={}", bindId, bindData, result);
        }
        if (mysqlFlag) {
            Integer insertResult = odasBindMybatis.insertBindData(cameraBindDto);
            log.info("saveBindData in mysql success,bindId={},cameraBindDto={}, result={}", bindId, JSON.toJSONString(cameraBindDto), insertResult);
        }

2:标识出事件错误和警告等

error:标识出接口名与,关键参数,与异常

warn:标识出告警信息,与关键参数

3:可以对日志进行统一的收集

如:

利用AOP增强方法,写入日志

package com.cmcc.littlec.camera.util;
​
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.protobuf.Message;
import com.googlecode.protobuf.format.JsonFormat;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
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.stereotype.Component;
​
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
​
// TODO: Auto-generated Javadoc
​
/**
 * Created by caojiantong on 2017/12/8.
 */
@Aspect
@Component
public class ServiceLogAspect {
​
    /**
     * The Constant LOG.
     */
    private static final Logger LOG = LoggerFactory.getLogger(ServiceLogAspect.class);
​
    /**
     * The Constant dateFormat.
     */
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
​
    /**
     * The Constant STRING_START.
     */
    private static final String STRING_START = "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n";
​
    /**
     * The Constant STRING_END.
     */
    private static final String STRING_END = "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n";
​
​
    /**
     * Innser service pointcut.
     */
    @Pointcut(value = "execution(* com.cmcc.littlec.camera.service.inner.impl.CameraServiceImpl.*(..))")
    public void innserServicePointcut() {
    }
​
    /**
     * Around.
     *
     * @param joinPoint the join point
     * @return the object
     */
    @Around(value = "innserServicePointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        JsonFormat jf = new JsonFormat();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = method.getDeclaringClass();
        String target = targetClass.getName() + "#" + method.getName();
        target = StringUtils.substring(target, target.lastIndexOf(".") + 1);
        String request = jf.printToString((Message) joinPoint.getArgs()[0]);
        String params = JSONObject.toJSONStringWithDateFormat(request, DATE_FORMAT, SerializerFeature.WriteMapNullValue);
        try {
            long start = System.currentTimeMillis();
            Object result = joinPoint.proceed();
            long timeConsuming = System.currentTimeMillis() - start;
            LOG.info("dubbo context info, Method={}, remoteHost:{}, providerSide:{}, ", target,
                    RpcContext.getContext().getRemoteHost(), RpcContext.getContext().isProviderSide());
            LOG.info("dubbo调用结束,接口: {}, 参数:{} 耗时: {}ms", target, params, timeConsuming);
            return result;
        } catch (Throwable throwable) {
            String errorMsg = null;
            try (StringWriter sw = new StringWriter();
                 PrintWriter pw = new PrintWriter(sw)) {
                throwable.printStackTrace(pw);
                errorMsg = sw.toString();
            }
            LOG.error("dubbo接口异常, method={}, error:{}, param={}, {}", target, throwable.getMessage(), params, errorMsg);
            // 对外抛出异常进行处理, 只返回第一级异常栈
            // StackTraceElement[] stackTrace = throwable.getStackTrace();
            // if (stackTrace != null && stackTrace.length > 0) {
            //     throwable.setStackTrace(new StackTraceElement[]{stackTrace[0]});
            // }
            // throw throwable;
            return null; // 暂时保留老方案
        }
    }
}
​

4:写日志注意事项:

1:占位符不能少

占位符{ }少了无法写入

2:转换成string类型

对象等非string类型需要转换成string类型,否则无法写入

3:日志里的,用英文符号

方便阿里云日志的查询(阿里云日志的查询中文的,无法分词)

5:线上日志排查

首先进入日志所在目录,找到对应的日志文件

tail

tail -f  xxx.log

实时查看日志文件,可以ctrl+C退出

tail - 100f xxx.log

实时查看日志文件 后一百行

less

查看线上日志时如果用 tail -f 用经常滚动而已不容易定位问题,可以用less命令。

less xxx.log

shift+G直跳到最后一行

?+要搜索的内容 ?是向上查询,/是向下查询

n向下查找

less -N xxx.log

带出行号查看文件

grep

grep "关键词"  xxx.log

根据关键词查看日志 并返回关键词所在行:

cat xxx.log|grep "关键词"

6:阿里云线上日志排查

正常情况我们无法连接到生产服务器,排查线上问题时可以通过阿里云查询

1:阿里云搜索:日志服务SLS控制台

2:找到project列表,这里有全部服务器的地址,找到你需要的服务器

3:查询写法类似于sql

例:查询所有/camera/playback/snapshot但排除掉/camera/playback/v2/snapshot

* and request_uri:"/camera/playback/snapshot" and not request_uri:"/camera/playback/v2/snapshot"

 例:查询所getPlaybackDownloadUrls接口的返回信息

* and message:"response" and message:"getPlaybackDownloadUrls"

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

admiraldeworm

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

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

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

打赏作者

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

抵扣说明:

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

余额充值