深入理解Java(七)—— 日志技术和单元测试

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 日志文件是用于记录系统操作事件的文件集合,可分为事件日志和消息日志。具有处理历史数据、诊断 问题的追踪以及理解系统的活动等重要作用;而单元测试则是指对软件中的最小可测试单元进行检查和验证。在本文中,我们会详细谈论目前市面上主流的日志技术和单元测试框架。

7 日志技术和单元测试

作者:来自ArimaMisaki创作

[TOC]

7.1 日志技术

7.1.1 基本概念

程序中的日志:程序中的日志可以用来记录程序运行过程的信息。

输入信息的转变方式

  • 传统记录日志:使用输出语句观察程序运行的过程
  • 如今记录日志:使用API查看日志

输出语句的弊端

  • 信息只能展示在控制台
  • 不能将其记录到其他位置
  • 想取消记录的信息需要修改代码才可以完成

记录日志的优势

  • 可以被定向到不同的处理器,用于在控制台显示,用于存储在文件中等。
  • 可以随时以开关的形式控制是否记录日志,无需修改源代码。
  • 可以采用不同的方式格式化,如纯文本或XML。
  • 可以对记录进行过滤。过滤器可以根据过滤实现器指定的标准丢弃那些无用的记录项。


7.1.2 日志技术体系结构

日志规范:一些接口,提供给日志的实现框架设计的标准。

日志框架:绝活哥或者第三方公司已经做好的日志记录实现代码,后人可直接使用。

两种日志规范

  • 官方日志规范接口Commons Logging,简称JCL
  • 第三方日志接口Simple Logging Facade for Java,简称slf4j

日志实现框架

  • Log4j
  • JUL
  • Logback
  • 其他实现


7.1.3 Logback的用法

说明:由log4j的创始人设计的一个开源日志组件,性能比log4j要好。其基于slf4j的日志规范实现的框架

官网日志返回首页 (qos.ch)

Logback技术模块

  • logback-core:logback-core模块为其他两个模块奠定了基础,必须要有
  • logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j的API
  • logback-acccess:模块与Tamcat和Jetty等Servlet容器集成,以提供HTTP访问日志功能

Logback的初步使用

  • 在项目新建文件夹lib,导入Logback的相关jar包到该文件夹下,并添加到项目依赖库中去。
  • 将Logback的核心配置文件logback.xml直接拷贝到src目录下。
  • 在代码中获取日志的对象。
public static final Logger LOGGER = LoggerFactory.getLogger("类对象");

附-logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。
     默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration  scan="true" scanPeriod="60 seconds" debug="true">

    <!-- 定义变量,可通过 ${log.path}和${CONSOLE_LOG_PATTERN} 得到变量值 -->
    <property name="log.path" value="D:/log" />
    <property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} |-[%-5p] in %logger.%M[line-%L] -%m%n"/>

    <!-- 输出到控制台 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!-- Threshold=即最低日志级别,此appender输出大于等于对应级别的日志
             (当然还要满足root中定义的最低级别)
        -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <!-- 日志格式(引用变量) -->
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <!-- 设置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 追加到文件中 -->
    <appender name="file" class="ch.qos.logback.core.FileAppender">
        <file>${log.path}/hello2.log</file>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>
    <!-- 滚动追加到文件中 -->
    <appender name="file2" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/hello.log</file>
        <!--日志文件输出格式-->
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset> <!-- 设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录
             文件超过最大尺寸后,会新建文件,然后新的日志文件中继续写入
             如果日期变更,也会新建文件,然后在新的日志文件中写入当天日志
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 新建文件后,原日志改名为如下  %i=文件序号,从0开始 -->
            <fileNamePattern>${log.path}/hello-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 每个日志文件的最大体量 -->
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>8kb</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- 日志文件保留天数,1=则只保留昨天的归档日志文件 ,不设置则保留所有日志-->
            <maxHistory>1</maxHistory>
        </rollingPolicy>
    </appender>

    <root level="trace">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="file"/>
        <appender-ref ref="file2"/>
    </root>

