『 Linux 』简单日志插件

07-21 1051阅读

文章目录

    • 什么是日志
    • 应用日志的设计要点
    • 简单应用日志插件设计编码
      • 大概框架及所需头文件与定义
      • 日志信息的输出
      • 日志的输出方式
      • operator()重载

        什么是日志

        『 Linux 』简单日志插件

        日志(log)是一种记录和存储系统运行状态,事件,和操作历史的文件和数据库,这些记录通常以时间顺序排列且详细记录系统中发生的各种活动;

        日志的类型常见有以下几种:

        • 系统日志

          记录操作系统级别的事件,如启动,关机,错误和警告等;

        • 应用日志:

          记录应用程序运行过程中的事件,如用户操作,错误,状态变更等;

        • 安全日志

          记录系统和应用程序的安全相关事件,如登录尝试,权限变更等;

          日志的作用为以下:

          • 问题诊断和故障排查

            日志可以帮助系统管理员和开发人员诊断和解决问题;

          • 系统监控和维护

            通过分析日志,管理员可以监控系统的运行状态并且识别潜在的问题和异常;

          • 安全审计和合规性

            日志记录系统和应用程序的各种安全事件以帮助进行安全审计,同时确保系统符合安全和法规要求;

          • 性能分析和优化

            日志数据可以用于分析系统和应用程序的性能,并间接提升系统效率和响应速度;

          • 历史记录和追踪

            日志提供了系统和应用程序运行过程中的详细历史记录,可以用于追踪和回溯系统的操作和变更;

            日志记录通常包含以下基本信息:

            • 时间戳

              记录事件发生的具体事件;

            • 事件类型

              记录事件的类型,如错误,警告,信息等;

            • 事件来源

              知名事件是由哪个系统组件或者是应用程序生成的;

            • 事件描述

              详细描述事件的内容和相关信息;

              本文主要简单设计一个简单应用日志;


              应用日志的设计要点

              『 Linux 』简单日志插件

              应用日志专门记录应用程序正在运行过程中发生的各种事件,错误,状态变化等信息;

              应用日志可以帮助跟踪程序的执行过程并进行诊断从而了解具体问题;

              应用日志的日志级别主要如下:

              • DEBUG

                详细的信息,一般用于诊断问题,经常用于开发过程;

              • INFO

                普通操作信息,用于记录应用程序的正常运行情况;

              • WARNING

                警告信息,标明看那会出现问题的情况,但程序仍可以正常运行;

              • ERROR

                错误信息,标明程序中的某些功能出现问题需要引起注意;

              • CRITICAL/FATAL

                严重错误(致命)信息,标明程序可能无法继续运行,需要立即解决;

                日志的统一格式有助于日志的解析和分析;

                常见的格式包括JSON,XML或简单的纯文本格式;

                如:

                2024-07-15 10:00:00 [INFO] [module_name] - This is an info message.
                2024-07-15 10:01:00 [ERROR] [module_name] - An error occurred.
                

                日志的存储通常有以下几种:

                • 文件

                  将日志记录到文件中,方便查看和归档;

                • 数据块

                  将日志存储到数据库中,便于查询和分析;

                • 日志管理系统

                  使用专门的日志管理工具,如ELK堆栈,进行集中化管理和分析;


                  简单应用日志插件设计编码

                  『 Linux 』简单日志插件

                  主要思路为封装一个为log的类;

                  在类中实现根据错误编码产生的日志并使用对应的方式打印至显示器或写入文件中;

                  文件为:

                  • log.hpp

                    该文件主要封装日志插件并实现;

                  • makefile

                    自动化构建过程文件;

                  • test.cc

                    测试文件;


                    大概框架及所需头文件与定义

                    『 Linux 』简单日志插件

                    #pragma once
                    #include 
                    #include 
                    #include 
                    #include 
                    #include 
                    #include 
                    #include 
                    #define INFO 0     // 常规信息
                    #define DEBUG 1    // debug信息
                    #define WARNING 2  // 警告信息
                    #define ERROR 3    // 错误信息
                    #define FATAL 4    // 致命错误信息
                    #define SCREEN 1    // 向显示器文件写入
                    #define ONEFILE 2   // 向单个文件进行写入
                    #define CLASSIFY 3  // 分类将日志进行写入
                    #define LOG_BUFFER_SIZE 1024  // 日志所需大小
                    #define LogFile "log.txt"  // 默认日志文件文件名
                    class Log {
                     public:
                      Log() {  // 构造函数 用于初始化默认打印方式以及设置默认路径
                        printMethod = SCREEN;
                        path = "./log/";
                      }
                      ~Log() { ; }  // 析构函数 未使用
                     private:
                      int printMethod;   // 打印风格
                      std::string path;  // 默认路径
                    };
                    

                    构造函数用于初始化默认打印风格以及初始化默认路径;

                    利用 #define 定义出可能需要使用的常量同时包含所需头文件;


                    日志信息的输出

                    『 Linux 』简单日志插件

                    此处使用的日志输出格式为:

                    [loglevel][time] logmassage
                    

                    日志的格式主要为 : 默认部分 + 自定义部分;

                    默认部分一般为 日志等级 与 日志时间 ;

                    • 日志等级

                      日志等级需要以字符串的形式进行显示,则可以使用switch() case:对日志等级进行返回;

                        const std::string levelToString(int level) {  // 以字符串形式获取日志等级
                          switch (level) {
                            case INFO:
                              return "INFO";
                            case DEBUG:
                              return "DEBUG";
                            case WARNING:
                              return "WARNING";
                            case ERROR:
                              return "ERROR";
                            case FATAL:
                              return "FATAL";
                            default:
                              return "NONE";
                          }
                        }
                      

                      需要注意此处返回的是std::string类型,若是需要打印字符串或是当做字符串使用需要调用其string::c_str();

                    • 日志时间

                      日志时间一般采用时间戳的方式获取;

                      常见的系统调用接口有time()系统调用接口与gettimeofday()系统调用接口,此处使用time();

                      NAME
                             time - get time in seconds
                      SYNOPSIS
                             #include 
                             time_t time(time_t *t);
                      DESCRIPTION
                             time() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
                             If t is non-NULL, the return value is also stored in the memory pointed to by t.
                      RETURN VALUE
                             On  success,  the  value  of  time in seconds since the Epoch is returned.  On error, ((time_t) -1) is returned, and errno is set
                             appropriately.
                      

                      调用time()系统调用接口将会返回time_t类型,实际打印的是未格式化的时间戳;

                      若是需要格式化需要调用localtime()接口将时间戳进行实例化;

                      NAME
                             asctime,  ctime,  gmtime,  localtime,  mktime, asctime_r, ctime_r, gmtime_r, localtime_r - transform date and time to broken-down
                             time or ASCII
                      SYNOPSIS
                             #include 
                             struct tm *localtime(const time_t *timep);
                      

                      其会返回一个struct tm类型的结构体,其结构体构造如下:

                                 struct tm {
                                     int tm_sec;         /* seconds */
                                     int tm_min;         /* minutes */
                                     int tm_hour;        /* hours */
                                     int tm_mday;        /* day of the month */
                                     int tm_mon;         /* month */
                                     int tm_year;        /* year */
                                     int tm_wday;        /* day of the week */
                                     int tm_yday;        /* day in the year */
                                     int tm_isdst;       /* daylight saving time */
                                 };
                      

                      可根据需要获取结构中数据;

                      可用printf打印进行debug显示数据是否正确:

                       printf("%d-%d-%d %d:%d:%d\n", ctime->tm_year + 1900, ctime->tm_mon + 1,
                               ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
                      

                      在time()系统调用接口中介绍了时间戳的开始时间,为了显示正确时间需要加上对应的数值;

                      自定义部分需要使用可变参数列表即...;

                      需要调用vsnprintf()来获取用户自定义的内容:

                      NAME
                             printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - formatted output conversion
                      SYNOPSIS
                           
                             #include 
                             int vsnprintf(char *str, size_t size, const char *format, va_list ap);
                         Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
                             snprintf(), vsnprintf():
                                 _BSD_SOURCE || _XOPEN_SOURCE >= 500 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L;
                                 or cc -std=c99
                      DESCRIPTION
                             The  functions  in  the  printf()  family  produce  output  according to a format as described below.  The functions printf() and
                             vprintf() write output to stdout, the standard output stream; fprintf() and vfprintf() write output to the given  output  stream;
                             sprintf(), snprintf(), vsprintf() and vsnprintf() write to the character string str.
                      

                      在使用可变参数的时候需要定义va_list变量来表示可变参数;

                      并且使用va_start()来初始化一个va_list类型的变量,这个变量用来访问传递给函数的可变参数;

                      结束使用后需要用va_end()结束对可变参数的访问;

                          va_list s;
                          va_start(s, format);
                          char custombuffer[LOG_BUFFER_SIZE];
                          vsnprintf(custombuffer, sizeof(custombuffer), format, s);
                          va_end(s);
                      

                      当获取结束默认部分与自定义部分后调用snprintf()将两个信息组合成一条信息,并根据接下来的需求输出日志信息;

                      • 输出日志信息代码

                          void logmessage(int level, const char* format, ...) {
                            // 获取时间
                            time_t currentTime = time(nullptr);
                            struct tm* ctime = localtime(&currentTime);
                            // 格式: 默认部分 + 自定义部分
                            // 默认部分
                            char defaultbuffer[LOG_BUFFER_SIZE];
                            snprintf(defaultbuffer, sizeof(defaultbuffer), "[%s][%d-%d-%d %d:%d:%d]",
                                     levelToString(level).c_str(), ctime->tm_year + 1900,
                                     ctime->tm_mon + 1, ctime->tm_mday, ctime->tm_hour,
                                     ctime->tm_min, ctime->tm_sec);
                            // 自定义部分
                            va_list s;
                            va_start(s, format);
                            char custombuffer[LOG_BUFFER_SIZE];
                            vsnprintf(custombuffer, sizeof(custombuffer), format, s);
                            va_end(s);
                            // 组合
                            char logtxt[LOG_BUFFER_SIZE * 2];
                            snprintf(logtxt, sizeof(logtxt), "%s %s\n", defaultbuffer, custombuffer);
                            // 组合完毕后可直接打印也可直接写到文件中
                            // std::cout  printMethod = method; }  // 更改日志输出方式
                          // 不同方式输出日志
                            switch (printMethod) {
                              case SCREEN:
                                /* code */
                                std::cout   // 单个文件输出日志
                            std::string _logname = path + logname;
                            int fd = open(_logname.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0666);
                            if (fd tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                                     ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
                            // 自定义部分
                            va_list s;
                            va_start(s, format);
                            char custombuffer[LOG_BUFFER_SIZE];
                            vsnprintf(custombuffer, sizeof(custombuffer), format, s);
                            va_end(s);
                            // 组合
                            char logtxt[LOG_BUFFER_SIZE * 2];
                            snprintf(logtxt, sizeof(logtxt), "%s %s\n", defaultbuffer, custombuffer);
                            // 组合完毕后可直接打印也可直接写到文件中
                            // std::cout 
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]