前言

此博客记录对于TinyWebServer项目的学习,并根据自己的理解做出些许更改。
原项目地址:https://github.com/qinguoyi/TinyWebServer

Log

日志系统是用来存储程序运行中的通知信息、warning、error等。
首要任务就是格式化输出字符串到一个文件,还应当记录消息产生的时间,此系统选择使用按天作为文件名。
以消费者-生产者模式,使用阻塞队列实现线程异步处理Log消息。
还实现了主线程同步处理Log消息。

Log.h

从头文件可以清楚看出Log类具有的功能。

#ifndef LOG_H
#define LOG_H#include <stdio.h>
#include <iostream>
#include <string>
#include <stdarg.h>
#include <pthread.h>
#include "block_queue.h"using namespace std;class Log
{
public://C++11以后,使用局部变量懒汉不用加锁static Log *get_instance();//异步写的线程static void *flush_log_thread(void *args){Log::get_instance()->async_write_log();}//可选择的参数有日志文件、日志缓冲区大小、最大行数以及最长日志条队列bool init(const char *file_name, int close_log, int log_buf_size = 8192, int split_lines = 5000000, int max_queue_size = 0);//写入消息void write_log(int level, const char *format, ...);//刷新缓冲区void flush(void);private:Log();virtual ~Log();void *async_write_log();private:char dir_name[128]; //路径名char log_name[128]; //log文件名int m_split_lines;  //日志最大行数int m_log_buf_size; //日志缓冲区大小long long m_count;  //日志行数记录int m_today;        //因为按天分类,记录当前时间是那一天FILE *m_fp;         //打开log的文件指针char *m_buf;block_queue<string> *m_log_queue; //阻塞队列bool m_is_async;                  //是否同步标志位locker m_mutex;int m_close_log; //关闭日志
};
//使用宏定义,便于调用, 使用##__VA_ARGS__,支持format后面可有0到多个参数
#define LOG_DEBUG(format, ...) if(0 == m_close_log) {Log::get_instance()->write_log(0, format, ##__VA_ARGS__); Log::get_instance()->flush();}
#define LOG_INFO(format, ...) if(0 == m_close_log) {Log::get_instance()->write_log(1, format, ##__VA_ARGS__); Log::get_instance()->flush();}
#define LOG_WARN(format, ...) if(0 == m_close_log) {Log::get_instance()->write_log(2, format, ##__VA_ARGS__); Log::get_instance()->flush();}
#define LOG_ERROR(format, ...) if(0 == m_close_log) {Log::get_instance()->write_log(3, format, ##__VA_ARGS__); Log::get_instance()->flush();}#endif

init

//异步需要设置阻塞队列的长度,同步不需要设置
bool Log::init(const char *file_name, int close_log, int log_buf_size, int split_lines, int max_queue_size)
{//如果设置了max_queue_size,则设置为异步if (max_queue_size >= 1){m_is_async = true;m_log_queue = new block_queue<string>(max_queue_size);pthread_t tid;//flush_log_thread为回调函数,这里表示创建线程异步写日志pthread_create(&tid, NULL, flush_log_thread, NULL);}m_close_log = close_log;m_log_buf_size = log_buf_size;m_buf = new char[m_log_buf_size];memset(m_buf, '\0', m_log_buf_size);m_split_lines = split_lines;time_t t = time(NULL);struct tm *sys_tm = localtime(&t);struct tm my_tm = *sys_tm;//‘/’最后出现的位置const char *p = strrchr(file_name, '/');char log_full_name[256] = {0};if (p == NULL){//没有'/',直接用时间+file_name作为文件名snprintf(log_full_name, 255, "%d_%02d_%02d_%s", my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday, file_name);}else{//有‘/’,把前面的路径提取出来作为文件路径strcpy(log_name, p + 1);strncpy(dir_name, file_name, p - file_name + 1);snprintf(log_full_name, 255, "%s%d_%02d_%02d_%s", dir_name, my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday, log_name);}m_today = my_tm.tm_mday;m_fp = fopen(log_full_name, "a");if (m_fp == NULL){return false;}return true;
}

write_log实现

这里主要是用到对字符操作的知识,因为要格式化输出,这里简单介绍两个函数:
snprintf()函数用于实现将多个参数格式化输入到一个字符串;
例如

int n = snprintf(m_buf, 48, "%d-%02d-%02d %02d:%02d:%02d.%06ld %s ",my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday,my_tm.tm_hour, my_tm.tm_min, my_tm.tm_sec, now.tv_usec, s);

vsnprintf()函数同样用于实现将多个参数格式化输入到一个字符串,使用va_list结构体获取指定参数后面的参数
例如

