2021年7月28日——日志技术(早期)

日志技术——早期

一、日志概念

记录信息,方便查错

  • 调试日志:开发时调试使用
  • 系统日志:生产环境找错使用

二、Java日志技术

1. 门面概念

日志框架层出不穷,如果没有统一接口,将大大增加用户的学习成本,扩展性也不好。所以现有日志框架都可以用门面来做到一套编码,任意更换,程序员面向接口编程,不直接使用日志的实现。

2. 现有日志框架

  • 日志门面
    • JCL、slf4j
  • 日志实现
    • JUL(java自带)、logback、log4j、log4j2(log4j的升级版)

三、JUL 学习

JUL —— java util logging,java的原生框架

1. 架构介绍

  • Loggers:产生日志
  • Appenders(Handlers):决定日志输出到哪
  • Layouts(Formatters): 决定日志输出的形式,xml还是其他的格式
  • Level:标记日志消息的重要性的紧迫性
  • Filters:根据不同的日志的重要性(级别),决定哪些日志会被过滤掉

2. 快速入门

  • 产生方式

net.logger_JUL.test.TestJUL

@Test
public void testQuick() {
    // 1. 创建日志记录器对象
    Logger logger = Logger.getLogger("net.logger_JUL.test.TestJUL");

    // 2. 产生日志记录(默认输出到控制台)
    logger.info("我是info信息");
    logger.log(Level.INFO, "我也是info信息");
    // 日志的占位参数
    logger.log(Level.INFO, "用户信息:{0},{1}", new Object[]{"leo", "18"});
}


/****************************** 输出 ******************************

七月 23, 2021 11:51:47 上午 net.logger_JUL.test.TestJUL testQuick
信息: 我是info信息
七月 23, 2021 11:51:47 上午 net.logger_JUL.test.TestJUL testQuick
信息: 我也是info信息
七月 23, 2021 11:51:47 上午 net.logger_JUL.test.TestJUL testQuick
信息: 用户信息:leo,18

******************************************************************/
  • 默认日志级别

net.logger_JUL.test.TestJUL

@Test
public void testLevelA() {
    /**
     * 一条日志他会有level属性,只有他的leve大于等于设置的level等级,它才会被输出
     * <ul>
     * <li>OFF (value = Integer.MAX_VALUE),用来关闭所有日志输出
     * <li>SEVERE (highest value)
     * <li>WARNING
     * <li>INFO
     * <li>CONFIG
     * <li>FINE
     * <li>FINER
     * <li>FINEST  (lowest value)
     * <li>All (value = Integer.MIN_VALUE),用来开启所有日志输出
     * </ul>
     */

    // 默认Level是info,所以只有info以上才会被输出
    Logger logger = Logger.getLogger("net.logger_JUL.test.TestJUL");

    logger.log(Level.SEVERE, "severe");
    logger.log(Level.WARNING, "warning");
    logger.log(Level.INFO, "info");
    logger.log(Level.CONFIG, "config");
    logger.log(Level.FINE, "fine");
    logger.log(Level.FINER, "finer");
    logger.log(Level.FINEST, "finest");
}


/****************************** 输出 ******************************

七月 23, 2021 11:56:35 上午 net.logger_JUL.test.TestJUL testLevelA
严重: severe
七月 23, 2021 11:56:35 上午 net.logger_JUL.test.TestJUL testLevelA
警告: warning
七月 23, 2021 11:56:35 上午 net.logger_JUL.test.TestJUL testLevelA
信息: info

******************************************************************/
  • 自定义级别

net.logger_JUL.test.TestJUL

@Test
public void testLevelB() {
    Logger logger = Logger.getLogger("net.logger_JUL.test.TestJUL");

    // 1.关闭系统默认配置
    // 不关闭的话logger会把日志也发一份给它的父亲,所以表现为info级别以上的日志打印了两遍
    logger.setUseParentHandlers(false);
    // 2.创建handler对象
    ConsoleHandler consoleHandler = new ConsoleHandler();
    // 3.创建formatter对象
    SimpleFormatter simpleFormatter = new SimpleFormatter();
    // 4.进行关联
    consoleHandler.setFormatter(simpleFormatter);
    logger.addHandler(consoleHandler);
    // 5.设置日志级别
    logger.setLevel(Level.ALL);
    consoleHandler.setLevel(Level.ALL);

    logger.log(Level.SEVERE, "severe");
    logger.log(Level.WARNING, "warning");
    logger.log(Level.INFO, "info");
    logger.log(Level.CONFIG, "config");
    logger.log(Level.FINE, "fine");
    logger.log(Level.FINER, "finer");
    logger.log(Level.FINEST, "finest");
}


