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