int m = vsnprintf(m_buf + n, m_log_buf_size - 1, format, valst);

fomat可以为写入的格式,valst为格式对应的参数,valst也可以为空,不包含参数。
两个函数的返回值都是后面的字符串的长度,字符串长度大于写入size的时候会被截断写入,但是返回值依旧是字符串的长度而不是写入的长度。

void Log::write_log(int level, const char *format, ...)
{struct timeval now = {0, 0};gettimeofday(&now, NULL);time_t t = now.tv_sec;struct tm *sys_tm = localtime(&t);struct tm my_tm = *sys_tm;char s[16] = {0};switch (level){case 0:strcpy(s, "[debug]:");break;case 1:strcpy(s, "[info]:");break;case 2:strcpy(s, "[warn]:");break;case 3:strcpy(s, "[erro]:");break;default:strcpy(s, "[info]:");break;}//写入一个log,对m_count++, m_split_lines最大行数m_mutex.lock();m_count++;//日期改变(不是同一天),或者达到最大行,新建log文件if (m_today != my_tm.tm_mday || m_count % m_split_lines == 0) //everyday log{char new_log[256] = {0};fflush(m_fp);fclose(m_fp);char tail[16] = {0};snprintf(tail, 16, "%d_%02d_%02d_", my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday);if (m_today != my_tm.tm_mday){snprintf(new_log, 255, "%s%s%s", dir_name, tail, log_name);m_today = my_tm.tm_mday;m_count = 0;}else{snprintf(new_log, 255, "%s%s%s.%lld", dir_name, tail, log_name, m_count / m_split_lines);}m_fp = fopen(new_log, "a");}m_mutex.unlock();va_list valst;//获取可变参数,传入的format后的参数va_start(valst, format);string log_str;m_mutex.lock();//写入的具体时间内容格式int n = snprintf(m_buf, 48, "%d-%02d-%02d %02d:%02d:%02d.%06ld %s ",my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday,my_tm.tm_hour, my_tm.tm_min, my_tm.tm_sec, now.tv_usec, s);//这里m是输入字符串的长度,vsnprintf最后一位默认为'\0'int m = vsnprintf(m_buf + n, m_log_buf_size - 1, format, valst);//这里m_log_buf_size如果小于输入n+m,会发生数组越界//m_buf[n + m] = '\n';//m_buf[n + m + 1] = '\0';//修改如下if(n + m + 1 > m_log_buf_size - 1){m_buf[m_log_buf_size - 2] = '\n';m_buf[m_log_buf_size - 1] = '\0';}else{m_buf[n + m] = '\n';m_buf[n + m + 1] = '\0';}log_str = m_buf;m_mutex.unlock();if (m_is_async && !m_log_queue->full()){//异步模式放到队列里等待处理m_log_queue->push(log_str);}else{//同步模式直接写入m_mutex.lock();fputs(log_str.c_str(), m_fp);m_mutex.unlock();}va_end(valst);
}

其中异步模式,是将处理好的字符串放到了阻塞队列中,等待处理线程进行处理,处理线程只需要取出该字符串,将其写入文件

void* Log::async_write_log()
{string single_log;//从阻塞队列中取出一个日志string,写入文件流while (m_log_queue->pop(single_log)){m_mutex.lock();//写入文件fputs(single_log.c_str(), m_fp);m_mutex.unlock();}
}

测试

同步模式

#include "log.h"
int main()
{int m_close_log = 0;Log::get_instance()->init("log_test", 0, 60);LOG_DEBUG("debug test");LOG_INFO("%d, %s\n", 22, "abc");return 0;
}

异步模式

设置两个写入线程,分别写入INFO和WARN

#include "log.h"
#include <unistd.h>
int m_close_log = 0;
static int count = 0;
locker mutex;
void* log_info(void *arg)
{while (1){usleep(1000);mutex.lock();LOG_INFO("INFO: %d", ++count);mutex.unlock();}
}
void* log_warn(void *arg)
{while (1){usleep(1000);mutex.lock();LOG_WARN("WARN: %d", ++count);mutex.unlock();}
}int main()
{Log::get_instance()->init("../log_info", 0, 60, 800, 20);pthread_t info, warn;pthread_create(&info, NULL, log_info, NULL);pthread_create(&warn, NULL, log_warn, NULL);sleep(1);pthread_cancel(info);pthread_cancel(warn);return 0;
}

因为最大行设置的为800,所以log信息被保存为两个文件,内容如下图所示。

