文章目录
- 日志系统
- 通信程序
- 运行效果
日志系统
// log.hpp
#pragma once
#include <time.h>
#include <iostream>
#include <stdio.h>
#include <string>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstdlib>
using std::cout;
using std::endl;
const int MAX_LOG_SIZE = 1024;
// 打开的文件名
const char* LOG_FILE_NAME = "log.txt";
// 日志等级
enum LEVEL
{
INFO = 1,
WARNING,
ERROR,
FATAL,
DEBUG
};
// 打印方式
enum PRINT_METHOD
{
SCREEN = 1,
ONE_FILE,
MULTIPLE_FILE
};
class Log
{
public:
Log(PRINT_METHOD printMethod = SCREEN)
: _printMethod(printMethod), _path("./log/"){}
std::string LevelToString(LEVEL level)
{
switch (level)
{
case INFO:
return "INFO";
break;
case WARNING:
return "WARNING";
break;
case ERROR:
return "ERROR";
break;
case FATAL:
return "FATAL";
break;
case DEBUG:
return "DEBUG";
break;
default:
return "NONE";
break;
}
}
// 更改打印方式
void ChangePrintMethod(PRINT_METHOD printMethod)
{
_printMethod = printMethod;
}
/*需要将字符串处理为: 默认部分+自定义部分*/
void LoadMessage(LEVEL level, const char *format, ...)
{
// 默认部分, 时间
char leftBuffer[MAX_LOG_SIZE] = {0};
time_t t = time(nullptr);
struct tm *ctime = localtime(&(t));
snprintf(leftBuffer, sizeof(leftBuffer), "[%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, LevelToString(level).c_str());
// 自定义部分, 用户输入
char rightBuffer[MAX_LOG_SIZE] = {0};
// 因为定义了可变参数, 所以需要处理一下
va_list args;
va_start(args, format);
vsnprintf(rightBuffer, sizeof(rightBuffer), format, args);
va_end(args);
// 将两者加到一起
char logTxt[MAX_LOG_SIZE * 2] = {0};
snprintf(logTxt, sizeof(logTxt), "%s %s\n", leftBuffer, rightBuffer);
// cout << logTxt;
PrintLog(level, logTxt);
}
// 通过不同的输出方式, 将logTxt打印到不同的地方
void PrintLog(LEVEL level, const std::string& logTxt)
{
switch (_printMethod)
{
case SCREEN:
cout << logTxt;
break;
case ONE_FILE:
PrintOneFile(LOG_FILE_NAME, logTxt);
break;
case MULTIPLE_FILE:
PrintMultipleFile(level, logTxt);
break;
default:
break;
}
}
// 向一个文件中写
void PrintOneFile(const std::string& fileName, const std::string& logTxt)
{
std::string logName = _path + fileName; // 将日志文件写到log文件夹下
int fd = open(logName.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0666);
if(fd == -1) return;
write(fd, logTxt.c_str(), logTxt.size());
close(fd);
}
// 向多个文件中写
void PrintMultipleFile(LEVEL level, const std::string& logTxt)
{
// 新的文件名: level+LOG_FILE_NAME
std::string fileName = LevelToString(level) + LOG_FILE_NAME;
PrintOneFile(fileName, logTxt);
}
/*
重载(), 调用更方便一点, 可变参数不允许二次传参, 所以代码与前面的LoadMessage()重复
现在若想要打日志, 可以有两种方法
首先先定义对象 Log log
1. log.LoadMessage(level, const char *format, ...)
2. log(level, const char *format, ...)
*/
void operator()(LEVEL level, const char *format, ...)
{
// 默认部分, 时间
char leftBuffer[MAX_LOG_SIZE] = {0};
time_t t = time(nullptr);
struct tm *ctime = localtime(&(t));
snprintf(leftBuffer, sizeof(leftBuffer), "[%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, LevelToString(level).c_str());
// 自定义部分, 用户输入
char rightBuffer[MAX_LOG_SIZE] = {0};
// 因为定义了可变参数, 所以需要处理一下
va_list args;
va_start(args, format);
vsnprintf(rightBuffer, sizeof(rightBuffer), format, args);
va_end(args);
// 将两者加到一起
char logTxt[MAX_LOG_SIZE * 2] = {0};
snprintf(logTxt, sizeof(logTxt), "%s %s\n", leftBuffer, rightBuffer);
// cout << logTxt;
PrintLog(level, logTxt);
}
private:
PRINT_METHOD _printMethod;
std::string _path;
};
通信程序
// server.cc
#include "common.hpp"
#include "log.hpp"
int main()
{
Init init;
Log log(MULTIPLE_FILE);
// 打开信道
int fd = open(FIFO_FILE, O_RDONLY);
if(fd == -1) {
// perror("open");
log(FATAL, "open file error, error string: %s, error code: %d", strerror(errno), errno);
exit(FIFO_OPEN_ERR);
}
// cout << "server open file done" << endl;
// 测试日志功能
log(INFO, "server open file done");
log(FATAL, "server open file done");
log(WARNING, "server open file done");
log(DEBUG, "server open file done");
// 开始通信, 从管道中读数据
while(true) {
char buffer[N] = {0};
int x = read(fd, buffer, sizeof(buffer));
if(x > 0) {
buffer[x] = 0;
cout << "client say@ " << buffer << endl;
}
else if(x == 0) {
// cout << "client quit, me too!" << endl;
log(INFO, "client quit, me too!");
break;
}
else {
// perror("read");
log(FATAL, "read file error, error string: %s, error code: %d", strerror(errno), errno);
exit(FIFO_READ_ERR);
}
}
return 0;
}
// client.cc
#include "common.hpp"
int main()
{
// 打开信道
int fd = open(FIFO_FILE, O_WRONLY);
if(fd == -1) {
perror("open");
exit(FIFO_OPEN_ERR);
}
cout << "client open file done" << endl;
// 开始通信, 向管道中写数据
std::string line;
while(true) {
cout << "Please enter@ ";
getline(std::cin, line);
write(fd, line.c_str(), line.size());
}
return 0;
}
// common.hpp
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <cerrno>
#include <cstring>
#include <string>
#include <unistd.h>
#include <cstdlib>
#include <fcntl.h>
using std::cout;
using std::endl;
#define FIFO_FILE "./myfifo"
#define MODE 0666
const int N = 1024;
enum {
FIFO_CREAT_ERR = 1,
FIFO_DEL_ERR,
FIFO_OPEN_ERR,
FIFO_READ_ERR
};
// 用于处理server.cc部分代码的初始化和析构处理
struct Init
{
Init()
{
// 创建信道
int n = mkfifo(FIFO_FILE, MODE);
if (n == -1) {
perror("mkfifo");
exit(FIFO_CREAT_ERR);
}
}
~Init()
{
// 删除信道
int m = unlink(FIFO_FILE);
if (m == -1) {
perror("unlink");
exit(FIFO_DEL_ERR);
}
}
};
.PHONY:all
all : server client
server : server.cc
g++ -o $@ $^ -std=c++11
client : client.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -rf server client
运行效果