日志编写 —— TinyWebServer
一、 前言
上期已经写完lock类的编写。这期是日志的编写。
对于日志需要弄懂几个基本概念才可以更好的理解和编写日志。
- 什么是日志?
- 常用的日志级别有哪些?
- 日志的基本格式是什么?
- 异步日志类刷新缓冲区的作用
- 同步和异步编写日志是什么意思?
- 什么是单例模式?
- 为什么使用单例模式?
- 单例模式的有哪几种常见的实现方式?
这些都是笔者作为小白刚开始不明白的点。如有其他问题,欢迎留言讨论!
二、问题回答
Ⅰ、什么是日志?
日志,由服务器自动创建,并记录运行状态,错误信息,访问数据的文件。
Ⅱ、常用的日志级别有哪些?
-
DEBUG(调试): 用于记录详细的调试信息,通常只在开发和调试阶段使用,以帮助开发人员识别和解决问题。这些日志通常包含程序的内部状态和变量值。
-
INFO(信息): 用于记录程序运行时的一般信息,如应用程序的启动和关闭,重要事件的发生,以及一些关键操作的结果。INFO级别的日志通常用于跟踪应用程序的正常运行。
-
WARNING(警告): 用于记录可能会影响应用程序正常运行但不是致命的问题。警告级别的日志通常表示潜在的问题或异常情况,但应用程序仍然可以继续运行。
-
ERROR(错误): 用于记录错误事件,这些事件可能会导致应用程序无法正常运行或无法执行关键操作。ERROR级别的日志通常需要开发人员注意和干预。
-
CRITICAL(严重): 用于记录严重的错误事件,这些事件可能会导致应用程序崩溃或无法继续运行。CRITICAL级别的日志通常表示需要立即采取行动来修复问题。
-
FATAL(致命): 有些日志系统中还包括FATAL级别,通常与CRITICAL级别类似,表示应用程序遇到了不可恢复的错误,需要立即处理。
当前项目涉及只有前4个级别
Ⅲ、日志的基本格式是什么?
-
时间戳: 记录日志事件发生的日期和时间,通常以标准的日期时间格式表示,例如ISO 8601格式(例如:2023-09-12T14:30:00Z)。
-
日志级别: 指示日志消息的严重性和紧急程度,如DEBUG、INFO、WARNING、ERROR或CRITICAL。
-
日志来源: 指示生成日志消息的组件、模块或系统的名称,以便识别日志消息的来源。
-
消息内容: 包含描述日志事件的具体信息,通常是一段文本,可以包括有关事件的详细信息、错误消息、警告或其他相关数据。
-
附加数据: 可选项,包括与日志事件相关的附加数据,通常以键值对的形式表示,用于提供更多上下文信息。例如,可以记录特定变量的值或事件的其他相关属性。
以下是一个简单的示例日志条目,展示了上述元素的基本格式:
2023-09-12T14:30:00Z INFO MyApp - User 'Alice' logged in successfully.
四、异步日志类刷新缓冲区的作用
-
减少I/O开销: 异步日志通常会将日志消息先存储在内存缓冲区中,然后异步地将缓冲区中的日志数据写入磁盘或其他存储介质。这可以降低频繁的磁盘I/O操作,提高性能,因为磁盘I/O通常是相对较慢的操作。但如果不及时刷新缓冲区,可能会导致大量的日志数据滞留在内存中,增加了在刷新时的I/O负担。
-
确保日志完整性: 如果不定期刷新缓冲区,而是等待程序结束时才刷新,那么在程序崩溃或非正常退出时,可能会丢失尚未刷新的日志数据。手动刷新缓冲区可以确保在应用程序的不同阶段或在异常情况下,已经写入缓冲区的数据会被及时写入磁盘,以保持日志的完整性。
-
日志实时性要求: 某些应用程序对日志消息的实时性有较高的要求,即要求日志消息能够立即写入磁盘或其他存储介质,以便实时监控或审计。在这种情况下,手动刷新缓冲区是确保日志消息及时写入的重要步骤。
要注意的是,在异步日志类中,刷新缓冲区的时机通常由一定的策略或触发条件来确定,而不是在每条日志消息之后都手动刷新。常见的策略包括:
-
定时刷新: 定期刷新缓冲区,以确保日志数据定期写入磁盘。例如,每隔一定时间或每秒刷新一次。
-
缓冲区大小限制: 当缓冲区达到一定大小时,自动触发刷新操作,以避免缓冲区过大。
-
关闭程序前刷新: 在程序即将正常关闭时,确保将缓冲区中的数据刷新到磁盘。
刷新缓冲区是异步日志实现中的一个重要设计考虑,以平衡性能和日志数据的可靠性和实时性。不同的应用场景和性能需求可能会导致不同的刷新策略。
Ⅴ、同步和异步编写日志是什么意思?
- 同步写日志:
- 同步写日志是指在应用程序的执行流程中,当发出日志消息时,程序会等待日志消息被写入到目标存储(通常是文件或数据库)之后才继续执行后续操作。
这意味着应用程序在记录每条日志消息时会阻塞,直到写入操作完成,然后才能继续执行。 - 同步写日志可以保证日志消息的完整性和可靠性,但可能会对应用程序的性能产生负面影响,特别是在高并发或高吞吐量的情况下。
- 异步写日志:
- 异步写日志是指在应用程序的执行流程中,当发出日志消息时,程序不会等待日志消息被写入到目标存储,而是立即继续执行后续操作。
- 写入日志消息的操作会在后台或独立线程中进行,以减少对主应用程序的性能影响。
- 异步写日志通常更适合高性能要求的应用程序,因为它可以减少因等待磁盘或网络写入操作而引起的延迟。
选择同步或异步写日志取决于应用程序的需求和性能要求:
-
如果应用程序要求高可靠性和确保每条日志消息都被记录,并且性能要求不是首要考虑因素,那么同步写日志可能更合适。
-
如果应用程序对性能有较高的要求,可以容忍一些日志消息可能丢失或延迟写入,那么异步写日志可能更适合,因为它可以减少对应用程序性能的影响。
一种常见的异步写日志策略是使用缓冲区,将多条日志消息先存储在内存中,然后定期批量写入到目标存储,以减少写入操作的频率,提高性能。不过需要注意的是,在异步写日志中,应该小心处理异常情况,以确保不会丢失重要的日志信息。
这里引用社长的图。这下大家应该都明白了
Ⅵ、什么是单例模式?
最常用的设计模式之一,保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
Ⅶ、为什么使用单例模式?
使用单例模式的日志记录类可以确保在整个应用程序中一致地使用和管理日志记录,提高了代码的可维护性和可扩展性。同时,它还可以帮助您更好地控制日志记录的配置和性能。
Ⅷ、单例模式的有哪几种常见的实现方式?
懒汉式和饿汉式是最常见的两种实现方式
- 懒汉式(Lazy Initialization): 在第一次请求实例时才创建实例,延迟初始化
- 饿汉式(Eager Initialization): 在类加载时就创建实例,立即初始化。
三、代码结构分析
要写好这个项目,应该要以以下的思路去编写
-
这个项目既可以同步又可以异步。其中异步需要使用阻塞队列。
-
阻塞队列的实现
- 引入"locker.h" : 阻塞队列,在添加到阻塞队列和写入到文件的时候,肯定只允许一个线程操作。多个线程同时会让数据错误。
- 使用模板: 阻塞队列不确定要写的是什么类型的数据,所以用模板。
- 成员函数: 现在的长度、最大长度、头、尾、循环队列
- 成员变量: 构造函数、析构函数、获取头元素、获取尾元素、判空、判满、增加、头删、定时头删、清空
-
日志的实现
-
引入"block_queue.h" :
-
使用单例模式: 这里使用的是懒汉模式
-
使用可变参数宏: 灵活且方便的方式来处理不同数量和类型的日志消息。
-
**成员函数:**构造函数、析构函数、同步写、异步写、初始化、缓冲区刷新
-
成员变量:
-
写入文件相关:
- 打开log的文件指针
- 路径名
- log文件名
- 日志最大行数
- 日志行数记录
- 日志缓冲区大小
- 因为按天分类,记录当前时间是那一天
-
是否同步标志位
-
阻塞队列
-
关闭日志
-
-
具体的代码实现,看仓库就可以了。就不赘诉了。
四、下期预告
数据连接池的编写
五、最后
如有帮助,求赞!