/****************************** 输出 ******************************

七月 23, 2021 1:52:46 下午 net.logger_JUL.test.TestJUL testLevelB
严重: severe
七月 23, 2021 1:52:46 下午 net.logger_JUL.test.TestJUL testLevelB
警告: warning
七月 23, 2021 1:52:46 下午 net.logger_JUL.test.TestJUL testLevelB
信息: info
七月 23, 2021 1:52:46 下午 net.logger_JUL.test.TestJUL testLevelB
配置: config
七月 23, 2021 1:52:46 下午 net.logger_JUL.test.TestJUL testLevelB
详细: fine
七月 23, 2021 1:52:46 下午 net.logger_JUL.test.TestJUL testLevelB
较详细: finer
七月 23, 2021 1:52:46 下午 net.logger_JUL.test.TestJUL testLevelB
非常详细: finest

******************************************************************/

3. 父子关系

JUL中的Logger之间存在父子关系,JUL初始时会创建一个顶层RootLogger作为所有Logger的父亲。子Logger默认会向父Logger的Handlers发送日志拷贝,从而子Logger的日志通过父Logger之手输出。

net.logger_JUL.test.TestJUL

@Test
public void testInherit() {
    Logger logger_f = Logger.getLogger("net.logger_JUL");
    Logger logger_s = Logger.getLogger("net.logger_JUL.test");

    // 谁是父亲谁是儿子,在命名的那刻就决定了
    System.out.println(logger_s.getParent() == logger_f);

    // logger_f 的父亲就是 RootLogger,而不是叫“net”的家伙,并且 RootLogger 无名
    System.out.println("logger_f's father: " + logger_f.getParent());
    System.out.println("logger_f's fatherName: " + logger_f.getParent().getName());

    // 现对 logger_f 自定义,看 logger_s 是否同父亲一样也自定义了
    // 其实子 Logger 就是啥都不做,全交给父 Logger 了
    logger_f.setUseParentHandlers(false);
    ConsoleHandler consoleHandler = new ConsoleHandler();
    consoleHandler.setFormatter(new SimpleFormatter());
    consoleHandler.setLevel(Level.ALL);
    logger_f.setLevel(Level.ALL);
    logger_f.addHandler(consoleHandler);

    logger_s.log(Level.SEVERE, "severe");
    logger_s.log(Level.WARNING, "warning");
    logger_s.log(Level.INFO, "info");
    logger_s.log(Level.CONFIG, "config");
    logger_s.log(Level.FINE, "fine");
    logger_s.log(Level.FINER, "finer");
    logger_s.log(Level.FINEST, "finest");
}


/****************************** 输出 ******************************

true
logger_f's father: java.util.logging.LogManager$RootLogger@61e717c2
logger_f's fatherName: 
七月 23, 2021 2:55:07 下午 net.logger_JUL.test.TestJUL testInherit
严重: severe
七月 23, 2021 2:55:07 下午 net.logger_JUL.test.TestJUL testInherit
警告: warning
七月 23, 2021 2:55:07 下午 net.logger_JUL.test.TestJUL testInherit
信息: info
七月 23, 2021 2:55:07 下午 net.logger_JUL.test.TestJUL testInherit
配置: config
七月 23, 2021 2:55:07 下午 net.logger_JUL.test.TestJUL testInherit
详细: fine
七月 23, 2021 2:55:07 下午 net.logger_JUL.test.TestJUL testInherit
较详细: finer
七月 23, 2021 2:55:07 下午 net.logger_JUL.test.TestJUL testInherit
非常详细: finest

******************************************************************/

4. 配置文件

默认配置文件路径:$JAVAHOME\jre\lib\logging.properties

logging.properties

# 不向父亲发送日志
net.logger_JUL.test.TestJUL.useParentHandlers = false
# 指定handler
net.logger_JUL.test.TestJUL.handlers = java.util.logging.ConsoleHandler,java.util.logging.FileHandler
# 设置日志级别
net.logger_JUL.test.TestJUL.level = ALL

