做網(wǎng)站的軟件著作權(quán)安卓手機(jī)優(yōu)化
日志是日后工作中非常重要的一部分,現(xiàn)在寫一份簡(jiǎn)單的日志項(xiàng)目可以幫助我們熟悉并理解原理。
目錄
- 設(shè)計(jì)思路:
- 一些實(shí)現(xiàn)細(xì)節(jié):
- 代碼:
- 日志的使用方法:
設(shè)計(jì)思路:
圖示是我們的最終目的。
- 設(shè)計(jì)一個(gè)類,這個(gè)類中存放著以上數(shù)據(jù)。
- 設(shè)計(jì)一個(gè)Log類,這個(gè)類中有一個(gè)Print函數(shù)進(jìn)行按照指定格式(文件或顯示器)輸出,我們向這個(gè)成員函數(shù)中傳參進(jìn)行打印。
一些實(shí)現(xiàn)細(xì)節(jié):
- 獲取時(shí)間:可以使用time函數(shù)獲取時(shí)間戳,由localtime進(jìn)行格式轉(zhuǎn)換為我們指定的樣式。
- printf的格式輸出:
printf("%02d", 10);
代表域?qū)挒?,數(shù)字在右邊,空余補(bǔ)0。 - 可變參數(shù)的處理:
有很多的方法:
比如自己逐個(gè)提取或者利用現(xiàn)有的接口提取。
這并不是重點(diǎn),會(huì)用即可。
可變參數(shù)的博客。
我們還是最好使用接口進(jìn)行提取,避免看很多麻煩的操作,那就是
與snprintf使用方法類似,可以直接將轉(zhuǎn)換后的直接寫入指定字符串中!
- 關(guān)于封裝:不同功能的函數(shù)可以在成員函數(shù)與非成員函數(shù)之間自由切換(根據(jù)對(duì)成員變量的需求…)
- 宏中的可變參數(shù):這個(gè)
__VA_ARGS__
鏈接包含了一些宏的用法。
代碼:
#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fstream>
#include <pthread.h>namespace log_ns
{#define SCREE_TYPE 1
#define FILE_TYPE 2const char *gfilename = "log.txt";pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;enum{DEBUG = 1,INFO,WARNNING,ERROR,FATAL};std::string LevelTostrint(int level){switch (level){case DEBUG:return "DEBUG";break;case INFO:return "INFO";break;case WARNNING:return "WARNING";break;case ERROR:return "ERROR";break;case FATAL:return "FATAL";break;default:return "Unknow";}}class LogMessage{public:std::string _level;pid_t _id;std::string _filaname;int _filenumber;std::string _curr_time;std::string _logmsg;};std::string GetCurTime(){time_t timestamp = time(nullptr);tm *ptm = localtime(×tamp);char buffer[128];snprintf(buffer, sizeof(buffer), "%d/%02d/%02d %02d:%02d:%02d",ptm->tm_year + 1900,ptm->tm_mon + 1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec);return buffer;}class Log{public:Log(int type = SCREE_TYPE, std::string FILEName = gfilename): _type(type), _FILEName(gfilename){}~Log(){}void Print(int level, const char *filename, int filenumber, const char *msg, ...){LogMessage lmsg;lmsg._level = LevelTostrint(level);lmsg._id = getpid();lmsg._filaname = filename;lmsg._filenumber = filenumber;lmsg._curr_time = GetCurTime();// 處理可變參數(shù)va_list ap;va_start(ap, msg);char buffer[1024];vsnprintf(buffer, sizeof(buffer), msg, ap);lmsg._logmsg = buffer;va_end(ap);// 打印到指定文件pthread_mutex_lock(&gmutex);Flush(lmsg);pthread_mutex_unlock(&gmutex);}void Enable(int type){_type = type;}void FlushScree(const LogMessage &logMsg){printf("[%s][%d][%s][%d][%s] %s",logMsg._level.c_str(),logMsg._id,logMsg._filaname.c_str(),logMsg._filenumber,logMsg._curr_time.c_str(),logMsg._logmsg.c_str());}void FlushFILE(const LogMessage &logMsg){std::ofstream out(_FILEName.c_str(), std::fstream::app | std::fstream::out);if (!out.is_open()){perror("open fail");return;}char buffer[2048];snprintf(buffer, sizeof(buffer), "[%s][%d][%s][%d][%s] %s",logMsg._level.c_str(),logMsg._id,logMsg._filaname.c_str(),logMsg._filenumber,logMsg._curr_time.c_str(),logMsg._logmsg.c_str());out << buffer;out.close();}void Flush(const LogMessage &logMsg){switch (_type){case SCREE_TYPE:FlushScree(logMsg);break;case FILE_TYPE:FlushFILE(logMsg);break;}}private:int _type;std::string _FILEName;};Log lg;
#define LOG(LEVEL, format, ...) \do \{ \lg.Print(LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__); \} while (0)#define EnableScree() \do \{ \lg.Enable(SCREE_TYPE); \} while (0)#define EnableFILE() \do \{ \lg.Enable(FILE_TYPE); \} while (0)
}
整體來說并不是很難,但是這里的一些知識(shí)點(diǎn)對(duì)于有些同學(xué)過于偏僻,導(dǎo)致了整個(gè)處理過程有點(diǎn)無措。
日志的使用方法:
我們定義了宏,就避免了一些繁瑣的步驟,比如創(chuàng)建一個(gè)對(duì)象再去調(diào)用。
另外,我們的宏也提供了改變輸出文件的,使用也更方便。
#include "Log.hpp"using namespace log_ns;int main()
{EnableScree();LOG(DEBUG, "hello world%d\n", 666);EnableFILE();LOG(DEBUG, "hello world%d\n", 666);LOG(DEBUG, "hello world%d\n", 666);LOG(DEBUG, "hello world%d\n", 666);return 0;
}
當(dāng)然,創(chuàng)建對(duì)象去調(diào)用Print也是可以的。
完~