文章目錄
一. 前言
二. 基本功能
三. 代碼實現(xiàn)
1. fdoglogger.h
2. fdoglogger.cpp
四. 測試用例
1. fdoglogger_test.cpp
哈嘍,自從實習(xí)以來很久沒有更文了,一是沒有時間,二是實習(xí)了之后突然發(fā)現(xiàn)自己能寫的東西也沒有多少了。趕上1024有征文活動,就寫一篇吧,在實習(xí)的這段時間,我更加認識到日志的重要性,客戶端值沒傳過來?看日志,服務(wù)崩潰了?看日志,沒錯,日志是出現(xiàn)異常第一個想到的東西,它記錄了程序運行過程中所調(diào)用的函數(shù),所接受到的值,所執(zhí)行的行為等等。大家也都看到這篇的標題了,我這個人有一個缺點,就是不太喜歡用別人的東西,如果有能力,我希望自己造,所以今天我們自己來動手擼一個日志庫,文章重點講實現(xiàn)過程,如果需要源碼,可以前往github獲取FdogLog,一個輕量級C++日志庫,用于日志服務(wù)。
跪求三連!
我們先來捋一捋這個日志庫應(yīng)該實現(xiàn)那些功能。
日志最最最基本的功能是什么,當(dāng)然是打印或記錄日志。
信息應(yīng)該包括哪些信息,時間?運行用戶?所在文件?想要顯示的信息?(自定義顯示信息下篇實現(xiàn))
信息雖然顯示豐富,但是要盡可能讓代碼自己獲取其他信息,調(diào)用者只需要設(shè)置最主要的信息。
信息有重要等級之分,所以我們需要對信息做必要分類,提高效率。
如何實現(xiàn)全局盡可能簡潔的調(diào)用。
如果日志庫是運行在多線程環(huán)境,如何保證線程安全。(下篇實現(xiàn))
這些就是一個日志庫所具備的最基本的功能,接下來繼續(xù)思考,還需要什么。
怎么控制日志的行為。
如果保存在文件,如何定義文件名。
隨著日志增加,文件會越來越大,如何解決。(下篇實現(xiàn))
簡單規(guī)劃完一個不那么完美的日志庫所具備的能力,現(xiàn)在我們來對這幾條做更詳細的規(guī)劃。
日志最最最基本的功能是什么,當(dāng)然是打印或記錄日志。
信息應(yīng)該包括哪些信息,時間?運行用戶?所在文件?想要顯示的信息?
當(dāng)我在調(diào)用一個名為function的函數(shù)時。
function();
1
你希望它輸出怎么樣的信息。
我被調(diào)用
[2021-10-20 23:27:23] 我被調(diào)用
[2021-10-20 23:27:23] INFO 我被調(diào)用
[2021-10-20 23:27:23] INFO root 我被調(diào)用
[2021-10-20 23:27:23] INFO root 17938 我被調(diào)用
[2021-10-20 23:27:23] INFO root 17938 [/media/rcl/FdogIM/service.h function:8] 我被調(diào)用
我想大部分人都會選擇最后一種輸出信息吧(雖然在這之前,我們都大量使用cout輸出第一種),所以我們的日志應(yīng)該包括時間,日志等級,運行用戶,進程ID,調(diào)用函數(shù)所在文件,以及調(diào)用時所在行數(shù)。當(dāng)然總會有人不想全部輸出,這將在后面給出方案。
信息雖然顯示豐富,但是要盡可能讓代碼自己獲取其他信息,調(diào)用者只需要設(shè)置最主要的信息。
信息有重要等級之分,所以我們需要對信息做必要分類,提高效率。
如何實現(xiàn)全局盡可能簡潔的調(diào)用.
信息有重要等級之分,要可以對信息做區(qū)分,按照常見的等級之分,有:
ERROR:?此信息輸出后,主體系統(tǒng)核心模塊不能正常工作,需要修復(fù)才能正常工作。
WARN:?? 此信息輸出后,系統(tǒng)一般模塊存在問題,不影響系統(tǒng)運行。
INFO:?????此信息輸出后,主要是記錄系統(tǒng)運行狀態(tài)等關(guān)聯(lián)信息。
DEBUG:?最細粒度的輸出,除去上面各種情況后,你希望輸出的相關(guān)信息,都可以在這里輸出。
TRACE:??最細粒度的輸出,除去上面各種情況后,你希望輸出的相關(guān)信息,都可以在這里輸出。
有了等級之分,如何實現(xiàn)全局盡可能簡潔的調(diào)用,通俗的說就是去掉一切不必要的調(diào)用,只留下最主要的調(diào)用。
例如:
#include #include"fdoglogger.h" //添加日志庫頭文件 using namespace fdog; //日志庫的命名空間 int main(){ FdogError("錯誤"); FdogWarn("警告"); FdogInfo("信息"); FdogDebug("調(diào)試"); FdogTrace("追蹤"); return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
你不必初始化什么信息,調(diào)用什么多余的初始化函數(shù),只需要用這五個類似函數(shù)的東西來輸出即可,同樣,如果是另一個源文件,依舊是這樣的調(diào)用方式(這里可以使用單一模式來實現(xiàn),其意圖是保證一個類僅有一個實列,并提供一個訪問它的全局訪問點,該實例被所有程序模塊共享。就比如日志的輸出。)。
如果日志庫是運行在多線程環(huán)境,如何保證線程安全。
到目前,一個基本的日志庫的調(diào)用基本成形,如果在單線程,它可以很好的工作,但是到了多線程環(huán)境下,就不能保證了,第一點就是單例模式的創(chuàng)建,當(dāng)兩個線程同時去初始化時,無法保證單一實例被成功創(chuàng)建,第二,日志既然是輸出到文件,不同線程寫入文件時,如何保證寫入數(shù)據(jù)不會錯亂。既然寫的是C++的日志輸出,必然用到了cout ,cout 不是原子性的操作,所以在多線程下是不安全的,這些都是我們需要考慮到的。
怎么控制日志的行為。
這里使用配置文件進行日志的行為規(guī)定,包括打印什么日志,輸入到文件,還是終端,輸出的等級,以及日志開關(guān),等等,配置文件將在程序啟動時被讀取。(提醒各位千萬不要寫死代碼,后患無窮!!!)
如果保存在文件,如何定義文件名。
隨著日志增加,文件會越來越大,如何解決。
日志的文件名由配置文件指定,但是創(chuàng)建時會在后面加上創(chuàng)建日期后綴,并且可以在配置文件中配置每隔多少天創(chuàng)建一個新的日志文件,如果配置中心有設(shè)置日志文件大小,則會優(yōu)先大小判斷,超過便創(chuàng)建一個新文件。
#ifndef FDOGLOGGER_H #define FDOGLOGGER_H #include #include #include
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include"fdoglogger.h" using namespace fdog; FdogLogger * FdogLogger::singleObject = nullptr; mutex * FdogLogger::mutex_new = new(mutex); FdogLogger::FdogLogger(){ initLogConfig(); } FdogLogger::~FdogLogger(){ } FdogLogger* FdogLogger::getInstance(){ mutex_new->lock(); if (singleObject == nullptr) { singleObject = new FdogLogger(); } mutex_new->unlock(); return singleObject; } void FdogLogger::initLogConfig(){ map flogConfInfo; flogConfInfo["logSwitch"] = &this->logger.logSwitch; flogConfInfo["logFileSwitch"] = &this->logger.logFileSwitch; flogConfInfo["logTerminalSwitch"] = &this->logger.logTerminalSwitch; flogConfInfo["logName"] = &this->logger.logName; flogConfInfo["logFilePath"] = &this->logger.logFilePath; flogConfInfo["logMixSize"] = &this->logger.logMixSize; flogConfInfo["logBehavior"] = &this->logger.logBehavior; flogConfInfo["logOverlay"] = &this->logger.logOverlay; flogConfInfo["logOutputLevelFile"] = &this->logger.logOutputLevelFile; flogConfInfo["logOutputLevelTerminal"] = &this->logger.logOutputLevelTerminal; string str; ifstream file; char str_c[100]={0}; file.open("fdoglogconf.conf"); if(!file.is_open()){ cout<<"文件打開失敗\n"; } while(getline(file, str)){ if(!str.length()) { continue; } string str_copy = str; //cout<<"獲取數(shù)據(jù):"<second = str_c; } else { } } } logger.logName = logger.logName + getLogNameTime() + ".log"; bindFileCoutMap("5", fileType::Error); bindFileCoutMap("4", fileType::Warn); bindFileCoutMap("3", fileType::Info); bindFileCoutMap("2", fileType::Debug); bindFileCoutMap("1", fileType::Trace); bindTerminalCoutMap("5", terminalType::Error); bindTerminalCoutMap("4", terminalType::Warn); bindTerminalCoutMap("3", terminalType::Info); bindTerminalCoutMap("2", terminalType::Debug); bindTerminalCoutMap("1", terminalType::Trace); if(logger.logFileSwitch == "on"){ if(!createFile(logger.logFilePath)){ std::cout<<"Log work path creation failed\n"; } } cout << "|========FdogLogger v2.0==========================|" <coutTypeMap[coutType]; } bool FdogLogger::getFileType(fileType fileCoutBool){ return singleObject->fileCoutMap[fileCoutBool]; } bool FdogLogger::getTerminalType(terminalType terminalCoutTyle){ return singleObject->terminalCoutMap[terminalCoutTyle]; } string FdogLogger::getLogCoutTime(){ time_t timep; time (&timep); char tmp[64]; strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S",localtime(&timep)); string tmp_str = tmp; return SQUARE_BRACKETS_LEFT + tmp_str + SQUARE_BRACKETS_RIGHT; } string FdogLogger::getLogNameTime(){ time_t timep; time (&timep); char tmp[64]; strftime(tmp, sizeof(tmp), "%Y-%m-%d-%H:%M:%S",localtime(&timep)); return tmp; } string FdogLogger::getFilePash(){ getcwd(szbuf, sizeof(szbuf)-1); string szbuf_str = szbuf; return szbuf_str + SLASH; } string FdogLogger::getLogCoutProcessId(){ #ifndef linux return to_string(getpid()); #endif #ifndef WIN32 // unsigned long GetPid(){ // return GetCurrentProcessId(); // } #endif } string FdogLogger::getLogCoutThreadId(){ #ifndef linux return to_string(syscall(__NR_gettid)); #endif #ifndef WIN32 // unsigned long GetTid(){ // return GetCurrentThreadId(); // } #endif } string FdogLogger::getLogCoutUserName(){ struct passwd *my_info; my_info = getpwuid(getuid()); string name = my_info->pw_name; return SPACE + name + SPACE; } bool FdogLogger::createFile(string filePash){ int len = filePash.length(); if(!len){ filePash = "log"; if (0 != access(filePash.c_str(), 0)){ if(-1 == mkdir(filePash.c_str(),0)){ std::cout<<"沒路徑"; return 0; } } } std::string filePash_cy(len,'\0'); for(int i =0;i1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#include #include"fdoglogger.h" //添加日志庫頭文件 using namespace fdog; //日志庫的命名空間 int main(){ FdogError("錯誤"); FdogWarn("警告"); FdogInfo("信息"); FdogDebug("調(diào)試"); FdogTrace("追蹤"); return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
暫時考慮到的就是這些,如有缺陷,歡迎評論區(qū)補充。(比如文件寫入打開就關(guān)閉,很浪費資源,如何優(yōu)化,下篇見)。
源碼已上傳github,還原star! FdogLog,一個輕量級C++日志庫,用于日志服務(wù)。
C++ 任務(wù)調(diào)度
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。