Linux 下 日志系统搭建全攻略

news2025/4/7 7:05:01

目录

一、引言

二、日志系统基础

日志级别

日志输出格式

三、创建日志所需函数

认识可变参数

​编辑

获取时间的函数

小结 

四、创建日志


一、引言

在 Linux 环境中开发 C/C++ 程序时,日志系统是不可或缺的一部分。它不仅有助于调试程序、排查问题,还能记录程序运行时的关键信息,方便后续分析与维护。本文将全面介绍如何在 Linux 下构建高效实用的 C/C++ 日志系统,涵盖从基础概念到实际应用的各个方面。

二、日志系统基础

日志级别

常见日志级别介绍:

DEBUG调试级别

INFO信息级别

WARN警告级别

ERROR错误级别

FATAL严重错误级别

日志输出格式

常用格式元素:

时间戳:记录日志发生的时间,精确到秒或毫秒,用于确定事件发生的先后顺序。时间戳的格式通常为 “YYYY - MM - DD HH:MM:SS.SSS”,其中 “YYYY” 表示年份,“MM” 表示月份,“DD” 表示日期,“HH” 表示小时,“MM” 表示分钟,“SS” 表示秒,“SSS” 表示毫秒。

日志级别:明确日志的级别,如 DEBUG、INFO、WARN、ERROR、FATAL 等,便于快速筛选和分析不同重要程度的日志信息。

文件名和行号:记录日志所在的源文件名称和行号,方便定位日志对应的代码位置,在调试和问题排查时非常有用。

线程 ID:在多线程程序中,线程 ID 可以帮助区分不同线程产生的日志,有助于分析多线程环境下的并发问题。

日志内容:具体的日志信息,是开发者或运维人员关注的核心内容,应尽可能清晰、准确地描述事件的发生情况。

自定义格式示例:

例如,定义一种日志格式为 “[时间戳] [日志级别] [线程 ID] [文件名:行号] - 日志内容”。在实际应用中,一条符合该格式的日志记录可能如下所示:

[2025 - 04 - 01 15:30:25.123] [INFO] [12345] [main.cpp:56] - 系统初始化完成,开始接受请求。

这种格式清晰地展示了日志发生的时间、级别、所属线程、代码位置以及具体内容,方便阅读和分析。通过自定义日志格式,可以根据项目的具体需求和团队的习惯,灵活地组织和呈现日志信息,提高日志的可读性和实用性。

三、创建日志所需函数

认识可变参数

像C语言中的printf函数也使用了可变参数,可以传入多个参数

printf函数原型如下:

int printf(const char *format, ...);

使用可变参数我们还需要借助宏

以下全部都是宏函数: 

#include <stdarg.h>

void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);

例如:以下代码的函数的作用是多个未知元素求和,由调用者任意输入数据

int sum(int n, ...);

我们知道创建栈帧时参数表中的参数是从左向右开始进行压栈的

va_list:创建一个指向栈帧起始位置的指针变量

void va_start(va_list ap, last);

  • 第一个参数是类型为va_list的对象
  •  第二个参数是一个固定参数,即可变参数列表之前的那个参数。因此,可变参数之前必须要有至少一个具体的参数(因为需要使用这个参数找到可变参数的起始位置)。

功能:初始化s指针,将s指针移动至可变参数第一个元素的起始地址处

type va_arg(va_list ap, type);

  •  第一个参数是类型为va_list的对象。
  •  第二个参数是你希望从可变参数列表中获取的参数类型。

功能:从当前地址开始,通过计算拿到下一个参数的地址,然后在根据type类型,对该地址解引用拿到该参数的值并返回,此时会更新ap的位置,以便继续遍历后续的列表。

void va_end(va_list ap);

功能:将指针变量s置空

va_end(s); //将指针s置为nullptr

以下代码设计:使用以上宏函数,求出1,2,3数值之和

#include <iostream>
#include <string>
#include <cstdarg>

using namespace std;

int sum(int n, ...)
{
    va_list s; //定义一个s变量
    va_start(s, n); //初始化

    int total = 0;
    for(int i = 0; i < n; ++i)
    {
        total += va_arg(s, int); //从初始化的地方开始依次获取可变参数元素     
    }
    
    va_end(s); //将s置空

    return total;    
}

int main()
{
    printf("total: %d\n", sum(3, 1, 2, 3));

    return 0;
}

获取时间的函数

time函数

time函数原型如下:

#include <time.h>

time_t time(time_t *tloc);

功能:获取当前时间戳

#include <iostream>
#include <string>
#include <cstdarg>

using namespace std;

int main()
{
    time_t t = time(nullptr);

    cout << t << endl;
    return 0;
}

gettimeofday函数

gettimeofday函数原型如下:

 #include <sys/time.h>

 int gettimeofday(struct timeval *tv, struct timezone *tz);

struct timeval结构体如下:

 struct timeval {
               time_t      tv_sec;     //秒
               suseconds_t tv_usec;    //微秒
           };

struct timeone第二个结构体是时区,不用管设置为nullptr即可

localtime函数

localtime原型如下:

#include <time.h>