</configuration>

基本使用

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Demo {
    //创建日志对象
    public static Logger LOGGER = LoggerFactory.getLogger("Demo.class");
    public static void main(String[] args) {
        try {
            LOGGER.debug("main方法开始执行了");
            LOGGER.info("我开始记录第二行日志,我们开始做除法");
            int a = 10;
            int b = 0;
            LOGGER.trace("a = "+a);
            LOGGER.trace("b="+b);

            System.out.println(a/b);
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("功能出现异常"+e);
        }
    }
}


7.1.4 Logback的配置

说明:Logback日志系统的特性都是通过核心配置文件logback.xml控制的

配置说明

标签 说明
\<append> 设置输出位置和日志信息的详细格式
\<target> 设置日志颜色
\<file> 设置日志输出路径
\<maxFileSize> 指定单个日志文件的大小,一旦溢出则生成新日志文件
\<fileNamePattern> 指定拆分的日志集中放置的位置
\<pattern> 使用正则匹配来完全提示信息,其中%d表示日期,%thread表示线程名,%-nlevel表示级别从左显示n个字符宽度,%c表示类名,%msg表示日志信息

输出到配置台的配置标志

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">

输出到系统文件的配置标志

<appender name="file2" class="ch.qos.logback.core.rolling.RollingFileAppender">

关联日志输出内容

    <root level="trace">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="file"/>
        <appender-ref ref="file2"/>
    </root>

日志级别设置

  • 日志级别程度依次为:TRACE<DEBUG<INFO<WARN<ERROR,默认级别是debug。对应日志对象中的方法名。
  • 日志级别作用:用于控制系统中哪些日志级别是可以输出的,只输出级别不低于设定级别的日志信息。
  • ALL和OFF分别是打开和关闭全部日志信息。

日志等级修改:只需修改level的值即可。

    <root level="trace">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="file"/>
        <appender-ref ref="file2"/>
    </root>


7.2 单元测试

说明:单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性。

在之前的学习中,如果我们要测试一个方法,都是采用是个main方法,里面包含多个方法来测试,但这样做会导致问题:如果中间一个方法失效,则下面的方法测试也会受到影响。


7.2.1 Junit单元测试框架

说明

  • Junit是使用Java语言实现的单元测试框架,它是开源的,Java开发者都应当学习并使用Junit编写单元测试。
  • 此外,几乎所有的IDE工具都继承了Junit。我们可以直接在IDE中编写并运行Junit测试,Junit目前最新的版本是5。

优点

  • Junit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。
  • Junit可以生成全部方法的测试报告。
  • 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。


7.2.2 单元测试的快速入门

步骤讲解

  1. 将Junit的jar包导入到项目中,如果你在联网的情况下,可以先写Test注解后按alt+回车让其自动导入。
  2. 编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。
  3. 在测试方法上使用@Test注解:标注该方法是一个测试方法。
  4. 在测试方法中完成被测试方法的预期正确性测试。
  5. 选中测试方法,选择Junit运行,如果测试良好是绿色,如果失败则为红色。

image-20220725190942653

局部测试和总体测试:如果我们在测试类中任意空白位置右键Run,则会测试所有方法;如果我们在测试类中的方法上右键Run,则会测试单个方法。

public class UserService {
    public String loginName(String loginName,String passWord){
        if("admin".equals(loginName) && "123456".equals(passWord)) return "登录成功";
        else return "用户名或者密码有问题";
    }

    public void selectNames(){
        System.out.println(18/0);
        System.out.println("查询全部用户名称成功");
    }
}
import org.junit.Assert;
import org.junit.Test;

/**测试类*/
public class TestUserService {
    @Test
    public void testLoginName(){
        UserService userService = new UserService();
        String rs = userService.loginName("admin","123456");
        Assert.assertEquals("您的功能业务数据可能出BUG","登录成功",rs);
    }

