C语言中的日志机制:打造全面强大的日志系统
前言
在软件开发中,良好的日志记录机制对于调试、监控程序状态和维护系统的稳定性至关重要。本文将介绍如何在C语言中构建一个全面强大的日志系统,并提供一些示例代码。
1. 日志的基本概念
- 日志级别:用于分类日志信息的重要性,如 DEBUG, INFO, WARNING, ERROR, CRITICAL 等。
- 日志消息:包含日志级别、时间戳、文件名、行号、消息内容等。
- 日志输出:日志可以输出到控制台、文件或网络服务等。
2. 日志级别宏定义
定义日志级别的宏,便于管理和调整日志输出。
1#define LOG_DEBUG 1
2#define LOG_INFO 2
3#define LOG_WARNING 3
4#define LOG_ERROR 4
5#define LOG_CRITICAL 5
6
7#define LOG_LEVEL LOG_DEBUG // 设置日志级别
8
9#define LOG(level, ...) \
10 do { \
11 if (level >= LOG_LEVEL) { \
12 fprintf(stderr, "[%s] %s:%d: ", logLevelToString(level), __FILE__, __LINE__); \
13 fprintf(stderr, __VA_ARGS__); \
14 fprintf(stderr, "\n"); \
15 } \
16 } while (0)
17
18static const char* logLevelToString(int level) {
19 switch (level) {
20 case LOG_DEBUG: return "DEBUG";
21 case LOG_INFO: return "INFO";
22 case LOG_WARNING: return "WARNING";
23 case LOG_ERROR: return "ERROR";
24 case LOG_CRITICAL: return "CRITICAL";
25 default: return "UNKNOWN";
26 }
27}
解释:
LOG_LEVEL定义了当前的日志级别。LOG宏用于输出日志,根据日志级别过滤日志输出。logLevelToString函数将日志级别转换为字符串。
3. 使用日志宏
使用定义好的日志宏来记录日志。
1#include <stdio.h>
2
3int main() {
4 LOG(LOG_DEBUG, "This is a debug message.");
5 LOG(LOG_INFO, "This is an info message.");
6 LOG(LOG_WARNING, "This is a warning message.");
7 LOG(LOG_ERROR, "This is an error message.");
8 LOG(LOG_CRITICAL, "This is a critical message.");
9
10 return 0;
11}
输出:
1[DEBUG] main.c:17: This is a debug message.
2[INFO] main.c:18: This is an info message.
3[WARNING] main.c:19: This is a warning message.
4[ERROR] main.c:20: This is an error message.
5[CRITICAL] main.c:21: This is a critical message.
解释:
- 使用
LOG宏记录不同级别的日志。
4. 日志到文件
将日志输出到文件。
1#include <stdio.h>
2#include <stdarg.h>
3#include <string.h>
4
5#define MAX_LOG_SIZE 1024
6
7void logToFile(const char *level, const char *file, int line, const char *fmt, ...) {
8 FILE *logfile = fopen("app.log", "a"); // 打开日志文件
9 if (logfile == NULL) {
10 perror("Failed to open log file");
11 return;
12 }
13
14 va_list args;
15 va_start(args, fmt);
16 char logBuffer[MAX_LOG_SIZE];
17 vsnprintf(logBuffer, MAX_LOG_SIZE - 1, fmt, args);
18 va_end(args);
19
20 time_t now = time(NULL);
21 struct tm *timeinfo = localtime(&now);
22 char timestamp[20];
23 strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", timeinfo);
24
25 fprintf(logfile, "[%s] %s %s:%d: %s\n", level, timestamp, file, line, logBuffer);
26 fclose(logfile);
27}
28
29#define LOG(level, ...) \
30 do { \
31 if (level >= LOG_LEVEL) { \
32 logToFile(logLevelToString(level), __FILE__, __LINE__, __VA_ARGS__); \
33 } \
34 } while (0)
解释:
logToFile函数用于将日志写入文件。- 使用
vsnprintf和va_list处理可变参数列表。 - 添加时间戳到日志消息。
5. 日志的旋转
实现日志文件的自动旋转,以便管理日志文件的大小。
1#include <stdio.h>
2#include <string.h>
3#include <errno.h>
4
5void rotateLog() {
6 FILE *logfile = fopen("app.log", "r");
7 if (logfile == NULL) {
8 perror("Failed to open log file");
9 return;
10 }
11
12 struct stat fileStat;
13 if (fstat(fileno(logfile), &fileStat) == -1) {
14 perror("Failed to get file status");
15 fclose(logfile);
16 return;
17 }
18
19 if (fileStat.st_size > 1024 * 1024) { // 如果日志文件大于1MB
20 fclose(logfile);
21 if (rename("app.log", "app.log.old") == -1) {
22 perror("Failed to rename log file");
23 }
24 } else {
25 fclose(logfile);
26 }
27}
28
29int main() {
30 // ... 日志记录代码 ...
31
32 // 在每次记录日志前检查是否需要旋转日志文件
33 rotateLog();
34 LOG(LOG_INFO, "This is an info message.");
35
36 return 0;
37}
解释:
rotateLog函数检查日志文件大小,如果超过阈值,则重命名旧的日志文件。- 在记录日志前调用
rotateLog。
6. 日志配置
实现日志配置的读取和设置。
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4
5typedef struct {
6 int level;
7 char *filename;
8} LogConfig;
9
10LogConfig loadConfig() {
11 LogConfig config = {LOG_DEBUG, "app.log"};
12
13 FILE *configFile = fopen("log.conf", "r");
14 if (configFile == NULL) {
15 perror("Failed to open config file");
16 return config;
17 }
18
19 char line[256];
20 while (fgets(line, sizeof(line), configFile) != NULL) {
21 if (strncmp(line, "level=", 6) == 0) {
22 char *levelStr = line + 6;
23 config.level = atoi(levelStr);
24 } else if (strncmp(line, "filename=", 9) == 0) {
25 char *filename = line + 9;
26 config.filename = strdup(filename);
27 }
28 }
29
30 fclose(configFile);
31
32 return config;
33}
34
35void setLogLevel(LogConfig config) {
36 LOG_LEVEL = config.level;
37 // 更改 logToFile 函数中的日志文件名
38}
39
40int main() {
41 LogConfig config = loadConfig();
42 setLogLevel(config);
43
44 // ... 日志记录代码 ...
45
46 return 0;
47}
解释:
loadConfig函数读取配置文件。setLogLevel函数设置日志级别和日志文件名。
结论
构建一个全面强大的日志机制对于软件开发至关重要。通过上述示例,你应该已经了解了如何在C语言中实现日志记录的不同方面。这种能力对于调试程序、监控应用程序状态和维护系统的稳定性非常有帮助。
相关文章:
C语言中的日志机制:打造全面强大的日志系统
前言 在软件开发中,良好的日志记录机制对于调试、监控程序状态和维护系统的稳定性至关重要。本文将介绍如何在C语言中构建一个全面强大的日志系统,并提供一些示例代码。 1. 日志的基本概念 日志级别:用于分类日志信息的重要性,…...
局域网广域网,IP地址和端口号,TCP/IP 4层协议,协议的封装和分用
前言 在古老的年代,如果我们要实现两台机器进行数据传输, A员工就得去B员工的办公电脑传数据(B休息,等A传完),这样就很浪费时间 所以能不能不去B的工位的同时,还能传数据。这时候网络通信就出来…...
LabVIEW项目编码器选择
在LabVIEW项目中,选择增量式(Incremental Encoder)和绝对式(Absolute Encoder)编码器取决于项目的具体需求。增量式编码器和绝对式编码器在工作原理、应用场景、精度和成本等方面存在显著差异。以下从多方面详细阐述两…...
Spring Boot实现房产租赁业务逻辑
1 绪论 1.1 研究背景 中国的科技的不断进步,计算机发展也慢慢的越来越成熟,人们对计算机也是越来越更加的依赖,科研、教育慢慢用于计算机进行管理。从第一台计算机的产生,到现在计算机已经发展到我们无法想象。给我们的生活改变很…...
汽车3d动画渲染选择哪个?选择最佳云渲染解决方案
面临汽车3D动画渲染挑战?选择正确的云渲染服务至关重要。探索最佳解决方案,优化渲染效率,快速呈现逼真动画。 汽车3d动画渲染选择哪个? 对于汽车3D动画渲染,选择哪个渲染器取决于你的项目需求、预算和期望的效果。Ble…...
火语言RPA流程组件介绍--网页/元素截图
🚩【组件功能】:对整个网页、可见区域或者某个元素进行截图 ,保存至指定文件夹,仅适用于内置浏览器 配置预览 配置说明 截图类型 整个网页/可见区域/元素截图 目标元素 支持T或# 通过自动捕获工具捕获(选择元素工具使用方法)…...
VSCode编程配置再次总结
VScode 中C++编程再次总结 0.简介 1.配置总结 1.1 launch jsion文件 launch.json文件主要用于运行和调试的配置,具有程序启动调试功能。launch.json文件会启用tasks.json的任务,并能实现调试功能。 左侧任务栏的第四个选项运行和调试,点击创建launch.json {"conf…...
银行管理系统
摘 要 伴随着信息技术与互联网技术的不断发展,人们进到了一个新的信息化时代,传统管理技术性没法高效率、容易地管理信息内容。为了实现时代的发展必须,提升管理高效率,各种各样管理管理体系应时而生,各个领域陆续进到…...
极狐GitLab 17.4 重点功能解读【四】
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料: 极狐GitLab 官网极狐…...
[每日一练]利用自连接实现数量查询
该题目来源于力扣: 1731. 每位经理的下属员工数量 - 力扣(LeetCode) 题目要求: 表:Employees----------------------- | Column Name | Type | ----------------------- | employee_id | int | | name …...
Linux云计算 |【第四阶段】RDBMS1-DAY3
主要内容: 子查询(单行单列、多行单列、单行多列、多行多列)、分页查询limit、联合查询union、插入语句、修改语句、删除语句 一、子查询 子查询就是指的在一个完整的查询语句之中,嵌套若干个不同功能的小查询,从而一…...
初始MYSQL数据库(8)—— JDBC编程
找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏: MYSQL 目录 JDBC的概念 JDBC的使用 加载驱动包 建立连接 创建 statement 对象 定义并执行SQL语句 处理结果集 关闭资源 SQL注入 …...
Vue $router.push打开新窗口
Vue $router.push打开新窗口 最近有粉丝小伙伴问我:$router.push方法用于在当前窗口中跳转路由,但有时候我们需要在新的窗口或标签页中打开一个路由改怎么实现呢? 那么这里就介绍下实现逻辑和代码案例! 文章目录 Vue $router.pus…...
SQL进阶技巧:如何利用if语句简化where或join中的条件 | if条件语句的优雅使用方法
目录 0 问题场景 1 数据准备 2 问题分析 2.1 需求一 2.2需求二 3 小结 0 问题场景 有两张表,一张用户下单表user_purchase(用户ID粒度)包含用户ID、订单ID和下单消耗金额和一张用户维表user_info包含用户ID、用户年龄和用户是否实名认证。 user_purchase user_info 需…...
SpringCloud-Alibaba第二代微服务快速入门
1.简介 Spring Cloud Alibaba其实是阿里的微服务解决方案,是阿里巴巴结合自身微服务实践,开源的微服务全家桶,在Spring Cloud项目中孵化成为Spring Cloud的子项目。第一代的Spring Cloud标准中很多组件已经停更,如:Eureak,zuul等。所以Sprin…...
JSON字符串转换成对象
在Java中,将JSON字符串转换成对象是一个常见的操作,特别是在处理Web服务或API时。这通常通过使用第三方库来实现,因为Java标准库(Java SE)本身并不直接支持JSON的序列化和反序列化。最常用的库之一是Jackson和Gson。下…...
第三十五章 结合加密和签名
文章目录 第三十五章 结合加密和签名使用非对称密钥签名并加密使用非对称密钥加密并签名 第三十五章 结合加密和签名 可以在同一条消息中加密和签名。在大多数情况下,只需组合前面主题中给出的方法即可。本主题讨论了多种场景。 使用非对称密钥签名并加密 要签名…...
FastAPI 第八课 -- 路径操作依赖项
目录 一. 前言 二. 依赖项(Dependencies) 2.1. 依赖注入 2.2. 依赖项的使用 三. 路径操作依赖项的基本使用 3.1. 预处理(Before) 3.2. 后处理(After) 四. 多个依赖项的组合 五. 异步依赖项 一. 前…...
大厂面试真题-说一下Mybatis的缓存
首先看一下原理图 Mybatis提供了两种缓存机制:一级缓存(L1 Cache)和二级缓存(L2 Cache),旨在提高数据库查询的性能,减少数据库的访问次数。注意查询的顺序是先二级缓存,再一级缓存。…...
jQuery UI 工作原理
jQuery UI 工作原理 引言 jQuery UI 是建立在 jQuery 库之上的一个开源 JavaScript 库,它提供了一系列用户界面交互、特效、小部件和主题。它旨在简化 HTML 用户界面的开发,使开发者能够轻松地创建具有丰富交互性和视觉吸引力的网页。本文将深入探讨 jQuery UI 的工作原理,…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...
轻量级Docker管理工具Docker Switchboard
简介 什么是 Docker Switchboard ? Docker Switchboard 是一个轻量级的 Web 应用程序,用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器,使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...
