编写一个可跨平台的Logger模块

2023-11-15

    编写项目时,不可避免要使用到日志模块,有时候还是十分有帮助的,用过的比较好的有log4cpp和其他一些,不多做介绍。这里也只是和大家分享以及记录自己编写的一个小的实用log模块而已。

// ccompat.h


#ifndef _CCOMPAT_H
#define _CCOMPAT_H

// Variable length arrays.
// VLA(type, name, size) allocates a variable length array with automatic
// storage duration. VLA_SIZE(name) evaluates to the runtime size of that array
// in bytes.
//
// If C99 VLAs are not available, an emulation using alloca (stack allocation
// "function") is used. Note the semantic difference: alloca'd memory does not
// get freed at the end of the declaration's scope. Do not use VLA() in loops or
// you may run out of stack space.
#if !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
// C99 VLAs.
#define VLA(type, name, size) type name[size]
#define SIZEOF_VLA sizeof
#else

// Emulation using alloca.
#ifdef _WIN32
#include <malloc.h>
#elif defined(__linux__)
#include <alloca.h>
#else
#include <stdlib.h>
#if !defined(alloca) && defined(__GNUC__)
#define alloca __builtin_alloca
#endif
#endif

#define VLA(type, name, size)                           \
  const size_t name##_size = (size) * sizeof(type);     \
  type *const name = (type *)alloca(name##_size)
#define SIZEOF_VLA(name) name##_size

#endif

#if !defined(__cplusplus) || __cplusplus < 201103L
#define nullptr NULL
#endif

#ifdef __GNUC__
#define GNU_PRINTF(f, a) __attribute__((__format__(__printf__, f, a)))
#else
#define GNU_PRINTF(f, a)
#endif

#ifndef UNUSED
#define UNUSED(context) (void*)0
#endif

#if !defined(__cplusplus)
#define true  1
#define false 0
#endif



#endif // _CCOMPAT_H

 

// logger.h

#ifndef _LOGGER_H
#define _LOGGER_H

#include <stdint.h>

#include "ccompat.h"
#include <stdio.h>
#include <time.h>
#include <string.h>

#ifndef MIN_LOGGER_LEVEL
#define MIN_LOGGER_LEVEL LOGGER_LEVEL_INFO
#endif

// NOTE: Don't forget to update build system files after modifying the enum.
typedef enum Logger_Level {
    LOGGER_LEVEL_TRACE,
    LOGGER_LEVEL_DEBUG,
    LOGGER_LEVEL_INFO,
    LOGGER_LEVEL_WARNING,
    LOGGER_LEVEL_ERROR
} Logger_Level;
typedef struct Logger Logger;


#define LOGGER_NO_OUT  (0)      /* do not print logger message*/
#define LOGGER_STDOUT  (1)      /* print message to stdout*/
#define LOGGER_TO_FILE (2)      /* save logger message to logger file*/

extern int logger_mode;

typedef void logger_cb(void *context, Logger_Level level, const char *file, int line,
                       const char *func, const char *message, void *userdata);


/**
  * Sets logger output mode.
  */
void set_logger_mode(int mode);

/**
 * Creates a new logger with logging disabled (callback is NULL) by default.
 */
Logger *logger_new(void);

/**
 * Frees all resources associated with the logger.
 */
void logger_kill(Logger *log);

/**
 * Sets the logger callback. Disables logging if set to NULL.
 * The context parameter is passed to the callback as first argument.
 */
void logger_callback_log(Logger *log, logger_cb *function, void *context, void *userdata);

/**
 * Main write function. If logging is disabled, this does nothing.
 *
 * If the logger is NULL, this writes to stderr. This behaviour should not be
 * used in production code, but can be useful for temporarily debugging a
 * function that does not have a logger available. It's essentially
 * fprintf(stderr, ...), but with timestamps and source location. Toxcore must
 * be built with -DUSE_STDERR_LOGGER for this to work. It will cause an
 * assertion failure otherwise.
 */
void logger_write(
    const Logger *log, Logger_Level level, const char *file, int line, const char *func,
    const char *format, ...) GNU_PRINTF(6, 7);


static inline char* get_cur_sys_time() {
    time_t timer;
    struct tm *tblock;
    char* time_tmp;
    timer = time(NULL);
    tblock = localtime(&timer);
    time_tmp = asctime(tblock);
    time_tmp[strlen(time_tmp)-1] = '\0';
    return time_tmp;
}


#define c_debug(fmt,...) \
    do { \
        if( int(logger_mode) ){\
            printf("[DATE: %s ] [ FILE: %s ] [ LINE: %4d ] [ FUNC: %s ] :"fmt"",get_cur_sys_time(),__FILE__,__LINE__,__FUNCTION__,##__VA_ARGS__);   \
        }                   \
    }while(0)

#define LOGGER_WRITE(log, level, ...) \
    do { \
        if (level >= MIN_LOGGER_LEVEL) { \
            logger_write(log, level, __FILE__, __LINE__, __func__, __VA_ARGS__); \
        } \
    } while (0)



/* To log with an logger */
#define LOGGER_TRACE(log, ...)   LOGGER_WRITE(log, LOGGER_LEVEL_TRACE  , __VA_ARGS__)
#define LOGGER_DEBUG(log, ...)   LOGGER_WRITE(log, LOGGER_LEVEL_DEBUG  , __VA_ARGS__)
#define LOGGER_INFO(log, ...)    LOGGER_WRITE(log, LOGGER_LEVEL_INFO   , __VA_ARGS__)
#define LOGGER_WARNING(log, ...) LOGGER_WRITE(log, LOGGER_LEVEL_WARNING, __VA_ARGS__)
#define LOGGER_ERROR(log, ...)   LOGGER_WRITE(log, LOGGER_LEVEL_ERROR  , __VA_ARGS__)

#define LOGGER_FATAL(log, ...) \
    do { \
        LOGGER_ERROR(log, __VA_ARGS__); \
        abort(); \
    } while(0)

#define LOGGER_ASSERT(log, cond, ...) \
    do { \
        if (!(cond)) { \
            LOGGER_ERROR(log, "Assertion failed"); \
            LOGGER_FATAL(log, __VA_ARGS__); \
        } \
    } while(0)

#endif // _LOGGER_H
// logger.cpp


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "logger.h"

#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(__WIN32)
#include <windows.h>
#include <pthread.h>
#else
#include <pthread.h>
#endif

struct Logger {
    logger_cb *callback;
    void *context;
    void *userdata;
};
extern int logger_mode = LOGGER_NO_OUT;

#ifdef USE_STDERR_LOGGER
static const char *logger_level_name(Logger_Level level)
{
    switch (level) {
    case LOGGER_LEVEL_TRACE:
        return "TRACE";

    case LOGGER_LEVEL_DEBUG:
        return "DEBUG";

    case LOGGER_LEVEL_INFO:
        return "INFO";

    case LOGGER_LEVEL_WARNING:
        return "WARNING";

    case LOGGER_LEVEL_ERROR:
        return "ERROR";
    }

    return "<unknown>";
}

static void logger_stderr_handler(void *context, Logger_Level level, const char *file, int line, const char *func,
                                  const char *message, void *userdata)
{
    // GL stands for "global logger".
    fprintf(stderr, "[GL] %s %s:%d(%s): %s\n", logger_level_name(level), file, line, func, message);
}

static const Logger logger_stderr = {
    logger_stderr_handler,
    nullptr,
    nullptr,
};
#endif

/**
 * Public Functions
 */

void set_logger_mode(int mode){
    logger_mode = mode;
}

Logger *logger_new(void)
{
    return (Logger *)calloc(1, sizeof(Logger));
}

void logger_kill(Logger *log)
{
    free(log);
}

void logger_callback_log(Logger *log, logger_cb *function, void *context, void *userdata)
{
    log->callback = function;
    log->context  = context;
    log->userdata = userdata;
}


/**
 * @brief logger_save_to_file
 * @param arg
 * @return
 */
#if !defined(__WIN32)
void* logger_save_to_file(void* arg){
#else
DWORD WINAPI logger_save_to_file(LPVOID arg){
#endif
    char logger_text[1024*2]={0};
    printf("[LPVOID arg in thread]:%s\n",(char*)arg);
    strcat(logger_text,(char*)arg);
    FILE * log_file= fopen("./log.txt","a+");
    if(log_file == nullptr) {
        fprintf(stderr, "open logger file failed.\n");
        return NULL;
    }
    fwrite(logger_text,strlen(logger_text),1,log_file);

    fclose(log_file);
    return 0;
}

void logger_write(const Logger *log, Logger_Level level, const char *file, int line, const char *func,
                  const char *format, ...)
{
    if (!log) {
#ifdef USE_STDERR_LOGGER
        log = &logger_stderr;
#else
        fprintf(stderr, "NULL logger not permitted.\n");
        abort();
#endif
    }

    if (!log->callback) {
        printf("callback is nullptr. error occured.");
        return;
    }

    // Only pass the file name, not the entire file path, for privacy reasons.
    // The full path may contain PII of the person compiling toxcore (their
    // username and directory layout).
    const char *filename = strrchr(file, '/');
    file = filename ? filename + 1 : file;

#if defined(_WIN32) || defined(__CYGWIN__)
    // On Windows, the path separator *may* be a backslash, so we look for that
    // one too.
    const char *windows_filename = strrchr(file, '\\');
    file = windows_filename ? windows_filename + 1 : file;
#endif
    // Format message
    char msg[1024]="";
    int size = 0;

    va_list args;
    va_start(args, format);
    size = vsnprintf(msg, sizeof(msg), format, args);
    va_end(args);

    log->callback(log->context, level, file, line, func, msg, log->userdata);


    if(logger_mode != LOGGER_TO_FILE) return;
    // save message to logger file ==>it's bad idea here when save too many message in files.
    char logger_text[1024*2]={0};
    sprintf(logger_text,"[Date: %s ][ %s : %d ][ %s ]: %s\n",get_cur_sys_time(),__FILE__,__LINE__,__func__,msg);

#if defined(__WIN32)
    HANDLE hThread;
    DWORD  threadId;
    hThread = CreateThread(NULL, 0,	logger_save_to_file,(LPVOID)logger_text, 0, &threadId);
    hThread == NULL ? printf("[Thread error]%s\n",stderr): printf("%d\n",GetCurrentThreadId());
    Sleep(1);
#else //linux
    pthread_t tid;
    int tret = pthread_create(&tid, NULL, logger_save_to_file, logger_text);
    if (tret != 0){
        printf("create pthread failed info: %s", strerror(tret));
        return tret;
    }

    sleep(1);
    int ret = pthread_join(tid, NULL);
    if (ret != 0){
        printf("pthread_join failed info: %s\n", strerror(ret));
        return ret;
    }
#endif
}




 

logger模块功能测试:

#include <iostream>
#include "ccompat.h"
#include "logger.h"
using namespace std;

typedef struct user_data{
    char* username;
    int age;
}*pud;

void logger_cb1(void *context, Logger_Level level, const char *file, int line,
                const char *func, const char *message, void *userdata){

    user_data* tmp = (user_data*)userdata;
    c_debug("[context]= %s "\
            "[file]= %s "\
            "[function]= %s "\
            "[line]= %d "\
            "[message]= %s "\
            "[userdata.username]= %s "\
            "[userdata.age]= %d \n",(char*)context,file,func,line,message,tmp->username,tmp->age);

    // dosomething here
    // ...
}


void* test_func(void* context){
    UNUSED(context);
    printf("ssss\n");

    return UNUSED();
}


void test_logger(Logger* log){

    char* context = "logger_callback_log_test";
    test_func((void*)context);

    VLA(user_data,userdata,1);// userdata 为user_data* 类型
    printf("%d \n",SIZEOF_VLA(userdata)); // 对齐原则

    userdata->username = "wangxiao";
    userdata->age = 10;

    logger_callback_log(log,logger_cb1,(void*)context,(void*)userdata); // bind logger callback function
    logger_write(log,LOGGER_LEVEL_DEBUG,"./log.txt",__LINE__,"%s","test logger_write to file when callback");

    LOGGER_WRITE(log,LOGGER_LEVEL_INFO,"she is so beautiful");

    LOGGER_FATAL(log,"error 0001");
    LOGGER_ASSERT(log,false,"");
}


int main()
{
    // set logger mode
    set_logger_mode(LOGGER_TO_FILE);

    c_debug("%s %s\n","this is"," MyDebug test.");

    // test logfunction
    Logger* log = logger_new();
    test_logger(log);
    logger_kill(log);

    return 0;
}


打印输出:

start when: Tue Apr 21 21:41:10 2020

[DATE: Tue Apr 21 21:41:10 2020 ] [ FILE: D:\QtCode\build-ThreadTest-unknown-Debug\MyDebug\main.cpp ] [ LINE: 63 ] [ FUNC: main ] :this is MyDebug test.

ssss

8

[DATE: Tue Apr 21 21:41:10 2020 ] [ FILE: D:\QtCode\build-ThreadTest-unknown-Debug\MyDebug\main.cpp ] [ LINE: 21 ] [ FUNC: logger_cb1 ] :[context]= logger_callback_log_test [file]= log.txt [function]= %s [line]= 48 [message]= test logger_write to file when callback [userdata.username]= wangxiao [userdata.age]= 10

5784

[LPVOID arg in thread]:[Date: Tue Apr 21 21:41:10 2020 ][ D:\QtCode\build-ThreadTest-unknown-Debug\MyDebug\logger.cpp : 159 ][ logger_write ]: test logger_write to file when callback

 

[DATE: Tue Apr 21 21:41:10 2020 ] [ FILE: D:\QtCode\build-ThreadTest-unknown-Debug\MyDebug\main.cpp ] [ LINE: 21 ] [ FUNC: logger_cb1 ] :[context]= logger_callback_log_test [file]= main.cpp [function]= test_logger [line]= 50 [message]= she is so beautiful [userdata.username]= wangxiao [userdata.age]= 10

5784

[LPVOID arg in thread]:[Date: Tue Apr 21 21:41:10 2020 ][ D:\QtCode\build-ThreadTest-unknown-Debug\MyDebug\logger.cpp : 159 ][ logger_write ]: she is so beautiful

 

[DATE: Tue Apr 21 21:41:10 2020 ] [ FILE: D:\QtCode\build-ThreadTest-unknown-Debug\MyDebug\main.cpp ] [ LINE: 21 ] [ FUNC: logger_cb1 ] :[context]= logger_callback_log_test [file]= main.cpp [function]= test_logger [line]= 52 [message]= error 0001 [userdata.username]= wangxiao [userdata.age]= 10

5784

[LPVOID arg in thread]:[Date: Tue Apr 21 21:41:10 2020 ][ D:\QtCode\build-ThreadTest-unknown-Debug\MyDebug\logger.cpp : 159 ][ logger_write ]: error 0001

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编写一个可跨平台的Logger模块 的相关文章

  • SpringBoot使用Nacos动态配置数据源

    SpringBoot SpringCloud项目部署运行后 如果使用硬编码方式定义数据源 那么如果需要更换数据库 就只能通过更改源码并重启的方式来达成目的 而SpringCloud生态中有配置中心这一组件 我们可以将数据源连接属性编写在配置

随机推荐

  • centos7修改语言环境($LANG)

    文章目录 1 问题 2 系统环境 3 解决方案 1 问题 安装centos7系统时 我默认选择的是LANG zh CN UTF 8 在执行各种命令后 英文中掺杂的中文显得有点格格不入 于是我想要将语言环境改成全英文的 2 系统环境 root
  • vue(9):自定义指令

    自定义指令 除了默认设置的核心指令 v model 和 v show Vue 也允许注册自定义指令 示例 注册一个全局指令 v focus 该指令的功能是在页面加载时 元素获得焦点 div p 请输入 p div
  • Java8 Stream 之sorted方法 排序讲解

    本文主要讲解 Java 8 Stream之sorted 排序示例 使用java 8 的流式sorted排序List集合 Stream sorted 字段排序 功能说明 以自然序或着自定义Comparator 接口排序规则来排序一个流 功能详
  • Vue组件化开发

    一 组件基础 1 组件介绍 组件 是一个独立显示的视图单元 在构建大型应用时 我们通常会把可复用性强的部分 例如 头部导航 侧边栏等需要在多个页面展示的部分 提取出来 最终组合成一个完整的页面 在vue中 组件是可复用的vue实例 2 组件
  • SVG: draw text

  • 对比学习系列(二)---MoCo 系列

    MoCo 系列 文章目录 MoCo 系列 自监督 MoCo V1 前言 动量对比 Dictionary as a queue Momentum update 代理任务 Shuffling BN 实验 MoCo V2 MoCo V3 参考 自
  • Proteus 8.9原理图转换为PCB图

    Proteus 8 9原理图转换为PCB图 1 点击PCB设计图标 2 定义边界 在Board Edge图层建立矩形 此矩形内放置所有元器件 3 选用自动布局 Auto Placer 勾选所有器件 也可手动布置 没有的元器件封装可以重新选择
  • java初阶---类和对象

    一 类和类的实例化 1 类就是一类对象的统称 对象就是这一类具体化的一个实例 一个类可以实例化无数个对象 2 声明一个类就是创建一个新的数据类型 而类在java中属于引用类型 java使用关键字class来声明类 3 基本语法 创建类 cl
  • “Failed to initialize NVML

    Checking nvidia driver version with error Failed to initialize NVML GPU access blocked by the operating system 解决办法 360安
  • 最大的以1为边界的正方形--动态规划

    leetcode 1139 最大的以1为边界的正方形 题目描述 给你一个由若干 0 和 1 组成的二维网格 grid 请你找出边界全部由 1 组成的最大 正方形 子网格 并返回该子网格中的元素数量 如果不存在 则返回 0 示例1 输入 gr
  • Macbook Pro 鼠标卡顿问题

    Macbook Pro 鼠标卡顿问题 目前无解 只能改善 该问题最早能追溯到 2015年 https jingyan baidu com article ff42efa93632c5c19e220208 html 原因 据说是无线频段冲突
  • 手把手教会机器学习(一)——特征工程

    Python机器学习 一 特征工程 特征工程 特征 Feature 当建立预测模型时 用以对结构经行分析预判 且与预测对象有较大相关性的变量 特征提取 Feature Extraction 特征的提取应注意避免提取到相关含义的变量 避免提取
  • mysql基本语句:DML(数据操作语言)

    目录 DML语言 1 INSERT 添加数据语句 2 UPDATE 更新数据语句 3 SQL中的运算符 4 DELETE 删除数据语句 DML语言 DML 数据操作语言 用于操作数据库对象中所包含的数据 DML包括 INSERT 添加数据语
  • dbeaver对数据进行可视化编辑

    进入数据编辑界面 然后在操作栏上点击编辑即可看到
  • java学习路线

    Java 学习路线 Java 学习路线一条龙版 by 程序员鱼皮 建议先观看视频导读 https www bilibili com video BV1Qf4y1K7ff 大纲 路线特点 最新 完整一条龙的大厂 Java 学习路线 从入门到入
  • 遥感影像分类之SVM

    目录 一 背景简介 二 环境介绍 三 分类步骤 3 1 训练数据预处理 3 2 导入相关扩展包 3 2 读取训练数据与训练标签 3 3 确定参数g和c 3 4 使用svm训练模型 3 5 使用模型进行分类 四 分类结果显示与保存 4 1 结
  • Thread类创建线程

    目录 1 在java中执行多线程编辑 1 1通过Thread类创建线程 1 2创建线程方法2 1 3创建线程方法3 1 4创建线程方法4 1 5创建线程方法5 2 多线程执行效率 2 1两个变量同时自增 3 Thread常见构造方法和属性
  • Git(三):暂存区处理与分支

    5 Git 操作 5 1 隐藏 Stash 在 Git 中 隐藏操作将能够修改跟踪文件 阶段更改 并将其保存在一系列未完成的更改中 并可以随时重新应用 当更改了文件的内容后 通过 git status 对跟踪文件进行检查 现在 要切换分支以
  • 使用预训练模型进行图片分类

    前言 TensorFlow js中加载预训练模型进行图片分类预测 项目结构 data mobileNet web model group1 shard1of1 bin model json mobileNet imagenet classe
  • 编写一个可跨平台的Logger模块

    编写项目时 不可避免要使用到日志模块 有时候还是十分有帮助的 用过的比较好的有log4cpp和其他一些 不多做介绍 这里也只是和大家分享以及记录自己编写的一个小的实用log模块而已 ccompat h ifndef CCOMPAT H de