webserver之日志系统相关推荐

  1. linux 任务计划 权限设置,Linux系统 文件权限+计划任务+日志系统

    实验目的:熟练掌握权限相关概念和具体操作 实验环境:Red Hat Enterprise Linux Server 5.3 实验步骤: 一.文件权限 二.计划任务 三.日志 一.文件权限 (一)基本权 ...

  2. 日志系统新贵Loki,确实比笨重的ELK轻

    来自:CSDN(作者:linkt1234) 原文链接: https://blog.csdn.net/Linkthaha/article/details/100575278 最近,在对公司容器云的日志方 ...

  3. cx_oracle写日志信息_日志系统的设计

    笔者在写作本章节的时候,并不敢把此章节的标题叫做<高性能日志系统的设计>,之所以不敢加上"高性能"三个字的原因是: 第一,我对于日志系统设计知识和经验都来自于学习和工作 ...

  4. 抛弃ELK!Loki日志系统详解!

    背景 最近,在对公司容器云的日志方案进行设计的时候,发现主流的ELK或者EFK比较重,再加上现阶段对于ES复杂的搜索功能很多都用不上最终选择了Grafana开源的Loki日志系统,下面介绍下Loki的 ...

  5. 百亿级日志系统架构设计及优化

    作者:杨津萍,大数据架构师,从业十余年,专攻 Web 架构及大数据架构. 来自:51cto技术栈(ID:blog51cto) " 日志数据是最常见的一种海量数据,以拥有大量用户群体的电商平台 ...

  6. PHP框架中的日志系统

    引言 接触过php框架的朋友们可能都知道,日志在项目中的重要作用了,他可以帮助我们定位错误的位置,让程序更友好(处理得当的话不会直接抛出一大堆只有程序猿才真正动的英文),调试的时候也会很方便,还可以记 ...

  7. 软件架构自学笔记-- 畅途亿级业务日志系统演变过程

    畅途亿级业务日志系统演变过程 原文链接:https://www.infoq.cn/article/xWlmaS0-rdIjUgNrTjGI 痛点 你是否遇到过如下情况:线上偶尔出现一个问题,但我们并不 ...

  8. 几十条业务线日志系统如何收集处理?

    在互联网迅猛发展的今天 各大厂发挥十八般武艺的收集用户的各种信息,甚至包括点击的位置,我们也经常发现自己刚搜完一个东西,再打开网页时每个小广告都会出现与之相关联的商品或信息,在感叹智能的同时不惊想 什 ...

  9. 大型互联网应用中的日志系统

    2019独角兽企业重金招聘Python工程师标准>>> 大型互联网应用的突出特点是应用本身规模大,结构复杂,用户访问量大.设计良好的日志系统,有助于分析流量趋势,帮助管理网络应用:有 ...

最新文章

  1. C语言指针数组,遍历查询!_只愿与一人十指紧扣_新浪博客
  2. AI算法效率每16个月提速一倍,算力革命超越摩尔定律
  3. Mocha Pro 2020中文版
  4. binlog2mysql,MySQL 数据恢复工具之binlog2sql
  5. 来呀,快活呀。iOS 超级码农群:538549344
  6. matplotlib axis
  7. 灰度变换——反转,对数变换,伽马变换,灰度拉伸,灰度切割,位图切割
  8. onenote2019导入_将OneNote 2007笔记本导入Evernote
  9. 用matlab产生chu序列和frank序列
  10. 高中补习数学:导数与函数的综合问题(2019年测试题)
  11. Word转PDF方法怎么转?这三种Word转PDF方法你得知道
  12. 快不快乐用计算机,快乐学计算机
  13. IDEA 2018 激活补丁 激活到2100年 ,最新版不用注册机
  14. O2O商业模式目前的状态需看透什么?未来会呈现怎样的特征?
  15. python画二次函数图像的顶点坐标为_二次函数顶点坐标公式
  16. 怎样理解vue中的slot
  17. 如何提升数据思维能力?
  18. Air780E模块RNDIS应用开发指南
  19. 无懈可击 css html5,无懈可击的Web设计(第3版) 带目录书签完整pdf[80MB]
  20. FPGA 结构分析 -IO 资源

热门文章

  1. 数据中台、数据仓库和数据湖传统的区别
  2. gdb学习(一)[第二版]
  3. 网页结构的简介和Xpath语法的入门教程
  4. 在应用开发中,网易云音乐如何兼顾质量和效益
  5. 蚌埠学院官网综合新闻条目抓取
  6. 201521123087 蒋勃超 软工作业
  7. [开发笔记]-C#获取pdf文档的页数
  8. C# 延时小函数 很好用
  9. 大道至简第三章。感受。
  10. Linux各个目录的作用及内容