日志的介绍及简单实现

个人主页:Lei宝啊 

愿所有美好如期而遇


目录

日志是什么?

为什么需要日志?

实现一个简单日志

时间戳

clock_gettime

time & localtime

可变模板参数(使用C语言),va_start & va_end &  vsprintf

宏  __LINE__ & __FILE__ & ##__VA_ARGS__

小技巧

代码实现


日志是什么?

日志(Log) 是记录系统、应用或设备在运行过程中产生的各种事件、状态、错误等信息的文件或数据流。这些日志信息对于了解系统的运行状况、排查问题、优化性能以及安全审计等方面都非常重要。

为什么需要日志?

  1. 问题排查:当系统或应用出现故障时,通过查看日志可以快速定位问题发生的原因和位置,从而加快故障解决的速度。
  2. 性能监控:通过分析日志,可以了解系统的运行状况,包括请求量、响应时间、资源使用情况等,从而发现性能瓶颈并进行优化。
  3. 安全审计:日志中记录了用户的操作行为、系统的访问情况等信息,这些信息可以用于安全审计,检查是否有异常操作或入侵行为。
  4. 合规性要求:在某些行业或地区,法律法规要求企业或组织必须保留一定期限的日志记录,以便在需要时进行审计或调查。
  5. 系统恢复:在某些情况下,如系统崩溃或数据丢失时,可以通过日志中的信息来恢复系统或数据。
  6. 版本追踪:对于软件来说,日志可以记录软件的版本变化、功能更新等信息,方便开发人员进行版本追踪和回滚。
  7. 用户行为分析:通过分析用户的操作日志,可以了解用户的使用习惯、喜好等信息,从而优化产品设计和提高用户体验

实现一个简单日志

实现日志前,我们先介绍几个函数以及几个小技巧:

时间戳

时间戳(Timestamp) 是一个表示特定时间点的数字或字符串。它通常表示从某个固定时间点(1970年1月1日00:00:00 UTC,也被称为Unix纪元或Unix时间戳)到特定事件发生时经过的秒数。

我们在Linux系统中有这样几个函数:

clock_gettime

第一个参数我们填CLOCK_REALTIME即可,表示系统实时时间。

#include <stdio.h>  
#include <time.h>  
  
int main() 
{  
    struct timespec ts;  

    if (clock_gettime(CLOCK_REALTIME, &ts) == 0) 
    {  
        printf("Current time: %ld.%09ld\n", ts.tv_sec, ts.tv_nsec);  
    } 
    else 
    {  
        perror("clock_gettime");  
        return 1;  
    }  

    return 0;  
}
time & localtime

time函数获取当前时间的时间戳。

locltime函数将时间戳转换成我们平时使用的年月日时分秒。 

可变模板参数(使用C语言),va_start & va_end &  vsprintf
void message(const char* format, ...){}  //可变模板参数

我们如何获取可变模板参数中的参数呢?

使用vsprintf函数:

这个函数用来将可变模板参数以format格式,将其写入到str指向的char类型数组中。 

而可变模板参数,将会使用va_list类型变量指向,实际上他是一个void*类型指针,我们定义后,还需要使用函数初始化以及销毁。

va_list args;
va_start(&args, format);
//...
va_end(&args);

va_start,初始化va_list类型变量,last就是可变模板参数左边第一个参数,这样这个函数就可以将args初始化为指向可变模板参数的指针。

va_end,将va_list类型变量清空。

char buffer[1024];
vsprintf(buffer, format, args); 
//这样就将可变模板参数中的内容以我们希望的格式获取到
//类似于printf("%d %s", xx,xx); "%d %s"就是格式,后面就是可变模板参数
宏  __LINE__ & __FILE__ & ##__VA_ARGS__
  • __LINE__ 当前代码所在行数
  • __FILE__ 当前代码所在文件
  • __VA_ARGS__    C99及C++支持的宏可变模板参数

 //##__VA_ARGS__ 中的 ## 操作符。当 __VA_ARGS__ 为空时(即没有额外的参数),
  ## 操作符会将其前面的逗号去掉,以防止在编译时产生语法错误。

简单来说,就是没有可变模板参数,只有格式,那么格式后面还有个逗号,##操作符会将其去掉。

小技巧

在宏替换中,关于语句的替换,以及多语句的替换,我们可以将其写在do{}while(0)中。

如果上述讲解没有理解,那么可以在下面的代码中进行理解,所有讲解都会在下面代码中有体现。

代码实现

#include <iostream>
#include <string>
#include <ctime>
#include <cstdarg>
#include <pthread.h>
#include <fstream>
using namespace std;

//##__VA_ARGS__ 中的 ## 操作符。当 __VA_ARGS__ 为空时(即没有额外的参数),
  ## 操作符会将其前面的逗号去掉,以防止在编译时产生语法错误。
