Java手写一个日志框架的示例代码

 更新时间:2023年12月29日 09:43:57   作者:乐乐家的乐乐  
日志框架是一种用于记录和管理应用程序运行时信息的软件组件,它通常提供了一套API让开发人员能够在代码中插入日志语句,下面我们就来学习一下如何手写一个日志框架吧

什么是日志框架

日志框架是一种用于记录和管理应用程序运行时信息的软件组件。它通常提供了一套API(Application Programming Interface),让开发人员能够在代码中插入日志语句,以便在应用程序运行时生成有关其状态和执行流的信息。

一个优秀的日志框架应该具备的功能:

  • 级别控制: 日志框架通常支持多个日志级别,如 DEBUG、INFO、WARN、ERROR 等。开发人员可以根据需要选择记录的信息级别,以便在不同场景中控制日志输出。
  • 输出目标: 日志框架支持将日志信息输出到不同的目标,如控制台、文件、数据库、远程服务器等。这使得开发人员能够根据实际需求选择合适的输出方式。
  • 性能优化: 一些日志框架支持异步日志记录,以减少对应用程序性能的影响。它们可能使用缓冲机制、线程池等技术,以提高日志记录的效率。
  • 灵活性: 日志框架提供了灵活的配置选项,使得开发人员能够根据实际需求进行定制和调整。

常见的Java日志框架包括 Log4j、Logback、java.util.logging 等。

手写一个简单的日志框架应该怎么做

首先,我们需要明确我们要做的功能。

从日渐成熟的日志框架中,我们总计日志框架的核心功能有;

  • 级别控制(DEBUG、INFO、WARN、ERROR)
  • 输出(控制台输出、文件输出)
  • 性能优化(做到不浪费性能)

我写了一个简单的源码

import java.io.*;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * LeLog类用于记录不同日志级别的消息。
 */
public class LeLog {
    /**
     * 日志级别的枚举: INFO, WARN, ERROR
     */
    public enum LogLevel {
        INFO, WARN, ERROR
    }

