1 /*
2 +----------------------------------------------------------------------+
3 | Swoole |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 2.0 of the Apache license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.apache.org/licenses/LICENSE-2.0.html |
9 | If you did not receive a copy of the Apache2.0 license and are unable|
10 | to obtain it through the world-wide-web, please send a note to |
11 | license@swoole.com so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Tianfeng Han <mikan.tenny@gmail.com> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "swoole.h"
18
19 #include <string.h>
20 #include <fcntl.h>
21 #include <sys/file.h>
22
23 #include <string>
24 #include <chrono> // NOLINT [build/c++11]
25
26 namespace swoole {
27
get_pretty_name(const std::string & pretty_function,bool strip)28 std::string Logger::get_pretty_name(const std::string &pretty_function, bool strip) {
29 size_t brackets = pretty_function.find_first_of("(");
30 if (brackets == pretty_function.npos) {
31 return "";
32 }
33
34 size_t begin = pretty_function.substr(0, brackets).rfind(" ") + 1;
35 size_t end = brackets - begin;
36 if (!strip) {
37 return pretty_function.substr(begin, end);
38 }
39
40 auto method_name = pretty_function.substr(begin, end);
41 size_t count = 0, index = method_name.length();
42 while (true) {
43 index = method_name.rfind("::", index);
44 if (index == method_name.npos) {
45 if (count == 1) {
46 return method_name.substr(method_name.rfind("::") + 2);
47 }
48 break;
49 }
50 count++;
51 if (count == 2) {
52 return method_name.substr(index + 2);
53 }
54 index -= 2;
55 }
56
57 return method_name;
58 }
59
open(const char * _log_file)60 bool Logger::open(const char *_log_file) {
61 if (opened) {
62 close();
63 }
64
65 log_file = _log_file;
66
67 if (log_rotation) {
68 log_real_file = gen_real_file(log_file);
69 } else {
70 log_real_file = log_file;
71 }
72
73 log_fd = ::open(log_real_file.c_str(), O_APPEND | O_RDWR | O_CREAT, 0666);
74 if (log_fd < 0) {
75 printf("open(%s) failed. Error: %s[%d]\n", log_real_file.c_str(), strerror(errno), errno);
76 log_fd = STDOUT_FILENO;
77 opened = false;
78 log_file = "";
79 log_real_file = "";
80
81 return false;
82 } else {
83 opened = true;
84
85 return true;
86 }
87 }
88
close(void)89 void Logger::close(void) {
90 if (opened) {
91 ::close(log_fd);
92 log_fd = STDOUT_FILENO;
93 log_file = "";
94 opened = false;
95 }
96 }
97
get_level()98 int Logger::get_level() {
99 return log_level;
100 }
101
set_level(int level)102 void Logger::set_level(int level) {
103 if (level < SW_LOG_DEBUG) {
104 level = SW_LOG_DEBUG;
105 }
106 if (level > SW_LOG_NONE) {
107 level = SW_LOG_NONE;
108 }
109 log_level = level;
110 }
111
set_rotation(int _rotation)112 void Logger::set_rotation(int _rotation) {
113 log_rotation = _rotation;
114 }
115
redirect_stdout_and_stderr(int enable)116 bool Logger::redirect_stdout_and_stderr(int enable) {
117 if (enable) {
118 if (!opened) {
119 swoole_warning("no log file opened");
120 return false;
121 }
122 if (redirected) {
123 swoole_warning("has been redirected");
124 return false;
125 }
126 if ((stdout_fd = dup(STDOUT_FILENO)) < 0) {
127 swoole_sys_warning("dup(STDOUT_FILENO) failed");
128 return false;
129 }
130 if ((stderr_fd = dup(STDERR_FILENO)) < 0) {
131 swoole_sys_warning("dup(STDERR_FILENO) failed");
132 return false;
133 }
134 swoole_redirect_stdout(log_fd);
135 redirected = true;
136 } else {
137 if (!redirected) {
138 swoole_warning("no redirected");
139 return false;
140 }
141 if (dup2(stdout_fd, STDOUT_FILENO) < 0) {
142 swoole_sys_warning("dup2(STDOUT_FILENO) failed");
143 }
144 if (dup2(stderr_fd, STDERR_FILENO) < 0) {
145 swoole_sys_warning("dup2(STDERR_FILENO) failed");
146 }
147 ::close(stdout_fd);
148 ::close(stderr_fd);
149 stdout_fd = -1;
150 stderr_fd = -1;
151 redirected = false;
152 }
153
154 return true;
155 }
156
reset()157 void Logger::reset() {
158 date_format = SW_LOG_DEFAULT_DATE_FORMAT;
159 date_with_microseconds = false;
160 log_rotation = SW_LOG_ROTATION_SINGLE;
161 log_level = SW_LOG_INFO;
162 }
163
set_date_format(const char * format)164 bool Logger::set_date_format(const char *format) {
165 char date_str[SW_LOG_DATE_STRLEN];
166 time_t now_sec;
167
168 now_sec = ::time(nullptr);
169 size_t l_data_str = std::strftime(date_str, sizeof(date_str), format, std::localtime(&now_sec));
170
171 if (l_data_str == 0) {
172 swoole_set_last_error(SW_ERROR_INVALID_PARAMS);
173 swoole_error_log(
174 SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, "The date format string[length=%ld] is too long", strlen(format));
175
176 return false;
177 } else {
178 date_format = format;
179
180 return true;
181 }
182 }
183
set_date_with_microseconds(bool enable)184 void Logger::set_date_with_microseconds(bool enable) {
185 date_with_microseconds = enable;
186 }
187
188 /**
189 * reopen log file
190 */
reopen()191 void Logger::reopen() {
192 if (!opened) {
193 return;
194 }
195
196 std::string new_log_file(log_file);
197 close();
198 open(new_log_file.c_str());
199 /**
200 * redirect STDOUT & STDERR to log file
201 */
202 if (redirected) {
203 swoole_redirect_stdout(log_fd);
204 }
205 }
206
get_real_file()207 const char *Logger::get_real_file() {
208 return log_real_file.c_str();
209 }
210
get_file()211 const char *Logger::get_file() {
212 return log_file.c_str();
213 }
214
gen_real_file(const std::string & file)215 std::string Logger::gen_real_file(const std::string &file) {
216 char date_str[16];
217 auto now_sec = ::time(nullptr);
218 const char *fmt;
219
220 switch (log_rotation) {
221 case SW_LOG_ROTATION_MONTHLY:
222 fmt = "%Y%m";
223 break;
224 case SW_LOG_ROTATION_HOURLY:
225 fmt = "%Y%m%d%H";
226 break;
227 case SW_LOG_ROTATION_EVERY_MINUTE:
228 fmt = "%Y%m%d%H%M";
229 break;
230 case SW_LOG_ROTATION_DAILY:
231 default:
232 fmt = "%Y%m%d";
233 break;
234 }
235
236 size_t l_data_str = std::strftime(date_str, sizeof(date_str), fmt, std::localtime(&now_sec));
237 std::string real_file = file + "." + std::string(date_str, l_data_str);
238
239 return real_file;
240 }
241
is_opened()242 bool Logger::is_opened() {
243 return opened;
244 }
245
put(int level,const char * content,size_t length)246 void Logger::put(int level, const char *content, size_t length) {
247 const char *level_str;
248 char date_str[SW_LOG_DATE_STRLEN];
249 char log_str[SW_LOG_BUFFER_SIZE];
250 int n;
251
252 if (level < log_level) {
253 return;
254 }
255
256 switch (level) {
257 case SW_LOG_DEBUG:
258 level_str = "DEBUG";
259 break;
260 case SW_LOG_TRACE:
261 level_str = "TRACE";
262 break;
263 case SW_LOG_NOTICE:
264 level_str = "NOTICE";
265 break;
266 case SW_LOG_WARNING:
267 level_str = "WARNING";
268 break;
269 case SW_LOG_ERROR:
270 level_str = "ERROR";
271 break;
272 case SW_LOG_INFO:
273 default:
274 level_str = "INFO";
275 break;
276 }
277
278 auto now = std::chrono::system_clock::now();
279 auto now_sec = std::chrono::system_clock::to_time_t(now);
280 size_t l_data_str = std::strftime(date_str, sizeof(date_str), date_format.c_str(), std::localtime(&now_sec));
281
282 if (log_rotation) {
283 std::string tmp = gen_real_file(log_file);
284 if (tmp != log_real_file) {
285 reopen();
286 }
287 }
288
289 if (date_with_microseconds) {
290 auto now_us = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
291 l_data_str += sw_snprintf(
292 date_str + l_data_str, SW_LOG_DATE_STRLEN - l_data_str, "<.%lld>", (long long) now_us - now_sec * 1000000);
293 }
294
295 char process_flag = '@';
296 int process_id = 0;
297
298 switch (swoole_get_process_type()) {
299 case SW_PROCESS_MASTER:
300 process_flag = '#';
301 process_id = swoole_get_thread_id();
302 break;
303 case SW_PROCESS_MANAGER:
304 process_flag = '$';
305 break;
306 case SW_PROCESS_WORKER:
307 process_flag = '*';
308 process_id = swoole_get_process_id();
309 break;
310 case SW_PROCESS_TASKWORKER:
311 process_flag = '^';
312 process_id = swoole_get_process_id();
313 break;
314 default:
315 break;
316 }
317
318 n = sw_snprintf(log_str,
319 SW_LOG_BUFFER_SIZE,
320 "[%.*s %c%d.%d]\t%s\t%.*s\n",
321 static_cast<int>(l_data_str),
322 date_str,
323 process_flag,
324 SwooleG.pid,
325 process_id,
326 level_str,
327 static_cast<int>(length),
328 content);
329
330 if (opened && flock(log_fd, LOCK_EX) == -1) {
331 printf("flock(%d, LOCK_EX) failed. Error: %s[%d]\n", log_fd, strerror(errno), errno);
332 goto _print;
333 }
334 if (write(log_fd, log_str, n) < 0) {
335 _print:
336 printf("write(log_fd=%d, size=%d) failed. Error: %s[%d].\nMessage: %.*s\n",
337 log_fd,
338 n,
339 strerror(errno),
340 errno,
341 n,
342 log_str);
343 }
344 if (opened && flock(log_fd, LOCK_UN) == -1) {
345 printf("flock(%d, LOCK_UN) failed. Error: %s[%d]\n", log_fd, strerror(errno), errno);
346 }
347 if (display_backtrace_) {
348 swoole_print_backtrace();
349 }
350 }
351 } // namespace swoole
352