引言:
日志大家都再熟悉不过了,日常开发中经常要用到,甲方接口人最喜欢对我说的话就是“赶紧去查一下日志,快点!”,说话的同时瞪着个铜铃大的眼睛。
就你接口人知道日志吗?还要你指挥啊,我心里可真来劲,但是没办法谁叫我是乙方的,我想大口喘气,也得拿手捂着,兄弟们都指着我好好说话呢,我这话一出口,我这一众兄弟这个月的绩效恐怕就打水漂了。
就在我进行心里斗争的时候,接口人又说了“什么时候能定位到问题,啥时候能解决?”。(甲方规定:超1小时通报、超过2小时扣分、超4小时约谈领导、24小时没搞定打包回家)。
下面恭迎今天的主角“日志”大神出场。
什么是日志
日志:记录程序的运行轨迹,打印一下设定的信息,比如错误,很方便查找关键信息,快速定位解决问题。
Java程序员在开发项目时都是依赖debug来调试错误、跟踪代码来解决bug,比如我们开发工具Eclipse/MyEclipse就有很强大错误调试功能,比如我自己就有4个版本的:
(我怀疑你在做广告)
当然在开发环境下,要调试这些错误很容易,打好断点跟踪代码就行了,但项目发布到了测试、生产环境怎么办?不好意思你调试不了,甲方会断了一切你可以这么做的念想。所以日志就派上用场了,日志打印的好,就能根据日志的轨迹快速定位并解决线上问题,反之你光凭分析代码、分析数据,脑细胞胡乱打架是没法快速解决问题的,日志记录的信息能够很好的帮助开发人家快速定位到问题,并且解决。
记录日志的方式
有很多记录日志的方式,比如 log4j、Logging、commons-logging、slf4j 等等,这里呢我就说说我们做项目经常用到的几种。
log4j
配置文章可以看我的文章 Log4j配置详解
我用的是 log4j-1.2.15.jar 这个包,将其放在lib下
输出到控制台
log4j.properties配置
-
# DEBUG 日志优先级,stdout(自定义名字) 代表日志输出到那个地方
-
log4j.rootLogger= ERROR, stdout
-
# 设置日志输出类型 appender负责控制日志记录操作的输出 ConsoleAppender:日志信息输出到控制台
-
log4j.appender.stdout= org.apache.log4j.ConsoleAppender
-
# 日志自定义格式
-
log4j.appender.stdout.layout= org.apache.log4j.PatternLayout
-
# 日志输出格式为 优先级、 [产生日志事件的线程名]、日志信息、换行
-
log4j.appender.stdout.layout.ConversionPattern= %
5p [%t] %m%n
-
测试代码
-
package com.
log;
-
-
import org.apache.log4j.Logger;
-
-
public
class Log4jTest
-
{
-
Logger logger = Logger.getLogger(
this.getClass());
-
-
public void test()
-
{
-
logger.debug(
"debug print");
-
logger.info(
"info print");
-
logger.warn(
"warn print");
-
logger.error(
"error print");
-
}
-
/**
-
* @param args
-
*/
-
public static void main(String[] args)
-
{
-
new Log4jTest().test();
-
}
-
-
}
因配置的是ERROR,所以只输出error的信息
输出到日志文件
log4j.properties配置
-
# DEBUG 日志优先级,stdout(自定义名字) 代表日志输出到那个地方
-
log4j.rootLogger= ERROR, stdout ,myLog
-
# 设置日志输出类型 appender负责控制日志记录操作的输出 ConsoleAppender:日志信息输出到控制台
-
log4j.appender.stdout= org.apache.log4j.ConsoleAppender
-
# 日志自定义格式
-
log4j.appender.stdout.layout= org.apache.log4j.PatternLayout
-
# 日志输出格式为 优先级、 [产生日志事件的线程名]、日志信息、换行
-
log4j.appender.stdout.layout.ConversionPattern= %
5p [%t] %m%n
-
-
### 输出到日志文件 ###
-
log4j.appender.myLog = org.apache.log4j.DailyRollingFileAppender
-
log4j.appender.myLog.File = E:\\testlog\\log4j\\server.log
-
log4j.appender.myLog.Append =
true
-
## 只输出DEBUG级别以上的日志
-
log4j.appender.myLog.Threshold = DEBUG
-
#'.'yyyy-MM-dd: 每天产生一个新的文件
-
log4j.appender.myLog.DatePattern =
'.'yyyy-MM-dd
-
log4j.appender.myLog.layout = org.apache.log4j.PatternLayout
-
log4j.appender.myLog.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [%t:%r] - [%p] [%c{
1}:%L] [%M] %m%n
-
log4j.additivity.myLog =
false
测试代码还是之前的,运行结果如下:
自定义文本方式
每天记录一个日志文件,这个日志文件把今天比较重要的日志记录下来。
每个日志文件记录的形式(时间+组装好的内容),如下 :
是不是有个疑问,如果有多个服务器,日志是记录到代码执行的这个服务器上了,外面访问进来肯定会访问到不同的服务器上了,日志文件也肯定存在多个咯,查问题的时候不就得一个个服务器的日志都翻一遍,烦人吗不是,这个时候你请搞工程的兄弟吃个夜宵他就会告诉你,其实服务器是可以搞成共享一个目录的,多个服务器访问同一个日志文件也是可以的(具体我就不讲了),但说实话这种方式我们现在也只是偶尔用一下。
示意代码
-
package com.log;
-
-
import java.io.BufferedWriter;
-
import java.io.FileOutputStream;
-
import java.io.IOException;
-
import java.io.OutputStreamWriter;
-
import java.text.SimpleDateFormat;
-
import java.util.Date;
-
-
public
class FileLog {
-
-
public
static
void writeLog(
String file,
String conent) {
-
BufferedWriter out =
null;
-
try {
-
out =
new BufferedWriter(
new OutputStreamWriter(
-
new FileOutputStream(file,
true)));
-
out.write(conent);
-
out.newLine();
//换行
-
}
catch (Exception e) {
-
e.printStackTrace();
-
}
finally {
-
try {
-
out.close();
-
}
catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
-
/**
-
* @param args
-
*/
-
public
static
void main(
String[] args) {
-
SimpleDateFormat df =
new SimpleDateFormat(
"yyyy-MM-dd");
-
SimpleDateFormat dfTime =
new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
-
Date date =
new
Date();
-
//当前时间 方便日志查看
-
String curTime = dfTime.format(date);
-
//yyyy-MM-dd格式的,为了每日更换日志文件
-
String curDay = df.format(date);
-
//组装日志内容
-
String text=
"日志内容"+org.apache.commons.lang.RandomStringUtils.randomNumeric(
4);
-
String logStr=
"["+curTime+
"]"+text;
-
//指定日志文件路径
-
String fileName=
"E:/testlog/"+curDay+
".txt";
-
System.out.println(
"start");
-
//打印日志
-
writeLog(fileName, logStr);
-
System.out.println(
"end");
-
}
-
-
}
记录到数据库
我们用的最多的场景是接口,把收发报文记录下来,如果接口发生错误的话,将错误的信息记录成一条数据。
一旦发生接口调用争议的实话,这个就派上用场了,场景:
对端是个大厂很大的那种(甲方都不吊的那种),你送了数据过去,然后对端说没收到或者说收到的有问题,但人家又不说具体什么问题,也不给报文,就做甩手掌柜,接口人只好找你了(你懂的,柿子都找软的捏),你如果没有调用报文和返回报文的记录的话,你就等着哭吧,挨骂你只能忍着,就算你确定你的程序没问题也没有用,你得有证据,你就得想方设法的找报文,但如果要从日志找的话不是那么好找,甚至有时候找不到。
记录到库就很好找,根据工单号、主键或者发生时间等等,可以很容易发现问题、定位问题,然后迅速把发送的报文、返回的报文发给接口人,手一甩就剩下你心情愉悦的摸鱼了。
我一般会记录这些信息
来源系统 | 对端系统/自己的系统代号 |
业务 | 不同的模块写不同的编码,方便区分 |
工单号 | 唯一标示即可,看自己系统的需求 |
方法名 | 调用的方法 |
类型 | 接收或者发送 |
状态 | 成功或者失败 |
报文 | 具体的报文信息 |
操作时间 | 创建时间 |
当然有可能会涉及到比较长的报文,所以我们设计的是有好几个字段来存,不够的就往后一个字段放,比如:
-
String
xmlInfo = vo.getXmlInfo();
-
int
xmlLength = xmlInfo.length();
-
int
count = xmlLength / 1000;
-
if
(count * 1000 < xmlLength)
-
count++;
-
-
String
xmlArr[] = new String[count];
-
for
(int i = 0; i < count; i++)
-
{
-
int
endLength = (i + 1) * 1000;
-
if
(endLength > xmlLength)
-
endLength =
xmlLength;
-
xmlArr[i] =
xmlInfo.substring(i * 1000, endLength);
-
if
(i == 0)
-
vo.setXmlInfo1(xmlArr[i]);
-
else
if (i == 1)
-
vo.setXmlInfo2(xmlArr[i]);
-
else
if (i == 2)
-
vo.setXmlInfo3(xmlArr[i]);
-
else
if (i == 3)
-
vo.setXmlInfo4(xmlArr[i]);
-
else
if (i == 4)
-
vo.setXmlInfo5(xmlArr[i]);
-
else
if (i == 5)
-
vo.setXmlInfo6(xmlArr[i]);
-
else
if (i == 6)
-
vo.setXmlInfo7(xmlArr[i]);
-
else
if (i == 7)
-
vo.setXmlInfo8(xmlArr[i]);
-
}
最后
通常情况下在程序日志里记录一些比较有意义的数据:程序启动、登录、退出、重要变量的状态变化、重要数据的执行记录、接口调用报文的记录、定时执行相关任务的状况 等等。
关注下方公众号,有更多资料、实例代码、面试技巧奉上!
转载:https://blog.csdn.net/dkm123456/article/details/115955604