#define Log(level, format, ...) do{LogMessage(__FILE__, __LINE__, level, 
                                  format, ##__VA_ARGS__);}while(0)
#define YSave do{IsSave = true;}while(0)
#define NSave do{IsSave = false;}while(0)

pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
string file_name_path = "log.txt";
bool IsSave = false; 

enum Level
{
    Debug = 0,
    Info,
    Warning,
    Error,
    Fatal
};

string LevelToString(int level)
{
    string name;
    switch (level)
    {
    case Debug   : return "Debug";
    case Info    : return "Info";
    case Warning : return "Warning";
    case Error   : return "Error";
    case Fatal   : return "Fatal";
    default: return "阿西吧";
    }
}

string TimeToString()
{
    time_t Time; time(&Time);
    struct tm* attr = localtime(&Time);

    char buffer[1024];
    sprintf(buffer,"%4d-%2d-%2d %2d:%2d:%2d",
                    attr->tm_year+1900,
                    attr->tm_mon+1,
                    attr->tm_mday,
                    attr->tm_hour,
                    attr->tm_min,
                    attr->tm_sec);
    
    return buffer;
}

void Save(string ret)
{
    fstream out(file_name_path.c_str(), ios::app); 
    if(out.is_open())
    {
        out << ret;
    }
    else
    {
        std::cerr << "无法打开文件: " << file_name_path << std::endl;
    }
    out.close();
}

// 2. 日志是有格式的
// 日志等级 时间 代码所在的文件名/行数 日志的内容
void LogMessage(string filename, int line, int level, const char* format, ...)
{
    string levelname = LevelToString(level);
    string timename = TimeToString();

    va_list args;           //一个void*指针
    va_start(args, format); //将args初始化,指向可变参数列表
    char buffer[1024]; vsprintf(buffer, format, args);
    va_end(args);

    string ret = "[" + filename + "]" + "[" + "line: " + to_string(line)
                   + "]" + "[" + levelname + "]" + "[" + timename + "]" 
                   + "[" + buffer + "]" + "\n";
    
    pthread_mutex_lock(&log_mutex);
    if(IsSave)
    {
        Save(ret);
    }
    else
    {
        cout << ret;
    }
    pthread_mutex_unlock(&log_mutex);
}




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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Anaconda+CUDA+CUDNN+Pycharm+Pytorch安装教程(第一节 Anconda安装)

1.选择和对应的anconda版本 官网地址&#xff1a;Index of / (anaconda.com) 下载地址&#xff1a;Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 2.安装流程 (1)下载安装包 (2)点击next &#xff08;3&#xff09;点击I agree &a…

跨域计算芯片,一把被忽视的汽车降本尖刀

作者 |王博 编辑 |德新 2019年前后&#xff0c;「中央运算单元区域控制」的架构被提出。基于这一趋势&#xff0c;从板级的多芯片&#xff0c;到板级的单芯片&#xff0c;集成度越来越高&#xff0c;跨域计算芯片随之来到聚光灯下。 跨域计算芯片的特点是&#xff0c;与专为智…

[CISCN2024]-PWN:gostack解析(go语言程序,syscall)

查看保护 ida比较复杂&#xff0c;建议动调配合静态分析程序运行 这里函数返回不用leave和ret&#xff0c;而是利用add rsp和ret&#xff0c;所以要动调查看到底要覆盖哪里。 完整exp&#xff1a; from pwn import* pprocess(./gostack) syscall0x4616c9 pop_rax0x40f984 po…

THREE.JS中的向量点乘,以及他的几何意义。

1. THREE.JS中的向量点乘&#xff0c;以及他的几何意义 向量点乘的公式 : 2. 在three.js 中计算向量点乘 const a new THREE.Vector3(10, 10, 0); const b new THREE.Vector3(20, 0, 0); const dot a.dot(b);从这里可以看出&#xff0c;向量的点乘的结果是一个数字(标量…

【调和级数】100321. 优质数对的总数 II

本文涉及知识点 调和级数 质数、最大公约数、菲蜀定理 LeetCode100321. 优质数对的总数 II 给你两个整数数组 nums1 和 nums2&#xff0c;长度分别为 n 和 m。同时给你一个正整数 k。 如果 nums1[i] 可以被 nums2[j] * k 整除&#xff0c;则称数对 (i, j) 为 优质数对&#…

【机器学习数据可视化-07】波士顿房价预测数据分析

波士顿房价预测&#xff1a;基于数据可视化的深入探索 一、引言   在当今社会&#xff0c;房地产市场作为经济的重要支柱之一&#xff0c;其走势与波动直接影响着国家经济的稳定和人民生活的品质。波士顿&#xff0c;这座历史悠久且充满活力的城市&#xff0c;其房地产市场一…