struct tm *localtime(const time_t *timep);

功能:将时间戳转化为 年 月 日 时 分 秒

struct tm结构体如下:

struct tm {
               int tm_sec;    /* Seconds (0-60) */
               int tm_min;    /* Minutes (0-59) */
               int tm_hour;   /* Hours (0-23) */
               int tm_mday;   /* Day of the month (1-31) */
               int tm_mon;    /* Month (0-11) */
               int tm_year;   /* Year - 1900 */
               int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
               int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
               int tm_isdst;  /* Daylight saving time */
           };

注意:

tm_year:表示从1900年开始经过的年数。因此,要获取实际的年份,需要将其加上1900,即tm_year + 1900
tm_mon:表示月份,范围从0到11,其中0表示一月,1表示二月,以此类推。因此,要获取实际的月份,需要将其加上1,即tm_mon + 1

小结 

按照va_list(创建变量)、va_start(初始化变量)、va_arg(遍历列表)、va_end(清理工作)这四个步骤,就可以拿到可变列表中的每一个值了。

四、创建日志

#include <iostream>
#include <string>
#include <cstdarg>

using namespace std;

#define SIZE 1024

enum class LogLevel
{
    INFO,
    DEBUG,
    WARNING,
    ERROR,
    FATAL
};

string LevelToString(LogLevel level) //需要传入枚举类型,我们使用的是枚举类进行进行判断
{
    switch(level)
    {
        case LogLevel::INFO :
        return "INFO";
        case LogLevel::DEBUG :
        return "DEBUG";
        case LogLevel::WARNING :
        return "WARNING";
        case LogLevel::ERROR :
        return "ERROR";
        case LogLevel::FATAL :
        return "FATAL";
    }
}

void PrintLog(LogLevel Log_Level, const char *format, ...)
{
    //左半部分 日志等级 + 日志时间
    time_t t = time(nullptr);
    struct tm* pt = localtime(&t);
    char leftbuffer[SIZE];
    snprintf(leftbuffer, sizeof(leftbuffer) - 1, "[%s][%d-%d-%d %d:%d:%d]: ",
     LevelToString(Log_Level).c_str(),
     pt->tm_year, pt->tm_mon, pt->tm_mday,
     pt->tm_hour, pt->tm_min, pt->tm_sec);


    //右半部分 自定义部分
    va_list s;
    va_start(s, format);
    char rightbuffer[SIZE];
    vsnprintf(rightbuffer, sizeof(rightbuffer) - 1, "%s", s);

    //进行打印
    fprintf(stdout, "%s%s", leftbuffer, rightbuffer);
}

int main()
{
    PrintLog(LogLevel::INFO, "%s", "HelloWorld\n");

    return 0;
}

你可以根据自己的需求增加日志打印内容,这里是一个简易的日志系统

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2329034.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统安装Postgre和Postgis教程

卸载 如果之前没装过可以忽略这一步 卸载前记得备份数据库数据(如果还需要的话)&#xff01;&#xff01;&#xff01; 一、删除 Docker 安装的 PostgreSQL/PostGIS 1. 停止并删除容器 # 查看所有容器 docker ps -a | grep postgres# 停止并删除容器&#xff08;替换为实际…

LXC 导入多Linux系统

前提要求 ubuntu下安装lxd 参考Rockylinux下安装lxd 参考LXC 源替换参考LXC 容器端口发布参考LXC webui 管理<

6547网:蓝桥STEMA考试 Scratch 试卷(2025年3月)

『STEMA考试是蓝桥青少教育理念的一部分&#xff0c;旨在培养学生的知识广度和独立思考能力。考试内容主要考察学生的未来STEM素养、计算思维能力和创意编程实践能力。』 一、选择题 第一题 运行下列哪个程序后&#xff0c;飞机会向左移动&#xff1f; ( ) A. …

使用Webpack搭建React项目:从零开始

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…

STM32提高篇: CAN通讯

