You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by me...@apache.org on 2020/03/15 01:22:23 UTC

[incubator-apisix] branch master updated: feature: implemented tcp logger plugin. (#1221)

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 <ak...@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