Skip to content
Open
18 changes: 16 additions & 2 deletions apisix/plugins/file-logger.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ local plugin = require("apisix.plugin")
local expr = require("resty.expr.v1")
local ngx = ngx
local io_open = io.open
local io_stdout = io.stdout
local io_stderr = io.stderr
local is_apisix_or, process = pcall(require, "resty.apisix.process")


Expand Down Expand Up @@ -89,6 +91,14 @@ local _M = {
}


local std_files = {
["/dev/stdout"] = io_stdout,
["/dev/stderr"] = io_stderr,
}
io_stdout:setvbuf("no")
io_stderr:setvbuf("no")


local function get_configured_path(conf)
if conf.path then
return conf.path
Expand Down Expand Up @@ -185,7 +195,10 @@ local function write_file_data(conf, log_message)

local file, err
local file_conf = conf.path and conf or {path = path}
if open_file_cache then
local std_file = std_files[path]
if std_file then
file = std_file
elseif open_file_cache then
file, err = open_file_cache(file_conf)
else
file, err = io_open(path, 'a+')
Expand All @@ -204,8 +217,9 @@ local function write_file_data(conf, log_message)
core.log.error("failed to write file: ", path, ", error info: ", err)
end

-- stdout/stderr should not be closed
-- file will be closed by gc, if open_file_cache exists
if not open_file_cache then
if not open_file_cache and not std_file then
file:close()
end
end
Expand Down
2 changes: 1 addition & 1 deletion docs/en/latest/plugins/file-logger.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ The `file-logger` Plugin is used to push log streams to a specific location.

| Name | Type | Required | Description |
| ---- | ------ | -------- | ------------- |
| path | string | True | Log file path. |
| path | string | True | Log file path. You can use `/dev/stdout` to write logs to the standard output and `/dev/stderr` to write to the standard error output. |
| log_format | object | False | Log format declared as key-value pairs in JSON. Values support strings and nested objects (up to five levels deep; deeper fields are truncated). Within strings, [APISIX](../apisix-variable.md) or [NGINX](http://nginx.org/en/docs/varindex.html) variables can be referenced by prefixing with `$`. |
| include_req_body | boolean | False | When set to `true` includes the request body in the log. If the request body is too big to be kept in the memory, it can't be logged due to Nginx's limitations. |
| include_req_body_expr | array | False | Filter for when the `include_req_body` attribute is set to `true`. Request body is only logged when the expression set here evaluates to `true`. See [lua-resty-expr](https://github.com/api7/lua-resty-expr) for more. |
Expand Down
2 changes: 1 addition & 1 deletion docs/zh/latest/plugins/file-logger.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ description: API 网关 Apache APISIX file-logger 插件可用于将日志数据

| 名称 | 类型 | 必选项 | 描述 |
| ---------------- | ------- |-----| ------------------------------------------------ |
| path | string | 是 | 自定义输出文件路径。例如:`logs/file.log`。 |
| path | string | 是 | 自定义输出文件路径。例如:`logs/file.log`。可以使用 `/dev/stdout` 将日志写入标准输出,使用 `/dev/stderr` 将日志写入标准错误输出。 |
| log_format | object | 否 | 日志格式以 JSON 的键值对声明。值支持字符串和嵌套对象(最多五层,超出部分将被截断)。字符串中可通过在前面加上 `$` 来引用 [APISIX 变量](../apisix-variable.md) 或 [NGINX 内置变量](http://nginx.org/en/docs/varindex.html)。 |
| include_req_body | boolean | 否 | 当设置为 `true` 时,日志中将包含请求体。如果请求体太大而无法在内存中保存,则由于 Nginx 的限制,无法记录请求体。|
| include_req_body_expr | array | 否 | 当 `include_req_body` 属性设置为 `true` 时的过滤器。只有当此处设置的表达式求值为 `true` 时,才会记录请求体。有关更多信息,请参阅 [lua-resty-expr](https://github.com/api7/lua-resty-expr) 。 |
Expand Down
77 changes: 77 additions & 0 deletions t/cli/test_file_logger_stdout.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash

#
# 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.
#

. ./t/cli/common.sh

# file-logger: verify logs are written to stdout via "/dev/stdout".
# Uses openresty directly (like docker-entrypoint.sh) because `apisix start`
# consumes stdout via io.popen, making /dev/stdout output unobservable.

echo '
apisix:
node_listen: 9080
deployment:
role: data_plane
role_data_plane:
config_provider: yaml
' > conf/config.yaml

echo '
routes:
- uri: /hello
upstream:
nodes:
"127.0.0.1:1980": 1
type: roundrobin
plugins:
file-logger:
path: /dev/stdout
#END
' > conf/apisix.yaml

make init
# `apisix start` would create the logs directory; since we launch openresty
# directly, create it ourselves so nginx can open its error log.
mkdir -p logs

# run openresty directly in the foreground and capture its stdout
openresty -p "$PWD" -g 'daemon off;' > stdout.log 2> stderr.log &
apisix_pid=$!

# wait until APISIX is ready to serve
for _ in $(seq 1 20); do
if curl -s -o /dev/null http://127.0.0.1:9080/hello; then
break
fi
sleep 0.5
done

curl -s http://127.0.0.1:9080/hello > /dev/null
sleep 1

kill "$apisix_pid" 2>/dev/null || true
wait "$apisix_pid" 2>/dev/null || true

if ! grep -q '"uri":"/hello"' stdout.log; then
echo "failed: file-logger did not write to stdout"
cat stdout.log
exit 1
fi

echo "passed: file-logger writes to stdout"
Loading