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: add error log skywalking reporter #4633

Merged
merged 25 commits into from
Aug 4, 2021
Merged
Show file tree
Hide file tree
Changes from 12 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
137 changes: 120 additions & 17 deletions apisix/plugins/error-log-logger.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ local errlog = require("ngx.errlog")
local batch_processor = require("apisix.utils.batch-processor")
local plugin = require("apisix.plugin")
local timers = require("apisix.timers")
local http = require("resty.http")
local plugin_name = "error-log-logger"
local table = core.table
local schema_def = core.schema
Expand All @@ -36,23 +37,50 @@ local lrucache = core.lrucache.new({
local metadata_schema = {
type = "object",
properties = {
host = schema_def.host_def,
dmsolr marked this conversation as resolved.
Show resolved Hide resolved
port = {type = "integer", minimum = 0},
tls = {type = "boolean", default = false},
tls_server_name = {type = "string"},
timeout = {type = "integer", minimum = 1, default = 3},
keepalive = {type = "integer", minimum = 1, default = 30},
type = {type = "string", default = "TCP", enum = {"TCP", "SKYWALKING"}},
dmsolr marked this conversation as resolved.
Show resolved Hide resolved
dmsolr marked this conversation as resolved.
Show resolved Hide resolved
tcp = {
type = "object",
properties = {
host = schema_def.host_def,
port = {type = "integer", minimum = 0},
tls = {type = "boolean", default = false},
tls_server_name = {type = "string"},
},
required = {"host", "port"}
},
skywalking = {
type = "object",
properties = {
endpoint_addr = {schema_def.uri, default = "http://127.0.0.1:12900/v3/logs"},
service_name = {type = "string", default = "APISIX"},
service_instance_name = {type="string", default = "APISIX Service Instance"},
},
required = {"endpoint_addr"}
dmsolr marked this conversation as resolved.
Show resolved Hide resolved
},
host = {schema_def.host_def, description = "Deprecated, use `tcp.host` instead."},
port = {type = "integer", minimum = 0, description = "Deprecated, use `tcp.port` instead."},
tls = {type = "boolean", default = false,
description = "Deprecated, use `tcp.tls` instead."},
tls_server_name = {type = "string",
description = "Deprecated, use `tcp.tls_server_name` instead."},
name = {type = "string", default = plugin_name},
level = {type = "string", default = "WARN", enum = {"STDERR", "EMERG", "ALERT", "CRIT",
"ERR", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG"}},
timeout = {type = "integer", minimum = 1, default = 3},
keepalive = {type = "integer", minimum = 1, default = 30},
batch_max_size = {type = "integer", minimum = 0, default = 1000},
max_retry_count = {type = "integer", minimum = 0, default = 0},
retry_delay = {type = "integer", minimum = 0, default = 1},
buffer_duration = {type = "integer", minimum = 1, default = 60},
inactive_timeout = {type = "integer", minimum = 1, default = 3},
},
required = {"host", "port"}
tokers marked this conversation as resolved.
Show resolved Hide resolved
oneOf = {
{required = {"skywalking"}},
{required = {"tcp"}}
}
}


local schema = {
type = "object",
}
Expand Down Expand Up @@ -85,7 +113,15 @@ local _M = {
}


local function send_to_server(data)
function _M.check_schema(conf, schema_type)
if schema_type == core.schema.TYPE_METADATA then
return core.schema.check(metadata_schema, conf)
end
return core.schema.check(schema, conf)
end


local function send_to_tcp_server(data)
local sock, soc_err = tcp()

if not sock then
Expand All @@ -94,33 +130,92 @@ local function send_to_server(data)

sock:settimeout(config.timeout * 1000)

local ok, err = sock:connect(config.host, config.port)
local tcp_config = config.tcp
local ok, err = sock:connect(tcp_config.host, tcp_config.port)
if not ok then
return false, "failed to connect the TCP server: host[" .. config.host
.. "] port[" .. tostring(config.port) .. "] err: " .. err
return false, "failed to connect the TCP server: host[" .. tcp_config.host
.. "] port[" .. tostring(tcp_config.port) .. "] err: " .. err
end

if config.tls then
ok, err = sock:sslhandshake(false, config.tls_server_name, false)
if tcp_config.tls then
ok, err = sock:sslhandshake(false, tcp_config.tls_server_name, false)
if not ok then
sock:close()
return false, "failed to perform TLS handshake to TCP server: host["
.. config.host .. "] port[" .. tostring(config.port) .. "] err: " .. err
.. tcp_config.host .. "] port[" .. tostring(tcp_config.port) .. "] err: " .. err
end
end

local bytes, err = sock:send(data)
if not bytes then
sock:close()
return false, "failed to send data to TCP server: host[" .. config.host
.. "] port[" .. tostring(config.port) .. "] err: " .. err
return false, "failed to send data to TCP server: host[" .. tcp_config.host
.. "] port[" .. tostring(tcp_config.port) .. "] err: " .. err
end

sock:setkeepalive(config.keepalive * 1000)
return true
end


local function send_to_skywalking(log_message)
local err_msg
local res = true
core.log.info("sending a batch logs to ", config.skywalking.endpoint_addr)

local httpc = http.new()
httpc:set_timeout(config.timeout * 1000)

local entries = {}
for i = 1, #log_message, 2 do
if not log_message[i] then
core.log.warn("------------------ : " .. tostring(i))
end
local content = {
service = config.skywalking.service_name,
serviceInstance = config.skywalking.service_instance_name,
endpoint = "",
body = {
text = {
text = log_message[i]
}
}
}
table.insert(entries, content)
end

local httpc_res, httpc_err = httpc:request_uri(
config.skywalking.endpoint_addr,
{
method = "POST",
body = core.json.encode(entries),
keepalive_timeout = config.keepalive * 1000,
headers = {
["Content-Type"] = "application/json",
}
}
)

if not httpc_res then
return false, "error while sending data to skywalking["
.. config.skywalking.endpoint_addr .. "] " .. httpc_err
end

-- some error occurred in the server
if httpc_res.status >= 400 then
res = false
err_msg = string.format(
"server returned status code[%s] skywalking[%s] body[%s]",
httpc_res.status,
config.skywalking.endpoint_addr.endpoint_addr,
httpc_res:read_body()
)
end

return res, err_msg
end


local function update_filter(value)
local level = log_level[string.upper(value.level)]
local status, err = errlog.set_filter_level(level)
Expand All @@ -134,6 +229,14 @@ local function update_filter(value)
end


local function send(data)
if config.tcp then
dmsolr marked this conversation as resolved.
Show resolved Hide resolved
return send_to_tcp_server(data)
end
return send_to_skywalking(data)
end


local function process()
local metadata = plugin.plugin_metadata(plugin_name)
if not (metadata and metadata.value and metadata.modifiedIndex) then
Expand Down Expand Up @@ -180,7 +283,7 @@ local function process()
}

local err
log_buffer, err = batch_processor:new(send_to_server, config_bat)
log_buffer, err = batch_processor:new(send, config_bat)

if not log_buffer then
core.log.warn("error when creating the batch processor: ", err)
Expand Down
64 changes: 45 additions & 19 deletions docs/en/latest/plugins/error-log-logger.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,39 @@ title: error-log-logger

## Name

`error-log-logger` is a plugin which pushes the log data of APISIX's error.log to TCP servers.
`error-log-logger` is a plugin which pushes the log data of APISIX's `error.log` to TCP servers or [Apache SkyWalking](https://skywalking.apache.org/).

This plugin will provide the ability to send the log data which selected by the level to Monitoring tools and other TCP servers.
This plugin will provide the ability to send the log data which selected by the level to Monitoring tools and other TCP servers, and SkyWalking over HTTP.

This plugin provides the ability as a batch to push the log data to your external TCP servers. If not receive the log data, don't worry, it will automatically send the logs after the timer function expires in our Batch Processor.
This plugin provides the ability as a batch to push the log data to your external TCP servers or monitoring tools. If not receive the log data, don't worry, it will automatically send the logs after the timer function expires in our Batch Processor.

For more info on Batch-Processor in Apache APISIX please refer.
[Batch-Processor](../batch-processor.md)

## Attributes

| Name | Type | Requirement | Default | Valid | Description |
| ---------------- | ------- | ----------- | ------- | ------- | ---------------------------------------------------------------------------------------- |
| host | string | required | | | IP address or the Hostname of the TCP server. |
| port | integer | required | | [0,...] | Target upstream port. |
| timeout | integer | optional | 3 | [1,...] | Timeout for the upstream to connect and send, unit: second. |
| keepalive | integer | optional | 30 | [1,...] | Time for keeping the cosocket alive, unit: second. |
| level | string | optional | WARN | | The filter's log level, default warn, choose the level in ["STDERR", "EMERG", "ALERT", "CRIT", "ERR", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG"], the value ERR equals ERROR. |
| tls | boolean | optional | false | | Control whether to perform SSL verification |
| tls_server_name | string | optional | | | The server name for the new TLS extension SNI |
| batch_max_size | integer | optional | 1000 | [1,...] | Max size of each batch |
| inactive_timeout | integer | optional | 3 | [1,...] | Maximum age in seconds when the buffer will be flushed if inactive |
| buffer_duration | integer | optional | 60 | [1,...] | Maximum age in seconds of the oldest entry in a batch before the batch must be processed |
| max_retry_count | integer | optional | 0 | [0,...] | Maximum number of retries before removing from the processing pipe line |
| retry_delay | integer | optional | 1 | [0,...] | Number of seconds the process execution should be delayed if the execution fails |
| Name | Type | Requirement | Default | Valid | Description |
| -------------------------------- | ------- | ----------- | ------------------------------ | ------- | ----------------------------------------------------------------------------------------- |
| tcp.host | string | required | | | IP address or the Hostname of the TCP server. |
| tcp.port | integer | required | | [0,...] | Target upstream port. |
| tcp.tls | boolean | optional | false | | Control whether to perform SSL verification. |
| tcp.tls_server_name | string | optional | | | The server name for the new TLS extension SNI. |
| skywalking.endpoint_addr | string | required | http://127.0.0.1:12900/v3/logs | | the http endpoint of Skywalking. |
| skywalking.service_name | string | optional | APISIX | | service name for skywalking reporter |
| skywalking.service_instance_name | String | optional | APISIX Instance Name | | Service instance name for skywalking reporter, set it to `$hostname` to get local hostname directly.|
| timeout | integer | optional | 3 | [1,...] | (`Deprecated`) Timeout for the upstream to connect and send, unit: second. |
| host | string | optional | | | IP address or the Hostname of the TCP server. |
dmsolr marked this conversation as resolved.
Show resolved Hide resolved
| port | integer | optional | | [0,...] | (`Deprecated`) Target upstream port. |
| tls | boolean | optional | false | | (`Deprecated`) Control whether to perform SSL verification. |
| tls_server_name | string | optional | | | (`Deprecated`) The server name for the new TLS extension SNI. |
| timeout | integer | optional | 3 | [1,...] | Timeout for the upstream to connect and send, unit: second. |
| keepalive | integer | optional | 30 | [1,...] | Time for keeping the cosocket alive, unit: second. |
| level | string | optional | WARN | | The filter's log level, default warn, choose the level in ["STDERR", "EMERG", "ALERT", "CRIT", "ERR", "ERROR", "WARN", "NOTICE", "INFO", "DEBUG"], the value ERR equals ERROR. |
| batch_max_size | integer | optional | 1000 | [1,...] | Max size of each batch. |
| inactive_timeout | integer | optional | 3 | [1,...] | Maximum age in seconds when the buffer will be flushed if inactive. |
| buffer_duration | integer | optional | 60 | [1,...] | Maximum age in seconds of the oldest entry in a batch before the batch must be processed. |
| max_retry_count | integer | optional | 0 | [0,...] | Maximum number of retries before removing from the processing pipe line. |
| retry_delay | integer | optional | 1 | [0,...] | Number of seconds the process execution should be delayed if the execution fails. |

## How To Enable And Disable

Expand Down Expand Up @@ -96,8 +104,26 @@ Step: update the attributes of the plugin
```shell
curl http://127.0.0.1:9080/apisix/admin/plugin_metadata/error-log-logger -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"host": "127.0.0.1",
"port": 1999,
"tcp": {
"host": "127.0.0.1",
"port": 1999
},
"inactive_timeout": 1
}'
```

## How to set the SkyWalking OAP server address

Step: update the attributes of the plugin

```shell
curl http://127.0.0.1:9080/apisix/admin/plugin_metadata/error-log-logger -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"type": "SKYWALKING",
"skywalking": {
"host": "127.0.0.1",
dmsolr marked this conversation as resolved.
Show resolved Hide resolved
"port": 1999
},
"inactive_timeout": 1
}'
```
Loading