    @Test
    public void testSelectNames(){
        UserService userService = new UserService();
        userService.selectNames();
    }
}


7.2.3 Junit常用注解

Junit4.x注解:开始执行的方法常用于初始化资源;执行完之后的方法常用于释放资源。

注解 说明
@Test 测试方法
@Before 用来修饰实例方法,该方法会在每个测试方法执行之前执行一次
@After 用来修饰实例方法,该方法会在每个测试方法执行之后执行一次
@BeforeClass 用来修饰静态方法,该方法会在所有测试方法执行之前执行一次
@AfterClass 用来修饰静态方法,该方法会在所有测试方法执行之后执行一次

Junit5.x注解

注解 说明
@Test 测试方法
@BeforeEach 用来修饰实例方法,该方法会在每个测试方法执行之前执行一次
@AfterEach 用来修饰实例方法,该方法会在每个测试方法执行之后执行一次
@BeforeAll 用来修饰静态方法,该方法会在所有测试方法执行之前执行一次
@AfterAll 用来修饰静态方法,该方法会在所有测试方法执行之后执行一次


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
10天前
|
Java Shell
「sh脚步模版自取」测试线排查的三个脚本:启动、停止、重启、日志保存
「sh脚步模版自取」测试线排查的三个脚本:启动、停止、重启、日志保存
27 1
|
6天前
|
存储 运维 监控
Elasticsearch Serverless 高性价比智能日志分析关键技术解读
本文解析了Elasticsearch Serverless在智能日志分析领域的关键技术、优势及应用价值。
Elasticsearch Serverless 高性价比智能日志分析关键技术解读
|
10天前
|
Java 程序员 应用服务中间件
「测试线排查的一些经验-中篇」&& 调试日志实战
「测试线排查的一些经验-中篇」&& 调试日志实战
12 1
「测试线排查的一些经验-中篇」&& 调试日志实战
|
2天前
|
存储 人工智能 Java
将 Spring AI 与 LLM 结合使用以生成 Java 测试
AIDocumentLibraryChat 项目通过 GitHub URL 为指定的 Java 类生成测试代码,支持 granite-code 和 deepseek-coder-v2 模型。项目包括控制器、服务和配置,能处理源代码解析、依赖加载及测试代码生成,旨在评估 LLM 对开发测试的支持能力。
9 1
|
6天前
|
存储 Java 大数据
该到重点啦—java的核心技术
该到重点啦—java的核心技术
7 1
|
8天前
|
安全 Java API
🌟探索Java宇宙:深入理解Java技术体系与JVM的奥秘
本文深入探讨了Java技术体系的全貌,从Java语言的概述到其优点,再到Java技术体系的构成,以及JVM的角色。旨在帮助Java开发者全面了解Java生态,提升对Java技术的认知,从而在编程实践中更好地发挥Java的优势。关键词:Java, JVM, 技术体系, 编程语言, 跨平台, 内存管理。
20 2
|
7天前
|
SQL Java API
深入探索Java的持久化技术——JPA(Java Persistence API)
【10月更文挑战第10天】深入探索Java的持久化技术——JPA(Java Persistence API)
10 0
|
7天前
|
Java API 数据库
深入探索Java的持久化技术——JPA(Java Persistence API)
【10月更文挑战第10天】深入探索Java的持久化技术——JPA(Java Persistence API)
13 0
|
12天前
|
存储 Java 数据库连接
Java技术深度探索:从基础到进阶的全方位指南
【10月更文挑战第5天】Java技术深度探索:从基础到进阶的全方位指南
16 0
|
12天前
|
前端开发 安全 Java
Java技术深度探索:构建高效稳定的企业级应用
【10月更文挑战第5天】Java技术深度探索:构建高效稳定的企业级应用
18 0