diff --git a/core-tests/src/core/log.cpp b/core-tests/src/core/log.cpp index 40ef7ecaeab..bef9c9d4148 100644 --- a/core-tests/src/core/log.cpp +++ b/core-tests/src/core/log.cpp @@ -45,6 +45,22 @@ TEST(log, date_format) ASSERT_TRUE(n); } +TEST(log, date_format_long_string) +{ + swLog_reset(); + swLog_set_level(SW_LOG_ERROR); + swoole::String content(swString_new(256)); + auto str = content.get(); + + swString_repeat(str, "x", 1, 120); + swString_append_ptr(str, SW_STRL("day %d of %B in the year %Y. Time: %I:%S %p")); + + int retval = swLog_set_date_format(str->str); + + ASSERT_EQ(retval, SW_ERR); + ASSERT_EQ(SwooleG.error, SW_ERROR_INVALID_PARAMS); +} + TEST(log, date_with_microseconds) { swLog_reset(); @@ -60,3 +76,19 @@ TEST(log, date_with_microseconds) std::regex e("\\[\\S+\\s\\d{2}:\\d{2}:\\d{2}\\<\\.(\\d+)\\>\\s@\\d+\\.\\d+\\]\tWARNING\thello world"); ASSERT_TRUE(std::regex_search(content.value(), e)); } + +TEST(log, rotation) +{ + swLog_reset(); + swLog_set_rotation(SW_LOG_ROTATION_DAILY); + swLog_open(file); + + swLog_put(SW_LOG_WARNING, SW_STRL("hello world")); + + ASSERT_EQ(access(swLog_get_file(), R_OK), -1); + ASSERT_EQ(errno, ENOENT); + ASSERT_EQ(access(swLog_get_real_file(), R_OK), 0); + + swLog_close(); + unlink(swLog_get_real_file()); +} diff --git a/include/swoole.h b/include/swoole.h index 1c4defa088e..4d736042c93 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -431,6 +431,12 @@ enum swLog_level SW_LOG_ERROR, SW_LOG_NONE, }; + +enum swLog_rotation_type +{ + SW_LOG_ROTATION_SINGLE = 0, + SW_LOG_ROTATION_DAILY, +}; //------------------------------------------------------------------------------- enum swWorker_status { @@ -1366,7 +1372,10 @@ void swLog_reopen(enum swBool_type redirect); void swLog_close(void); void swLog_reset(); void swLog_set_level(int lv); -void swLog_set_date_format(const char *format); +int swLog_set_date_format(const char *format); +void swLog_set_rotation(int rotation); +const char* swLog_get_real_file(); +const char* swLog_get_file(); void swLog_set_date_with_microseconds(bool enable); //----------------------Tool Function--------------------- diff --git a/src/core/log.cc b/src/core/log.cc index e4a7cac6277..f3f21aa6910 100644 --- a/src/core/log.cc +++ b/src/core/log.cc @@ -20,13 +20,17 @@ #include #define SW_LOG_BUFFER_SIZE (SW_ERROR_MSG_SIZE+256) -#define SW_LOG_DATE_STRLEN 64 +#define SW_LOG_DATE_STRLEN 128 #define SW_LOG_DEFAULT_DATE_FORMAT "%F %T" static bool opened = false; static bool date_with_microseconds = false; static std::string date_format = SW_LOG_DEFAULT_DATE_FORMAT; static std::string log_file = ""; +static std::string log_real_file; +static int log_rotation = SW_LOG_ROTATION_SINGLE; + +static std::string swLog_gen_real_file(const std::string &file); int swLog_open(const char *_log_file) { @@ -35,19 +39,34 @@ int swLog_open(const char *_log_file) swLog_close(); } - SwooleG.log_fd = open(_log_file, O_APPEND | O_RDWR | O_CREAT, 0666); + log_file = _log_file; + + if (log_rotation) + { + log_real_file = swLog_gen_real_file(log_file); + } + else + { + log_real_file = log_file; + } + + SwooleG.log_fd = open(log_real_file.c_str(), O_APPEND | O_RDWR | O_CREAT, 0666); if (SwooleG.log_fd < 0) { - printf("open(%s) failed. Error: %s[%d]\n", _log_file, strerror(errno), errno); + printf("open(%s) failed. Error: %s[%d]\n", log_real_file.c_str(), strerror(errno), errno); SwooleG.log_fd = STDOUT_FILENO; opened = false; + log_file = ""; + log_real_file = ""; + return SW_ERR; } + else + { + opened = true; - opened = true; - log_file = _log_file; - - return SW_OK; + return SW_OK; + } } void swLog_close(void) @@ -74,16 +93,40 @@ void swLog_set_level(int level) SwooleG.log_level = level; } +void swLog_set_rotation(int _rotation) +{ + log_rotation = _rotation == 0 ? SW_LOG_ROTATION_SINGLE : SW_LOG_ROTATION_DAILY; +} + void swLog_reset() { date_format = SW_LOG_DEFAULT_DATE_FORMAT; date_with_microseconds = false; + log_rotation = SW_LOG_ROTATION_SINGLE; SwooleG.log_level = SW_LOG_INFO; } -void swLog_set_date_format(const char *format) +int swLog_set_date_format(const char *format) { - date_format = format; + char date_str[SW_LOG_DATE_STRLEN]; + time_t now_sec; + + now_sec = time(NULL); + size_t l_data_str = std::strftime(date_str, sizeof(date_str), format, std::localtime(&now_sec)); + + if (l_data_str == 0) + { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, "The date format string[length=%ld] is too long", + strlen(format)); + + return SW_ERR; + } + else + { + date_format = format; + + return SW_OK; + } } void swLog_set_date_with_microseconds(bool enable) @@ -100,6 +143,7 @@ void swLog_reopen(enum swBool_type redirect) { return; } + std::string new_log_file(log_file); swLog_close(); swLog_open(new_log_file.c_str()); @@ -112,6 +156,26 @@ void swLog_reopen(enum swBool_type redirect) } } +const char* swLog_get_real_file() +{ + return log_real_file.c_str(); +} + +const char* swLog_get_file() +{ + return log_file.c_str(); +} + +static std::string swLog_gen_real_file(const std::string &file) +{ + char date_str[16]; + auto now_sec = time(NULL); + size_t l_data_str = std::strftime(date_str, sizeof(date_str), "%Y%m%d", std::localtime(&now_sec)); + std::string real_file = file + "." + std::string(date_str, l_data_str); + + return real_file; +} + void swLog_put(int level, const char *content, size_t length) { const char *level_str; @@ -141,7 +205,7 @@ void swLog_put(int level, const char *content, size_t length) case SW_LOG_ERROR: level_str = "ERROR"; break; - // case SW_LOG_INFO: + case SW_LOG_INFO: default: level_str = "INFO"; break; @@ -151,6 +215,15 @@ void swLog_put(int level, const char *content, size_t length) auto now_sec = std::chrono::system_clock::to_time_t(now); size_t l_data_str = std::strftime(date_str, sizeof(date_str), date_format.c_str(), std::localtime(&now_sec)); + if (log_rotation) + { + std::string tmp = swLog_gen_real_file(log_file); + if (tmp != log_real_file) + { + swLog_reopen(SW_FALSE); + } + } + if (date_with_microseconds) { auto now_us = std::chrono::duration_cast(now.time_since_epoch()).count(); diff --git a/swoole.cc b/swoole.cc index 01bc4ed342f..f8affc7c2fd 100644 --- a/swoole.cc +++ b/swoole.cc @@ -245,6 +245,10 @@ void php_swoole_set_global_option(HashTable *vht) { swLog_set_date_with_microseconds(zval_is_true(ztmp)); } + if (php_swoole_array_get_value(vht, "log_rotation", ztmp)) + { + swLog_set_rotation(zval_get_long(ztmp)); + } if (php_swoole_array_get_value(vht, "display_errors", ztmp)) { SWOOLE_G(display_errors) = zval_is_true(ztmp); @@ -540,6 +544,9 @@ PHP_MINIT_FUNCTION(swoole) SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_ERROR", SW_LOG_ERROR); SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_NONE", SW_LOG_NONE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_ROTATION_SINGLE", SW_LOG_ROTATION_SINGLE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_ROTATION_DAILY", SW_LOG_ROTATION_DAILY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_NONE", SW_IPC_NONE); SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_UNIXSOCK", SW_IPC_UNIXSOCK); SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_SOCKET", SW_IPC_SOCKET);