    private static Class<?> defaultClass; // 用于日志记录的默认类
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); // 日志记录的日期格式
    private static Map<Class<?>, LeLog> instances = new HashMap<>(); // 存储LeLog实例的映射
    private Class<?> clazz; // 与日志关联的类

    /**
     * 构造函数,设置默认日志记录的类。
     * @param clazz 与日志关联的类
     */
    public LeLog(Class<?> clazz) {
        this.clazz = clazz;
        defaultClass = clazz; // 设置默认日志记录的类
    }

    /**
     * 多例模式创建实例
     * @param clazz 类
     * @return LeLog实例
     */
    public static synchronized LeLog getLeLog(Class<?> clazz) {
        if (!instances.containsKey(clazz)) {
            LeLog instance = new LeLog(clazz);
            instances.put(clazz, instance);
        }
        return instances.get(clazz);
    }

    /**
     * 将日志消息写入控制台和文件。
     * @param level 日志级别
     * @param logName 日志名称
     * @param message 日志消息
     */
    private static void write(LogLevel level, String logName, String message) {
        // 创建并格式化日志消息
        String timeStamp = dateFormat.format(new Date());
        String threadName = Thread.currentThread().getName();
        long threadId = Thread.currentThread().getId();
        String logMessage = timeStamp + " [" + level + "] "
                + "Thread: " + threadName + " (ID: " + threadId + ") "
                + logName + " - " + message;
        // 输出日志消息到控制台
        System.out.println(logMessage);
        // 将日志消息写入文件
        writeToFile(logMessage);
    }

    /**
     * 获取日志文件路径。
     * @return 日志文件路径
     */
    private static String getLogFile() {
        Properties prop = new Properties();
        try (FileInputStream input = new FileInputStream("le.log.properties")) {
            prop.load(input);
            String logFilePath = prop.getProperty("logFilePath");

            File logFolder = new File(logFilePath);
            if (!logFolder.exists()) {
                logFolder.mkdirs();
            }

            String logFileName = prop.getProperty("logFileName");
            return logFilePath + "/" + logFileName + "_" + LocalDate.now() + ".log";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将日志消息写入文件。
     * @param message 日志消息
     */
    private static void writeToFile(String message) {
        String logFile = getLogFile();
        if (logFile != null) {
            try (PrintWriter out = new PrintWriter(new FileWriter(logFile, true))) {
                out.println(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 记录INFO级别的日志消息。
     * @param message 日志消息
     */
    public static void info(String message){
        write(LeLog.LogLevel.INFO,defaultClass.getName(),message);
    }

    /**
     * 记录WARN级别的日志消息。
     * @param message 日志消息
     */
    public static void warn(String message){
        write(LogLevel.WARN,defaultClass.getName(),message);
    }

    /**
     * 记录ERROR级别的日志消息。
     * @param message 日志消息
     */
    public static void error(String message){
        write(LogLevel.ERROR,defaultClass.getName(),message);
    }

}

代码结构和核心思路

LeLog 类是一个用于记录不同日志级别消息的工具类。以下是它的核心思路和代码结构:

日志级别枚举: LeLog 定义了一个LogLevel枚举,包括 INFO、WARN 和 ERROR 三个级别,分别表示信息、警告和错误。

    /**
     * 日志级别的枚举: INFO, WARN, ERROR
     */
    public enum LogLevel {
        INFO, WARN, ERROR
    }

类与实例关联: LeLog 通过多例模式创建实例,每个实例与一个特定的类关联。这种设计使得每个类都有自己的日志记录器,方便了解来自不同类的日志信息。

    private static Map<Class<?>, LeLog> instances = new HashMap<>(); // 存储LeLog实例的映射
    private Class<?> clazz; // 与日志关联的类

    /**
     * 构造函数,设置默认日志记录的类。
     * @param clazz 与日志关联的类
     */
    public LeLog(Class<?> clazz) {
        this.clazz = clazz;
        defaultClass = clazz; // 设置默认日志记录的类
    }

    /**
     * 多例模式创建实例
     * @param clazz 类
     * @return LeLog实例
     */
    public static synchronized LeLog getLeLog(Class<?> clazz) {
        if (!instances.containsKey(clazz)) {
            LeLog instance = new LeLog(clazz);
            instances.put(clazz, instance);
        }
        return instances.get(clazz);
    }

日志消息格式化: 在 write 方法中,使用 SimpleDateFormat 对日期进行格式化,同时获取线程信息和日志级别,将这些信息拼接成格式化的日志消息。

    /**
     * 将日志消息写入控制台和文件。
     * @param level 日志级别
     * @param logName 日志名称
     * @param message 日志消息
     */
    private static void write(LogLevel level, String logName, String message) {
        // 创建并格式化日志消息
        String timeStamp = dateFormat.format(new Date());
        String threadName = Thread.currentThread().getName();
        long threadId = Thread.currentThread().getId();
        String logMessage = timeStamp + " [" + level + "] "
                + "Thread: " + threadName + " (ID: " + threadId + ") "
                + logName + " - " + message;
        // 输出日志消息到控制台
        System.out.println(logMessage);
        // 将日志消息写入文件
        writeToFile(logMessage);
    }

输出到控制台和文件: LeLog 将日志消息输出到控制台,并通过 writeToFile 方法将消息写入到文件。日志文件路径和文件名从配置文件中读取,提高了灵活性。日志文件名中包含当前日期,每天生成一个新的日志文件,方便按日期查看日志。

# 日志输出路径
logFilePath=logs/xxx/xx
# 日志输出名称
logFileName=xia_le
    /**
     * 获取日志文件路径。
     * @return 日志文件路径
     */
    private static String getLogFile() {
        Properties prop = new Properties();
        try (FileInputStream input = new FileInputStream("le.log.properties")) {
            prop.load(input);
            String logFilePath = prop.getProperty("logFilePath");

            File logFolder = new File(logFilePath);
            if (!logFolder.exists()) {
                logFolder.mkdirs();
            }

            String logFileName = prop.getProperty("logFileName");
            return logFilePath + "/" + logFileName + "_" + LocalDate.now() + ".log";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将日志消息写入文件。
     * @param message 日志消息
     */
    private static void writeToFile(String message) {
        String logFile = getLogFile();
        if (logFile != null) {
            try (PrintWriter out = new PrintWriter(new FileWriter(logFile, true))) {
                out.println(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

使用方法

使用 LeLog 记录日志非常简单。首先,通过 LeLog.getLeLog(Class clazz) 方法获取 LeLog 实例,然后通过该实例的 infowarnerror 方法记录不同级别的日志消息。以下是一个简单的示例:

public class TestOne {
    private static final LeLog leLog = LeLog.getLeLog(TestOne.class);
    public void test(){
        leLog.info("This is an information message.");
        leLog.warn("This is a warning message.");
        leLog.error("This is an error message.");
    }
}

public class TestTwo {
    private static final LeLog leLog = LeLog.getLeLog(TestTwo.class);
    public void test(){
        leLog.info("This is an information message.");
        leLog.warn("This is a warning message.");
        leLog.error("This is an error message.");
    }
}

public class Main {
    public static void main(String[] args) {
        new TestOne().test();
        new TestTwo().test();
    }
}

控制台输出如下

文件输出如下:

后期优化

这个类可以说是漏洞百出,我写出来不过是给大伙提供一个思路和一个敢于手写框架造轮子的程序员桀骜不驯的心。

那这个类来说,值得喷的点有哪些:

  • 异常处理你是一点没做啊!
  • 文件输出类型太单一,我只做了windows的日志路径,Linux上的你是一点没做啊!
  • 文件的格式问题,现在日志都流行是json格式的,以便于是以后在NoSql数据库展示和分析。
  • 过滤我也是一个都么看见。
  • 异常追踪呢?
  • 最重要的是一个框架的灵活性,你这个你不觉得笨重吗?
  • 字符编码和乱码问题呢,System.out.println很耗费性能的好吧。
  • ..........太多了,懒得吐槽。

以上就是Java手写一个日志框架的示例代码的详细内容,更多关于Java日志框架的资料请关注脚本之家其它相关文章!

相关文章

  • Java基础入门 Swing中间容器的使用

    Java基础入门 Swing中间容器的使用

    这篇文章主要介绍了Java基础入门 Swing中间容器的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 分布式系统下调用链追踪技术面试题

    分布式系统下调用链追踪技术面试题

    这篇文章主要为大家介绍了分布式系统下调用链追踪技术面试问题合集,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-03-03
  • JavaScript中的closest方法示例详解

    JavaScript中的closest方法示例详解

    这篇文章主要介绍了JavaScript中closest方法的相关资料,closest()是JavaScript中的一个非常实用的 DOM 方法,用于查找与当前元素匹配的最近的祖先元素,它支持 CSS 选择器,可以应用于事件委托、动态内容处理等场景,需要的朋友可以参考下
    2025-02-02
  • 详解java并发之重入锁-ReentrantLock

    详解java并发之重入锁-ReentrantLock

    这篇文章主要介绍了java并发之重入锁-ReentrantLock,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • springboot sleuth 日志跟踪问题记录

    springboot sleuth 日志跟踪问题记录

    Spring Cloud Sleuth是一个在应用中实现日志跟踪的强有力的工具,使用Sleuth库可以应用于计划任务 、多线程服务或复杂的Web请求,尤其是在一个由多个服务组成的系统中,这篇文章主要介绍了springboot sleuth 日志跟踪,需要的朋友可以参考下
    2023-07-07
  • Mybatis批量更新对象数据的两种实现方式

    Mybatis批量更新对象数据的两种实现方式

    这篇文章主要介绍了Mybatis批量更新对象数据的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • JavaWeb实现显示mysql数据库数据

    JavaWeb实现显示mysql数据库数据

    MySQL是最流行的关系型数据库管理系统,在WEB应用方面MySQL是最好的。本文将利用JavaWeb实现显示mysql数据库数据功能,需要的可以参考一下
    2022-03-03
  • Spring中@Scope的几种取值方式

    Spring中@Scope的几种取值方式

    这篇文章主要介绍了Spring中@Scope的几种取值方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • 一篇文章带你了解jdk1.8新特性--为什么使用lambda表达式

    一篇文章带你了解jdk1.8新特性--为什么使用lambda表达式

    Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码,本篇文章就带你了解,希望能给你带来帮助
    2021-08-08
  • Java汉字转成汉语拼音工具类

    Java汉字转成汉语拼音工具类

    这篇文章主要为大家详细介绍了Java汉字转成汉语拼音工具类,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05

最新评论