-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlog.cpp
More file actions
190 lines (167 loc) · 5.5 KB
/
Copy pathlog.cpp
File metadata and controls
190 lines (167 loc) · 5.5 KB
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include "log.h"
// 构造函数
Log::Log() {
fp_ = nullptr;
deque_ = nullptr;
writeThread_ = nullptr;
lineCount_ = 0;
toDay_ = 0;
isAsync_ = false;
}
Log::~Log() {
while(!deque_->empty()) {
deque_->flush(); // 唤醒消费者,处理掉剩下的任务
}
deque_->Close(); // 关闭队列
writeThread_->join(); // 等待当前线程完成手中的任务
if(fp_) { // 冲洗文件缓冲区,关闭文件描述符
lock_guard<mutex> locker(mtx_);
flush(); // 清空缓冲区中的数据
fclose(fp_); // 关闭日志文件
}
}
// 唤醒阻塞队列消费者,开始写日志
void Log::flush() {
if(isAsync_) { // 只有异步日志才会用到deque
deque_->flush();
}
fflush(fp_); // 清空输入缓冲区
}
// 懒汉模式 局部静态变量法(这种方法不需要加锁和解锁操作)
Log* Log::Instance() {
static Log log;
return &log;
}
// 异步日志的写线程函数
void Log::FlushLogThread() {
Log::Instance()->AsyncWrite_();
}
// 写线程真正的执行函数
void Log::AsyncWrite_() {
string str = "";
while(deque_->pop(str)) {
lock_guard<mutex> locker(mtx_);
fputs(str.c_str(), fp_);
}
}
// 初始化日志实例
void Log::init(int level, const char* path, const char* suffix, int maxQueCapacity) {
isOpen_ = true;
level_ = level;
path_ = path;
suffix_ = suffix;
if(maxQueCapacity) { // 异步方式
isAsync_ = true;
if(!deque_) { // 为空则创建一个
unique_ptr<BlockQueue<std::string>> newQue(new BlockQueue<std::string>);
// 因为unique_ptr不支持普通的拷贝或赋值操作,所以采用move
// 将动态申请的内存权给deque,newDeque被释放
deque_ = move(newQue); // 左值变右值,掏空newDeque
unique_ptr<thread> newThread(new thread(FlushLogThread));
writeThread_ = move(newThread);
}
} else {
isAsync_ = false;
}
lineCount_ = 0;
time_t timer = time(nullptr);
struct tm* systime = localtime(&timer);
char fileName[LOG_NAME_LEN] = {0};
snprintf(fileName, LOG_NAME_LEN - 1, "%s/%04d_%02d_%02d%s",
path_, systime->tm_year + 1900, systime->tm_mon + 1, systime->tm_mday, suffix_);
toDay_ = systime->tm_mday;
{
lock_guard<mutex> locker(mtx_);
buff_.RetrieveAll();
if(fp_) { // 重新打开
flush();
fclose(fp_);
}
fp_ = fopen(fileName, "a"); // 打开文件读取并附加写入
if(fp_ == nullptr) {
mkdir(fileName, 0777);
fp_ = fopen(fileName, "a"); // 生成目录文件(最大权限)
}
assert(fp_ != nullptr);
}
}
void Log::write(int level, const char *format, ...) {
struct timeval now = {0, 0};
gettimeofday(&now, nullptr);
time_t tSec = now.tv_sec;
struct tm *sysTime = localtime(&tSec);
struct tm t = *sysTime;
va_list vaList;
// 日志日期 日志行数 如果不是今天或行数超了
if (toDay_ != t.tm_mday || (lineCount_ && (lineCount_ % MAX_LINES == 0)))
{
unique_lock<mutex> locker(mtx_);
locker.unlock();
char newFile[LOG_NAME_LEN];
char tail[36] = {0};
snprintf(tail, 36, "%04d_%02d_%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
if (toDay_ != t.tm_mday) // 时间不匹配,则替换为最新的日志文件名
{
snprintf(newFile, LOG_NAME_LEN - 72, "%s/%s%s", path_, tail, suffix_);
toDay_ = t.tm_mday;
lineCount_ = 0;
}
else {
snprintf(newFile, LOG_NAME_LEN - 72, "%s/%s-%d%s", path_, tail, (lineCount_ / MAX_LINES), suffix_);
}
locker.lock();
flush();
fclose(fp_);
fp_ = fopen(newFile, "a");
assert(fp_ != nullptr);
}
// 在buffer内生成一条对应的日志信息
{
unique_lock<mutex> locker(mtx_);
lineCount_++;
int n = snprintf(buff_.BeginWrite(), 128, "%d-%02d-%02d %02d:%02d:%02d.%06ld ",
t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
t.tm_hour, t.tm_min, t.tm_sec, now.tv_usec);
buff_.HasWritten(n);
AppendLogLevelTitle_(level);
va_start(vaList, format);
int m = vsnprintf(buff_.BeginWrite(), buff_.WritableBytes(), format, vaList);
va_end(vaList);
buff_.HasWritten(m);
buff_.Append("\n\0", 2);
if(isAsync_ && deque_ && !deque_->full()) { // 异步方式(加入阻塞队列中,等待写线程读取日志信息)
deque_->push_back(buff_.RetrieveAllToStr());
} else { // 同步方式(直接向文件中写入日志信息)
fputs(buff_.Peek(), fp_); // 同步就直接写入文件
}
buff_.RetrieveAll(); // 清空buff
}
}
// 添加日志等级
void Log::AppendLogLevelTitle_(int level) {
switch(level) {
case 0:
buff_.Append("[debug]: ", 9);
break;
case 1:
buff_.Append("[info] : ", 9);
break;
case 2:
buff_.Append("[warn] : ", 9);
break;
case 3:
buff_.Append("[error]: ", 9);
break;
default:
buff_.Append("[info] : ", 9);
break;
}
}
int Log::GetLevel() {
lock_guard<mutex> locker(mtx_);
return level_;
}
void Log::SetLevel(int level) {
lock_guard<mutex> locker(mtx_);
level_ = level;
}