# 设置ConsoleHandler的级别
java.util.logging.ConsoleHandler.level = ALL
# 设置ConsoleHandler的formatter
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# 设置FileHandler的输出目录,这里的 / 是用户目录
java.util.logging.FileHandler.pattern = java%u.log
java.util.logging.FileHandler.level = INFO
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 设置formatter输出格式
java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n
# 设置一个日志文件的大小
java.util.logging.FileHandler.limit = 50000
# 输出日志文件限制个数
java.util.logging.FileHandler.count = 10
# 输出日志文件 是否是追加
java.util.logging.FileHandler.append = true
java.util.logging.FileHandler.encoding = utf-8

net.logger_JUL.test.TestJUL

@Test
public void testConfigFile() throws IOException {
    InputStream inputStream = TestJUL.class.getClassLoader().getResourceAsStream("logging.properties");
    LogManager logManager = LogManager.getLogManager();
    logManager.readConfiguration(inputStream);

    Logger logger = Logger.getLogger("net.logger_JUL.test.TestJUL");
    logger.log(Level.SEVERE, "severe");
    logger.log(Level.WARNING, "warning");
    logger.log(Level.INFO, "info");
    logger.log(Level.CONFIG, "config");
    logger.log(Level.FINE, "fine");
    logger.log(Level.FINER, "finer");
    logger.log(Level.FINEST, "finest");
}


/****************************** 输出 ******************************

七月 23, 2021 2:53:25 下午 net.logger_JUL.test.TestJUL testConfigFile
严重: severe
七月 23, 2021 2:53:25 下午 net.logger_JUL.test.TestJUL testConfigFile
警告: warning
七月 23, 2021 2:53:25 下午 net.logger_JUL.test.TestJUL testConfigFile
信息: info
七月 23, 2021 2:53:25 下午 net.logger_JUL.test.TestJUL testConfigFile
配置: config
七月 23, 2021 2:53:25 下午 net.logger_JUL.test.TestJUL testConfigFile
详细: fine
七月 23, 2021 2:53:25 下午 net.logger_JUL.test.TestJUL testConfigFile
较详细: finer
七月 23, 2021 2:53:25 下午 net.logger_JUL.test.TestJUL testConfigFile
非常详细: finest

日志文件内容略
******************************************************************/

5. 日志原理解析

四、LOG4J 学习

Apache 下的日志框架。

1. 快速入门

net.logger_log4j.test.TestLog4j

@Test
public void testQuick() {
    // 1. 初始化配置,不需要配置文件
    BasicConfigurator.configure();

    // 2. 创建日志记录器对象
    Logger logger = Logger.getLogger(TestLog4j.class);

    // 3. 日志记录输出
    logger.fatal("fatal");

    // 以下四个为常用级别
    logger.error("error");
    logger.warn("warn");
    logger.info("info");
    logger.debug("debug"); // 默认 debug 级别

    logger.trace("trace");
    // 和 JUL 一样,OFF 和 All 用来关闭和打开日志
}

2. Log4j 组件

  • Loggers

    • 收集处理日志记录
    • 命名是类的全限定类名,和JUL一样根据名称决定logger之间的继承关系
    • 例如:net.logger_log4j.test 是 net.logger_log4j.test.TestLog4j 父亲
    • 所有 logger 顶层父亲是 root,用 getRootLogger() 来获取
    • 自定义的 logger 默认没有 appender,所以用的是父亲的 appender
  • Appenders

    • 用来指定日志输出到哪里
    输出端类型作用
    ConsoleAppender输出到控制台
    FileAppender输出到文件中
    RollingFileAppenderFileAppender的子类,根据文件大小拆分日志文件
    DailyRollingFileAppenderFileAppender的子类,每天输出到一个新的文件
    JDBCAppender将日志保存到数据库
  • Layouts

    • 用来控制日志的输出格式
    输出格式类作用
    HTMLLayout输出为html格式
    SimpleLayout输出为简单文本格式
    PatternLayout自定输出格式

3. Layout 格式

占位符含义
%m输出日志信息
%p输出日志信息的等级
%n输出换行
%r输出自应用启动到输出该 log 信息耗费的毫秒数
%c输出打印语句所属的类的全名
%t输出产生该日志的线程全名
%d输出服务器当前时间,默认为 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss}
%%输出%
%F输出日志消息产生时所在的文件名称
%L输出代码中的行号
%l输出日志时间发生的位置,包括类名、及在代码中的行数。