AtCoder Regular Contest 178 A~D

A.Good Permutation 2&#xff08;贪心&#xff09; 题意&#xff1a; 给你一个正整数 N N N和一个由 M M M个正整数 A ( A 1 , A 2 , … , A M ) A(A_{1},A_{2}, \dots,A_{M}) A(A1​,A2​,…,AM​)组成的序列。 在这里&#xff0c; A A A的所有元素都是介于 1 1 1和 N N …

胶原蛋白三肽能否深入皮肤?一场关于美丽的科学之旅

在追求美丽的道路上&#xff0c;我们总是对各种护肤成分充满好奇。今天&#xff0c;就让我们一起探讨一个热门话题——胶原蛋白三肽&#xff0c;它究竟能否深入我们的皮肤&#xff0c;为我们带来期待中的美丽改变呢&#xff1f; 首先&#xff0c;我们需要了解胶原蛋白肽是什么…

【编译原理复习笔记】中间语言

中间语言 中间语言的特点和作用 &#xff08;1&#xff09;独立于机器 &#xff08;2&#xff09;复杂性介于源语言和目标语言之间 中间语言可以使编译程序的结构在逻辑上更为简单明确 常用的中间语言 后缀式 图表示&#xff1a;抽象语法树&#xff0c;有向无环图 三地址代…

css卡片翻转 父元素翻转子元素不翻转效果

css卡片翻转 父元素翻转子元素不翻转效果 vue <div class"moduleBox"><div class"headTitle"><span class"headName">大额案例</span></div><div class"moduleItem"><span class"module…

时间(空间)复杂度(结构篇)

目录 前言&#xff1a; 一、时间复杂度 1.1 时间复杂度的定义 1.2 时间复杂度的分析 表示方法&#xff1a; 1.3 常见的时间复杂度 1.4 时间复杂度的计算以及简单的分析 冒泡排序 折半查找&#xff08;二分查找&#xff09; 斐波那契数列&#xff08;递归&#xff09…

手把手教学,一站式教你实现服务器(Ubuntu)Anaconda多用户共享

背景&#xff1a;书接上回&#xff0c;一站式安装Ubuntu及配置服务器手把手教学&#xff0c;一站式安装ubuntu及配置服务器-CSDN博客 在安装及配置好服务器后&#xff0c;因为课题组可能涉及多个用户共用一台服务器&#xff0c;为了防止服务器上代码误删和Anaconda环境管理混乱…

爽!AI手绘变插画,接单赚爆了!

我最近发现一款名叫Hyper-SD15-Scribble的AI项目&#xff0c;可以实现一键手绘变插画的功能&#xff0c;而且它搭载了字节出品的超快速生成图片的AI大模型Hyper-SD15&#xff0c;可以实现几乎实时生成图片&#xff0c;有了它&#xff0c;拿去接一些手绘商单分分钟出图&#xff…

安卓手机电脑平板均支持

最近随着人工智能的火热&#xff0c;越来越多人问我怎么设置&#xff0c;我这边主要提供简单的配置&#xff0c;能够实现想要的功能&#xff0c;不懂得的友友们可以私聊我&#xff0c;

MyBatis详细教程!!(入门版)

目录 什么是MyBatis&#xff1f; MyBatis入门 1&#xff09;创建工程 2&#xff09;数据准备 3&#xff09;配置数据库连接字符串 4&#xff09;写持久层代码 5&#xff09;生成测试类 MyBatis打印日志 传递参数 MyBatis的增、删、改 增&#xff08;Insert&#xff0…

鸿蒙 DevEco Studio 3.1 Release 下载sdk报错的解决办法

鸿蒙 解决下载SDK报错的解决方法 最近在学习鸿蒙开发&#xff0c;以后也会记录一些关于鸿蒙相关的问题和解决方法&#xff0c;希望能帮助到大家。 总的来说一般有下面这样的报错 报错一&#xff1a; Components to install: - ArkTS 3.2.12.5 - System-image-phone 3.1.0.3…

Django-auth组件

Django-auth组件 1 表结构 我们从python manage.py migrate为我们创建的auth组件内置的表开始看 auth_user&#xff1a;用户表存储用户信息&#xff08;登录admin后台&#xff09; 里面的字段分两类&#xff1a;用户基本信息&#xff08;用户名&#xff0c;邮箱&#xff0c;密…

【线程的互斥】

线程的互斥 临界区资源多个线程的运行多个线程对同一资源的竞争原子性保持线程之间地互斥互斥量(锁的原理)为什么是原子的 正确使用锁 临界区资源 进程创建线程&#xff0c;是共享内存的&#xff0c;可以对共享的资源有很方便的操作&#xff0c;当一些共享资源可以被多个线程进…