STM32提高篇: CAN通讯 一.CAN通讯介绍1.物理层2.协议层二.STM32CAN外设1.CAN控制器的3种工作模式2.CAN控制器的3种测试模式3.功能框图三.CAN的寄存器介绍1.环回静默模式测试2.双击互发测试四.CAN的HAL代码解读一.CAN通讯介绍 CAN(Controller Area Network 控制器局域网,简称…

25.Reactor

预备知识 std::bind template <class Fn, class... Args>/* unspecified */ bind (Fn&& fn, Args&&... args);解释&#xff1a; std::bind(&TcpServer::Accepter, this, std::placeholders::_1) 这段代码使用了 C11 中的 std::bind 函数&#xff0…

Linux进程间通信——有名管道

一.概念 函数形式&#xff1a;int mkfifo(const char \*filename,mode_t mode); 功能&#xff1a;创建管道文件 参数&#xff1a;管道文件文件名\路径&#xff0c;权限&#xff0c;创建的文件权限仍然和umask有关系。 返回值&#xff1a;创建成功返回0&#xff0c;创建失败返回…

Axure RP9.0教程: 查询条件隐藏与显示(综合了动态面板状态切换及展开收缩效果实现)

文章目录 引言I 原型显示/隐藏搜索框思路步骤详细操作II 若依 ruoyi 显示/隐藏搜索框 & 显示隐藏列自定义设置显示隐藏列显示/隐藏搜索框引言 数据筛选有大量的查询条件时,可以选择查询隐藏效果。 I 原型显示/隐藏搜索框 综合了动态面板状态切换及展开收缩效果实现 思…

【漫话机器学习系列】183.非参数方法(Non-parametric Methods)

非参数方法&#xff08;Non-parametric Methods&#xff09;详解 概述 非参数方法是一类在统计学和机器学习中广泛应用的技术&#xff0c;它的特点是不对特征值和目标值之间的关系做具体的假设。与传统的参数方法&#xff08;如线性回归、逻辑回归等&#xff09;不同&#xf…

智能驾驶中预测模块简介

1.轨迹预测的定义 轨迹预测是自动驾驶系统“感知-预测-规控”流程中的核心环节&#xff0c;位于感知与规划模块之间&#xff0c;起到承上启下的作用。感知系统负责检测道路环境中的动态和静态元素&#xff0c;包括车辆、行人、自行车、交通标志、车道线等&#xff0c;而预测模…

剑指offer经典题型(一)

本期我们将开始进行剑指offer中经典题型的学习。 数组相关 题目1&#xff1a;在一个二维数组中&#xff08;每个一维数组的长度相同&#xff09;&#xff0c;每一行都按照从左到右递增的顺序排序&#xff0c;每一列都按照从上到下递增的顺序排序。请完成一个函数&#xff0c;输…

ctfshow VIP题目限免 版本控制泄露源码2

根据题目提示是版本控制泄露源码 版本控制&#xff08;Version Control&#xff09;是一种在软件开发和其他领域中广泛使用的技术&#xff0c;用于管理文件或项目的变更历史。 主流的版本控制工具&#xff1a; ‌Git‌&#xff1a;目前最流行的分布式版本控制系统。‌SVN‌&am…

蓝牙跳频扩频技术的作用:提升抗干扰能力与通信可靠性的核心机制

在无线通信技术领域&#xff0c;蓝牙&#xff08;Bluetooth&#xff09;以其短距离、低功耗和高兼容性成为连接电子设备的首选方案。其核心技术之一 ——跳频扩频&#xff08;Frequency Hopping Spread Spectrum, FHSS&#xff09;&#xff0c;是蓝牙在2.4 GHz ISM频段复杂电磁…

推荐系统(二十二):基于MaskNet和WideDeep的商品推荐CTR模型实现

在上一篇文章《推荐系统&#xff08;二十一&#xff09;&#xff1a;基于MaskNet的商品推荐CTR模型实现》中&#xff0c;笔者基于 MaskNet 构建了一个简单的模型。笔者所经历的工业级实践证明&#xff0c;将 MaskNet 和 Wide&Deep 结合应用&#xff0c;可以取得不错的效果&…

Ubuntu挂载HDD迁移存储PostgreSQL数据

关联博客&#xff1a;windows通用网线连接ubuntu实现ssh登录、桌面控制、文件共享 背景&#xff1a; 在个人ubuntu机器上安装了pgsql&#xff0c;新建了一张表插入了2000w数据用于模拟大批量数据分页查询用&#xff0c;但是发现查询也不慢&#xff08;在公司测试环境查询1700…

Flink CDC Pipeline mysql to doris

版本兼容 flink 与 flink-cdc版本兼容 flink 与doris版本兼容 运行同步程序 最终在 flink-1.20.1 与 flink-cdc-3.1.1 跑通测试 配置yaml文件 [rootchb1 flink-cdc-3.1.1]# cat mysql2doris.yaml ##################################################################…

Git 教程:从 0 到 1 全面指南 教程【全文三万字保姆级详细讲解】

目录 什么是 Git &#xff1f; Git 与 SVN 区别 Git 安装配置 Linux 平台上安装 Centos/RedHat 源码安装 Windows 平台上安装 使用 winget 工具 Mac 平台上安装 Git 配置 用户信息 文本编辑器 差异分析工具 查看配置信息 生成 SSH 密钥&#xff08;可选&#xf…

【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 的未来:从微服务到云原生的演进

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、引子&…

beego文件上传

1file.go 2html代码 3路由设置 beego.Router("/file/Upload", &controllers.FileUploadController{}, "post:Upload") 注意 1&#xff0c;得新建个upload文件夹 2&#xff0c;路由设置严格区分大小写。 biiego文件下载上传代码 github 觉得不错Star下

2025-04-05 吴恩达机器学习5——逻辑回归(2):过拟合与正则化

文章目录 1 过拟合1.1 过拟合问题1.2 解决过拟合 2 正则化2.1 正则化代价函数2.2 线性回归的正则化2.3 逻辑回归的正则化 1 过拟合 1.1 过拟合问题 欠拟合&#xff08;Underfitting&#xff09; 模型过于简单&#xff0c;无法捕捉数据中的模式&#xff0c;导致训练误差和测试误…