4. Appender 输出

classpath:log4j.properties

# 这里root没有指定appender,所以即使子logger把日志传给它,也不会有任何输出
# 级别,appender的名字,另一个appender的名字...
log4j.rootLogger=info
# 自定义logger
log4j.logger.net.logger_log4j.test = trace, Console

## 输出到控制台配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
# 指定layout
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.conversionPattern=%l %p %r %d %F %m%n

## 输出到文件配置
log4j.appender.A = org.apache.log4j.DailyRollingFileAppender
# 指定日志输出路径
log4j.appender.A.file = D:/log.txt
# 设置为追加模式
log4j.appender.A.append = true
# 指定layout
log4j.appender.A.layout = org.apache.log4j.PatternLayout
log4j.appender.A.layout.conversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [%t:%r] -[%p] %m%n
log4j.appender.A.encoding = utf-8

## 输出到数据库设置
log4j.appender.LogDB = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.LogDB.layout = org.apache.log4j.PatternLayout
log4j.appender.LogDB.driver = com.mysql.jdbc.Driver
log4j.appender.LogDB.URL = jdbc:mysql://localhost:3306/log?useUnicode=true&characterEncoding=utf8
log4j.appender.LogDB.user = root
log4j.appender.LogDB.password = 123456
log4j.appender.LogDB.sql = INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('log4j_test','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')

sql 语句

CREATE TABLE `log` (
`log_id` int(11) NOT NULL AUTO_INCREMENT,
`project_name` varchar(255) DEFAULT NULL COMMENT '目项名',
`create_date` varchar(255) DEFAULT NULL COMMENT '创建时间',
`level` varchar(255) DEFAULT NULL COMMENT '优先级',
`category` varchar(255) DEFAULT NULL COMMENT '所在类的全名',
`file_name` varchar(255) DEFAULT NULL COMMENT '输出日志消息产生时所在的文件名称 ',
`thread_name` varchar(255) DEFAULT NULL COMMENT '日志事件的线程名',
`line` varchar(255) DEFAULT NULL COMMENT '号行',
`all_category` varchar(255) DEFAULT NULL COMMENT '日志事件的发生位置',
`message` varchar(4000) DEFAULT NULL COMMENT '输出代码中指定的消息',
PRIMARY KEY (`log_id`)
);

net.logger_log4j.test.TestLog4j

@Test
public void testLayout() {
    Logger logger = Logger.getLogger(TestLog4j.class);

    // 输出试试
    logger.fatal("fatal");
    logger.error("error");
    logger.warn("warn");
    logger.info("info");
    logger.debug("debug");
    logger.trace("trace");
}
  • 配置文件总结
    • 即使 RootLogger,它初始也没任何appenders,所以需要配置
    • 定义 logger,指定等级,appenders
    • 定义 appenders,指定layout

五、JCL 学习

1. JCL 是什么

  • JCL —— Jakarta Commons Logging,Apache 提供的通用日志API
  • 为 “所有” Java 日志实现提供统一接口,自身也提供一个日志实现 —— SimpleLog。
  • 上面的 “所有”,指的是:Log4j、JUL、SimpleLog
  • 两个基本抽象类:Log(日志记录) 和 LogFactory(创建Log实例)

2. 快速入门

net.logger_JCL.test.TestJCL

@Test
public void testQuick() {
    Log log = LogFactory.getLog(TestJCL.class);

    log.fatal("fatal");
    log.error("error");
    log.warn("warn");
    log.info("info");
    log.debug("debug");
}

3. JCL 原理

  • LogFactory 动态加载Log实现类

  • 日志门面支持的日志实现数组:

    • org.apache.commons.logging.impl.LogFactoryImpl 类

    • private static final String[] classesToDiscover = new String[]{"org.apache.commons.logging.impl.Log4JLogger"
          , "org.apache.commons.logging.impl.Jdk14Logger"
          , "org.apache.commons.logging.impl.Jdk13LumberjackLogger"
          , "org.apache.commons.logging.impl.SimpleLog"};
      
  • 获取具体日志的实现

    • org.apache.commons.logging.impl.LogFactoryImpl 类

    • private Log discoverLogImplementation(String logCategory) 方法

    • for(int i = 0; i < classesToDiscover.length && result == null; ++i) {
          result = this.createLogFromClass(classesToDiscover[i], logCategory, true);
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值