Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(log-rorate): log-rotate plugin support max_size #7749

Merged
merged 11 commits into from
Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion apisix/plugins/log-rotate.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ local ngx = ngx
local ngx_time = ngx.time
local ngx_update_time = ngx.update_time
local lfs = require("lfs")
local assert = assert
local type = type
local io_open = io.open
local os_date = os.date
Expand Down Expand Up @@ -219,18 +220,29 @@ local function init_default_logs(logs_info, log_type)
end


local function file_size (file)
local current = file:seek() -- get current position
local size = file:seek("end") -- get file size
file:seek("set", current) -- restore position
return size
end


local function rotate()
local interval = INTERVAL
local max_kept = MAX_KEPT
local max_size = -1
qihaiyan marked this conversation as resolved.
Show resolved Hide resolved
local attr = plugin.plugin_attr(plugin_name)
if attr then
interval = attr.interval or interval
max_kept = attr.max_kept or max_kept
max_size = attr.max_size or max_size
enable_compression = attr.enable_compression or enable_compression
end

core.log.info("rotate interval:", interval)
core.log.info("rotate max keep:", max_kept)
core.log.info("rotate max size:", max_size)

if not default_logs then
-- first init default log filepath and filename
Expand All @@ -251,7 +263,18 @@ local function rotate()
if now_time < rotate_time then
-- did not reach the rotate time
core.log.info("rotate time: ", rotate_time, " now time: ", now_time)
return

local log_file = assert(io_open(default_logs[DEFAULT_ACCESS_LOG_FILENAME].file, "r"))
qihaiyan marked this conversation as resolved.
Show resolved Hide resolved
local access_log_file_size = file_size(log_file)
log_file:close()

log_file = assert(io_open(default_logs[DEFAULT_ERROR_LOG_FILENAME].file, "r"))
local error_log_file_size = file_size(log_file)
log_file:close()

if max_size <= 0 or access_log_file_size < max_size and error_log_file_size < max_size then
qihaiyan marked this conversation as resolved.
Show resolved Hide resolved
return
end
end

local now_date = os_date("%Y-%m-%d_%H-%M-%S", now_time)
Expand Down
1 change: 1 addition & 0 deletions conf/config-default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ plugin_attr:
log-rotate:
interval: 3600 # rotate interval (unit: second)
max_kept: 168 # max number of log files will be kept
max_size: -1 # max size bytes of log files to be rotated, size check would be skipped with a value less than 0
enable_compression: false # enable log file compression(gzip) or not, default false
skywalking:
service_name: APISIX
Expand Down
8 changes: 5 additions & 3 deletions docs/en/latest/plugins/log-rotate.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ You can configure how often the logs are rotated and how many logs to keep. When
|--------------------|---------|----------|---------|------------------------------------------------------------------------------------------------|
| interval | integer | True | 60 * 60 | Time in seconds specifying how often to rotate the logs. |
| max_kept | integer | True | 24 * 7 | Maximum number of historical logs to keep. If this number is exceeded, older logs are deleted. |
| max_size | integer | False | -1 | Max size(Bytes) of log files to be rotated, size check would be skipped with a value less than 0. |
qihaiyan marked this conversation as resolved.
Show resolved Hide resolved
| enable_compression | boolean | False | false | When set to `true`, compresses the log file (gzip). Requires `tar` to be installed. |

## Enabling the Plugin
Expand All @@ -51,9 +52,10 @@ plugins:

plugin_attr:
log-rotate:
interval: 3600
max_kept: 168
enable_compression: false
interval: 3600 # rotate interval (unit: second)
max_kept: 168 # max number of log files will be kept
max_size: -1 # max size of log files will be kept
enable_compression: false # enable log file compression(gzip) or not, default false
```

## Example usage
Expand Down
2 changes: 2 additions & 0 deletions docs/zh/latest/plugins/log-rotate.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ title: log-rotate
| -------- | ------- | ------ | ------- | ------ | ------------------------------------------------------ |
| interval | integer | 必须 | 60 * 60 | | 每间隔多长时间切分一次日志,秒为单位 |
| max_kept | integer | 必须 | 24 * 7 | | 最多保留多少份历史日志,超过指定数量后,自动删除老文件 |
| max_size | integer | 可选 | -1 | | 日志文件超过指定大小时进行切分,单位为 Byte |
| enable_compression | boolean | 可选 | false | | 是否启用日志文件压缩(gzip)。该功能需要安装 `tar` 。 |

开启该插件后,就会按照参数自动切分日志文件了。比如下面的例子是根据 `interval: 10` 和 `max_kept: 10` 得到的样本。
Expand Down Expand Up @@ -93,6 +94,7 @@ plugin_attr:
log-rotate:
interval: 3600 # rotate interval (unit: second)
max_kept: 168 # max number of log files will be kept
max_size: -1 # max size of log files will be kept
enable_compression: false # enable log file compression(gzip) or not, default false
```

Expand Down
143 changes: 143 additions & 0 deletions t/plugin/log-rotate3.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

use t::APISIX 'no_plan';

repeat_each(1);
no_long_string();
no_shuffle();
no_root_location();

add_block_preprocessor(sub {
my ($block) = @_;

if (!defined $block->yaml_config) {
my $yaml_config = <<_EOC_;
apisix:
node_listen: 1984
admin_key: ~
plugins:
- log-rotate
plugin_attr:
log-rotate:
interval: 86400
max_size: 9
max_kept: 3
enable_compression: false
_EOC_

$block->set_value("yaml_config", $yaml_config);
}

if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
$block->set_value("no_error_log", "[error]");
}

if (!defined $block->request) {
$block->set_value("request", "GET /t");
}

});

run_tests;

__DATA__

=== TEST 1: log rotate by max_size
--- config
location /t {
content_by_lua_block {
ngx.log(ngx.ERR, "start xxxxxx")
ngx.sleep(2)
local has_split_access_file = false
local has_split_error_file = false
local lfs = require("lfs")
for file_name in lfs.dir(ngx.config.prefix() .. "/logs/") do
if string.match(file_name, "__access.log$") then
has_split_access_file = true
end

if string.match(file_name, "__error.log$") then
has_split_error_file = true
end
end

if not has_split_access_file or not has_split_error_file then
ngx.status = 500
else
ngx.status = 200
end
}
}
--- error_code eval
qihaiyan marked this conversation as resolved.
Show resolved Hide resolved
[200]



=== TEST 2: in current log
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.1)
ngx.log(ngx.WARN, "start xxxxxx")
ngx.say("done")
}
}
--- response_body
done
--- error_log
start xxxxxx



=== TEST 3: check file changes
--- config
location /t {
content_by_lua_block {
ngx.sleep(2)

local default_logs = {}
for file_name in lfs.dir(ngx.config.prefix() .. "/logs/") do
if string.match(file_name, "__error.log$") or string.match(file_name, "__access.log$") then
local filepath = ngx.config.prefix() .. "/logs/" .. file_name
local attr = lfs.attributes(filepath)
if attr then
default_logs[filepath] = { change = attr.change, size = attr.size }
end
end
end

ngx.sleep(1)

local passed = false
for filepath, origin_attr in pairs(default_logs) do
local check_attr = lfs.attributes(filepath)
if check_attr.change == origin_attr.change and check_attr.size == origin_attr.size then
passed = true
else
passed = false
tzssangglass marked this conversation as resolved.
Show resolved Hide resolved
break
end
end

if passed then
ngx.say("passed")
end
}
}
--- response_body
passed