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 级别的日志,只在测试环境中打印,代 码稳定后建议删除。
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); }
error:标识出接口名与,关键参数,与异常
warn:标识出告警信息,与关键参数
如:
利用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; // 暂时保留老方案 } } }
占位符{ }少了无法写入
对象等非string类型需要转换成string类型,否则无法写入
方便阿里云日志的查询(阿里云日志的查询中文的,无法分词)
首先进入日志所在目录,找到对应的日志文件
tail -f xxx.log
实时查看日志文件,可以ctrl+C退出
tail - 100f xxx.log
实时查看日志文件 后一百行
查看线上日志时如果用 tail -f
用经常滚动而已不容易定位问题,可以用less命令。
less xxx.log
shift+G直跳到最后一行
?+要搜索的内容 ?是向上查询,/是向下查询
n向下查找
less -N xxx.log
带出行号查看文件
grep "关键词" xxx.log
根据关键词查看日志 并返回关键词所在行:
cat xxx.log|grep "关键词"
正常情况我们无法连接到生产服务器,排查线上问题时可以通过阿里云查询
例:查询所有/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"