This is an automated email from the ASF dual-hosted git repository. membphis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-apisix.git
The following commit(s) were added to refs/heads/master by this push: new d078711 feature: implemented tcp logger plugin. (#1221) d078711 is described below commit d07871186ac330f077dbba0a51d96b2e4d899094 Author: Ayeshmantha Perera <akayeshman...@apache.org> AuthorDate: Sun Mar 15 02:22:13 2020 +0100 feature: implemented tcp logger plugin. (#1221) --- conf/config.yaml | 1 + doc/README.md | 1 + doc/plugins/tcp-logger.md | 102 +++++++++++++++++ lua/apisix/plugins/tcp-logger.lua | 98 ++++++++++++++++ t/APISIX.pm | 2 + t/admin/plugins.t | 2 +- t/debug/debug-mode.t | 2 + t/plugin/tcp-logger.t | 230 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 437 insertions(+), 1 deletion(-) diff --git a/conf/config.yaml b/conf/config.yaml index 770bd47..8c1beb2 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -124,6 +124,7 @@ plugins: # plugin list - fault-injection - udp-logger - wolf-rbac + - tcp-logger stream_plugins: - mqtt-proxy diff --git a/doc/README.md b/doc/README.md index 7b01f22..63efcbf 100644 --- a/doc/README.md +++ b/doc/README.md @@ -58,6 +58,7 @@ Plugins * [redirect](plugins/redirect.md): URI redirect. * [response-rewrite](plugins/response-rewrite.md): Set customized response status code, body and header to the client. * [fault-injection](plugins/fault-injection.md): The specified response body, response code, and response time can be returned, which provides processing capabilities in different failure scenarios, such as service failure, service overload, and high service delay. +* [tcp-logger](plugins/tcp-logger.md): Log requests to TCP servers Deploy to the Cloud ======= diff --git a/doc/plugins/tcp-logger.md b/doc/plugins/tcp-logger.md new file mode 100644 index 0000000..6fe59dd --- /dev/null +++ b/doc/plugins/tcp-logger.md @@ -0,0 +1,102 @@ +<!-- +# +# 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. +# +--> + +# Summary +- [**Name**](#name) +- [**Attributes**](#attributes) +- [**How To Enable**](#how-to-enable) +- [**Test Plugin**](#test-plugin) +- [**Disable Plugin**](#disable-plugin) + + +## Name + +`tcp-logger` is a plugin which push Log data requests to TCP servers. + +This will provide the ability to send Log data requests as JSON objects to Monitoring tools and other TCP servers. + +## Attributes + +|Name |Requirement |Description| +|--------- |--------|-----------| +| host |required| IP address or the Hostname of the TCP server.| +| port |required| Target upstream port.| +| timeout |optional|Timeout for the upstream to send data.| +| tls |optional|Boolean value to control whether to perform SSL verification| +| tls_options |optional|tls options| + + +## How To Enable + +1. Here is an examle on how to enable tcp-logger plugin for a specific route. + +```shell +curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "username": "foo", + "plugins": { + "plugins": { + "tcp-logger": { + "host": "127.0.0.1", + "port": 5044, + "tls": false + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + }, + "uri": "/hello" + } +}' +``` + +## Test Plugin + +* success: + +```shell +$ curl -i http://127.0.0.1:9080/hello +HTTP/1.1 200 OK +... +hello, world +``` + +## Disable Plugin + +When you want to disable the `tcp-logger` plugin, it is very simple, + you can delete the corresponding json configuration in the plugin configuration, + no need to restart the service, it will take effect immediately: + +```shell +$ curl http://127.0.0.1:2379/apisix/admin/routes/1 -X PUT -d value=' +{ + "methods": ["GET"], + "uri": "/hello", + "plugins": {}, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + } +}' +``` diff --git a/lua/apisix/plugins/tcp-logger.lua b/lua/apisix/plugins/tcp-logger.lua new file mode 100644 index 0000000..965741b --- /dev/null +++ b/lua/apisix/plugins/tcp-logger.lua @@ -0,0 +1,98 @@ +-- +-- 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. +-- +local core = require("apisix.core") +local log_util = require("apisix.utils.log-util") +local plugin_name = "tcp-logger" +local ngx = ngx + +local timer_at = ngx.timer.at +local tcp = ngx.socket.tcp + +local schema = { + type = "object", + properties = { + host = {type = "string"}, + port = {type = "integer", minimum = 0}, + timeout = { -- timeout in milliseconds + type = "integer", minimum = 1, default= 1000 + }, + tls = { type = "boolean", default = false }, + tls_options = { type = "string" } + }, + required = {"host", "port"} +} + + +local _M = { + version = 0.1, + priority = 405, + name = plugin_name, + schema = schema, +} + +function _M.check_schema(conf) + return core.schema.check(schema, conf) +end + +local function log(premature, conf, log_message) + if premature then + return + end + + local sock,err = tcp() + if not sock then + core.log.error("failed to init the socket", err) + return + end + + sock:settimeout(conf.timeout) + + local ok, err = sock:connect(conf.host, conf.port) + if not ok then + core.log.error("failed to connect to TCP server: host[", + conf.host, "] port[", conf.port, "] ", err) + return + end + + if conf.tls then + ok, err = sock:sslhandshake(true, conf.tls_options, false) + if not ok then + core.log.error("failed to to perform TLS handshake to TCP server: host[", + conf.host, "] port[", conf.port, "] ", err) + return + end + end + + ok, err = sock:send(log_message) + if not ok then + core.log.error("failed to send data to TCP server: host[", + conf.host, "] port[", conf.port, "] ", err) + end + + ok, err = sock:close() + if not ok then + core.log.error("failed to close the TCP connection, host[", + conf.host, "] port[", conf.port, "] ", err) + end +end + + +function _M.log(conf) + return timer_at(0, log, conf, core.json.encode(log_util.get_full_log(ngx))) +end + +return _M diff --git a/t/APISIX.pm b/t/APISIX.pm index 6d4da7a..8091ae5 100644 --- a/t/APISIX.pm +++ b/t/APISIX.pm @@ -189,6 +189,8 @@ _EOC_ listen 1980; listen 1981; listen 1982; + listen 5044; + _EOC_ my $ipv6_fake_server = ""; diff --git a/t/admin/plugins.t b/t/admin/plugins.t index 85a9c10..b8430c7 100644 --- a/t/admin/plugins.t +++ b/t/admin/plugins.t @@ -30,7 +30,7 @@ __DATA__ --- request GET /apisix/admin/plugins/list --- response_body_like eval -qr/\["limit-req","limit-count","limit-conn","key-auth","basic-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction","grpc-transcode","serverless-pre-function","serverless-post-function","openid-connect","proxy-rewrite","redirect","response-rewrite","fault-injection","udp-logger","wolf-rbac"\]/ +qr/\["limit-req","limit-count","limit-conn","key-auth","basic-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction","grpc-transcode","serverless-pre-function","serverless-post-function","openid-connect","proxy-rewrite","redirect","response-rewrite","fault-injection","udp-logger","wolf-rbac","tcp-logger"\]/ --- no_error_log [error] diff --git a/t/debug/debug-mode.t b/t/debug/debug-mode.t index b069a23..012262a 100644 --- a/t/debug/debug-mode.t +++ b/t/debug/debug-mode.t @@ -72,6 +72,7 @@ loaded plugin and sort by priority: 900 name: redirect loaded plugin and sort by priority: 899 name: response-rewrite loaded plugin and sort by priority: 506 name: grpc-transcode loaded plugin and sort by priority: 500 name: prometheus +loaded plugin and sort by priority: 405 name: tcp-logger loaded plugin and sort by priority: 400 name: udp-logger loaded plugin and sort by priority: 0 name: example-plugin loaded plugin and sort by priority: -1000 name: zipkin @@ -79,6 +80,7 @@ loaded plugin and sort by priority: -2000 name: serverless-post-function + === TEST 2: set route(no plugin) --- config location /t { diff --git a/t/plugin/tcp-logger.t b/t/plugin/tcp-logger.t new file mode 100644 index 0000000..1859286 --- /dev/null +++ b/t/plugin/tcp-logger.t @@ -0,0 +1,230 @@ +# +# 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_root_location(); +run_tests; + +__DATA__ + +=== TEST 1: sanity +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.tcp-logger") + local ok, err = plugin.check_schema({host = "127.0.0.1", port = 3000}) + if not ok then + ngx.say(err) + end + + ngx.say("done") + } + } +--- request +GET /t +--- response_body +done +--- no_error_log +[error] + + + +=== TEST 2: missing host +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.tcp-logger") + local ok, err = plugin.check_schema({port = 3000}) + if not ok then + ngx.say(err) + end + + ngx.say("done") + } + } +--- request +GET /t +--- response_body +property "host" is required +done +--- no_error_log +[error] + + + +=== TEST 3: wrong type of string +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.tcp-logger") + local ok, err = plugin.check_schema({host= "127.0.0.1", port = 2000, timeout = "10", + tls = false, tls_options = "tls options"}) + if not ok then + ngx.say(err) + end + + ngx.say("done") + } + } +--- request +GET /t +--- response_body +property "timeout" validation failed: wrong type: expected integer, got string +done +--- no_error_log +[error] + + + +=== TEST 4: add plugin +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "tcp-logger": { + "host": "127.0.0.1", + "port": 5044, + "tls": false + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]], + [[{ + "node": { + "value": { + "plugins": { + "tcp-logger": { + "host": "127.0.0.1", + "port": 5044, + "tls": false + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }, + "key": "/apisix/routes/1" + }, + "action": "set" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 5: access +--- request +GET /hello +--- response_body +hello world +--- no_error_log +[error] +--- wait: 0.2 + + + +=== TEST 6: error log +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "tcp-logger": { + "host": "312.0.0.1", + "port": 2000 + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]], + [[{ + "node": { + "value": { + "plugins": { + "tcp-logger": { + "host": "312.0.0.1", + "port": 2000 + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }, + "key": "/apisix/routes/1" + }, + "action": "set" + }]] + ) + + if code >= 300 then + ngx.status = code + end + + ngx.say(body) + + local http = require "resty.http" + local httpc = http.new() + local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri, {method = "GET"}) + } + } +--- request +GET /t +--- error_log +failed to connect to TCP server: host[312.0.0.1] port[2000] +[error] +--- wait: 0.2