You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by GitBox <gi...@apache.org> on 2020/10/23 06:58:52 UTC

[GitHub] [apisix] membphis commented on a change in pull request #2488: feat: add error-log-logger plugin

membphis commented on a change in pull request #2488:
URL: https://github.com/apache/apisix/pull/2488#discussion_r510666517



##########
File path: conf/config-default.yaml
##########
@@ -193,6 +193,7 @@ plugins:                          # plugin list
   - proxy-mirror
   - request-id
   - hmac-auth
+  - error-log-logger

Review comment:
       Do not need to be turned on by default.
   
   `# - error-log-logger`, should be fine.

##########
File path: conf/config-default.yaml
##########
@@ -201,3 +202,9 @@ plugin_attr:
   log-rotate:
     interval: 3600    # rotate interval (unit: second)
     max_kept: 168     # max number of log files will be kept
+  error-log-logger:
+    host: "127.0.0.1"

Review comment:
       need some comment for each field

##########
File path: t/plugin/error-log-logger.t
##########
@@ -0,0 +1,122 @@
+#
+# 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();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    my $stream_single_server = <<_EOC_;
+    # fake server, only for test
+    server {
+        listen 33333;
+
+        content_by_lua_block {
+            local exiting = ngx.worker.exiting
+            local sock, err = ngx.req.socket(true)
+            if not sock then
+                ngx.log(ngx.WARN, "socket error:" .. err)

Review comment:
       `ngx.log(ngx.WARN, "socket error:" .. err)`
   
   to
   
   `ngx.log(ngx.WARN, "socket error:", err)`

##########
File path: t/plugin/error-log-logger.t
##########
@@ -0,0 +1,122 @@
+#
+# 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();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    my $stream_single_server = <<_EOC_;
+    # fake server, only for test
+    server {
+        listen 33333;
+
+        content_by_lua_block {
+            local exiting = ngx.worker.exiting
+            local sock, err = ngx.req.socket(true)
+            if not sock then
+                ngx.log(ngx.WARN, "socket error:" .. err)
+                return
+            end
+            sock:settimeout(30 * 1000)
+            while(not exiting())
+            do
+                local data, err =  sock:receive()
+                if (data) then
+                    ngx.log(ngx.INFO, "[Server] receive data:" .. data)
+                else 
+                    if err ~= "timeout" then
+                        ngx.log(ngx.WARN, "socket error:" .. err)
+                        return
+                    end
+                end
+            end
+        }
+    }
+_EOC_
+
+    $block->set_value("stream_config", $stream_single_server);
+});
+
+
+add_block_preprocessor(sub {

Review comment:
       why we need two `add_block_preprocessor`? I think one is enough

##########
File path: apisix/plugins/error-log-logger.lua
##########
@@ -0,0 +1,140 @@
+--
+-- 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 plugin_name = "error-log-logger"
+local errlog = require "ngx.errlog"
+local ngx = ngx
+local tcp = ngx.socket.tcp
+local select = select
+local type = type
+local string = string
+
+local timer
+local schema = {
+    type = "object",
+    properties = {},
+    additionalProperties = false,
+}
+
+local log_level = {
+    STDERR =    ngx.STDERR,
+    EMERG  =    ngx.EMERG,
+    ALERT  =    ngx.ALERT,
+    CRIT   =    ngx.CRIT,
+    ERR    =    ngx.ERR,
+    ERROR  =    ngx.ERR,
+    WARN   =    ngx.WARN,
+    NOTICE =     ngx.NOTICE,
+    INFO   =    ngx.INFO,
+    DEBUG  =    ngx.DEBUG
+}
+
+local _M = {
+    version = 0.1,
+    priority = 1091,
+    name = plugin_name,
+    schema = schema,
+}
+
+
+function _M.check_schema(conf)
+    return core.schema.check(schema, conf)
+end
+
+
+local function try_attr(t, ...)
+    local count = select('#', ...)
+    for i = 1, count do
+        local attr = select(i, ...)
+        t = t[attr]
+        if type(t) ~= "table" then
+            return false
+        end
+    end
+
+    return true
+end
+
+
+local function report()
+    local local_conf = core.config.local_conf()
+    local host, port
+    local timeout = 3
+    local keepalive = 3
+    local level = "warn"
+    if try_attr(local_conf, "plugin_attr", plugin_name) then
+        local attr = local_conf.plugin_attr[plugin_name]
+        host = attr.host
+        port = attr.port
+        level = attr.loglevel or level
+        timeout = attr.timeout or timeout
+        keepalive = attr.keepalive or keepalive
+    end
+    level = log_level[string.upper(level)]
+
+    local status, err = errlog.set_filter_level(level)
+    if not status then
+        core.log.warn("failed to set filter level by ngx.errlog, the error is :", err)
+        return
+    end
+
+    local sock, soc_err = tcp()
+    if not sock then
+        core.log.warn("failed to init the socket " .. soc_err)
+        return
+    end
+    sock:settimeout(timeout*1000)
+    local ok, err = sock:connect(host, port)
+    if not ok then
+        core.log.warn("connect to the server failed for " .. err)
+        return
+    end
+    local logs = errlog.get_logs(10)
+    while ( logs and #logs>0 ) do
+        for i = 1, #logs, 3 do
+            if logs[i] <= level then --ommit the lower log producted at the initial

Review comment:
       I think we can remove this line, it seems useless

##########
File path: apisix/plugins/error-log-logger.lua
##########
@@ -0,0 +1,140 @@
+--
+-- 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 plugin_name = "error-log-logger"
+local errlog = require "ngx.errlog"
+local ngx = ngx
+local tcp = ngx.socket.tcp
+local select = select
+local type = type
+local string = string
+
+local timer
+local schema = {
+    type = "object",
+    properties = {},
+    additionalProperties = false,
+}
+
+local log_level = {
+    STDERR =    ngx.STDERR,
+    EMERG  =    ngx.EMERG,
+    ALERT  =    ngx.ALERT,
+    CRIT   =    ngx.CRIT,
+    ERR    =    ngx.ERR,
+    ERROR  =    ngx.ERR,
+    WARN   =    ngx.WARN,
+    NOTICE =     ngx.NOTICE,
+    INFO   =    ngx.INFO,
+    DEBUG  =    ngx.DEBUG
+}
+
+local _M = {
+    version = 0.1,
+    priority = 1091,
+    name = plugin_name,
+    schema = schema,
+}
+
+
+function _M.check_schema(conf)
+    return core.schema.check(schema, conf)
+end
+
+
+local function try_attr(t, ...)
+    local count = select('#', ...)
+    for i = 1, count do
+        local attr = select(i, ...)
+        t = t[attr]
+        if type(t) ~= "table" then
+            return false
+        end
+    end
+
+    return true
+end
+
+
+local function report()
+    local local_conf = core.config.local_conf()
+    local host, port
+    local timeout = 3
+    local keepalive = 3
+    local level = "warn"
+    if try_attr(local_conf, "plugin_attr", plugin_name) then
+        local attr = local_conf.plugin_attr[plugin_name]
+        host = attr.host
+        port = attr.port
+        level = attr.loglevel or level
+        timeout = attr.timeout or timeout
+        keepalive = attr.keepalive or keepalive
+    end
+    level = log_level[string.upper(level)]
+
+    local status, err = errlog.set_filter_level(level)
+    if not status then
+        core.log.warn("failed to set filter level by ngx.errlog, the error is :", err)
+        return
+    end
+
+    local sock, soc_err = tcp()
+    if not sock then
+        core.log.warn("failed to init the socket " .. soc_err)
+        return
+    end
+    sock:settimeout(timeout*1000)
+    local ok, err = sock:connect(host, port)
+    if not ok then
+        core.log.warn("connect to the server failed for " .. err)
+        return
+    end
+    local logs = errlog.get_logs(10)
+    while ( logs and #logs>0 ) do
+        for i = 1, #logs, 3 do
+            if logs[i] <= level then --ommit the lower log producted at the initial
+                local bytes, err = sock:send(logs[i + 2])
+                if not bytes then
+                    core.log.info("send data  failed for " , err, ", the data:", logs[i + 2] )

Review comment:
       `] )` remove this space

##########
File path: conf/config-default.yaml
##########
@@ -201,3 +202,9 @@ plugin_attr:
   log-rotate:
     interval: 3600    # rotate interval (unit: second)
     max_kept: 168     # max number of log files will be kept
+  error-log-logger:
+    host: "127.0.0.1"
+    port: 33333
+    loglevel: "warn"
+

Review comment:
       one blank line is enough here

##########
File path: conf/config-default.yaml
##########
@@ -201,3 +202,9 @@ plugin_attr:
   log-rotate:
     interval: 3600    # rotate interval (unit: second)
     max_kept: 168     # max number of log files will be kept
+  error-log-logger:
+    host: "127.0.0.1"
+    port: 33333
+    loglevel: "warn"

Review comment:
       need a better name: `log_level` or `level`

##########
File path: apisix/plugins/error-log-logger.lua
##########
@@ -0,0 +1,140 @@
+--
+-- 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 plugin_name = "error-log-logger"
+local errlog = require "ngx.errlog"
+local ngx = ngx
+local tcp = ngx.socket.tcp
+local select = select
+local type = type
+local string = string
+
+local timer
+local schema = {
+    type = "object",
+    properties = {},
+    additionalProperties = false,
+}
+
+local log_level = {
+    STDERR =    ngx.STDERR,
+    EMERG  =    ngx.EMERG,
+    ALERT  =    ngx.ALERT,
+    CRIT   =    ngx.CRIT,
+    ERR    =    ngx.ERR,
+    ERROR  =    ngx.ERR,
+    WARN   =    ngx.WARN,
+    NOTICE =     ngx.NOTICE,
+    INFO   =    ngx.INFO,
+    DEBUG  =    ngx.DEBUG
+}
+
+local _M = {
+    version = 0.1,
+    priority = 1091,
+    name = plugin_name,
+    schema = schema,
+}
+
+
+function _M.check_schema(conf)
+    return core.schema.check(schema, conf)
+end
+
+
+local function try_attr(t, ...)
+    local count = select('#', ...)
+    for i = 1, count do
+        local attr = select(i, ...)
+        t = t[attr]
+        if type(t) ~= "table" then
+            return false
+        end
+    end
+
+    return true
+end
+
+
+local function report()
+    local local_conf = core.config.local_conf()
+    local host, port
+    local timeout = 3
+    local keepalive = 3
+    local level = "warn"
+    if try_attr(local_conf, "plugin_attr", plugin_name) then
+        local attr = local_conf.plugin_attr[plugin_name]
+        host = attr.host
+        port = attr.port
+        level = attr.loglevel or level
+        timeout = attr.timeout or timeout

Review comment:
       need to add this field in `config-default.yaml`

##########
File path: t/plugin/error-log-logger.t
##########
@@ -0,0 +1,122 @@
+#
+# 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();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    my $stream_single_server = <<_EOC_;
+    # fake server, only for test
+    server {
+        listen 33333;
+
+        content_by_lua_block {
+            local exiting = ngx.worker.exiting
+            local sock, err = ngx.req.socket(true)
+            if not sock then
+                ngx.log(ngx.WARN, "socket error:" .. err)
+                return
+            end
+            sock:settimeout(30 * 1000)
+            while(not exiting())
+            do
+                local data, err =  sock:receive()
+                if (data) then
+                    ngx.log(ngx.INFO, "[Server] receive data:" .. data)
+                else 
+                    if err ~= "timeout" then
+                        ngx.log(ngx.WARN, "socket error:" .. err)
+                        return
+                    end
+                end
+            end
+        }
+    }
+_EOC_
+
+    $block->set_value("stream_config", $stream_single_server);
+});
+
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    my $stream_default_server = <<_EOC_;
+	    content_by_lua_block {
+	    	ngx.log(ngx.INFO, "a stream server")
+	    }
+_EOC_
+
+    $block->set_value("stream_server_config", $stream_default_server);
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: log a warn level message
+--- config
+    location /tg {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            core.log.warn("this is a warning message for test.\n")
+        }
+    }
+--- request
+GET /tg
+--- response_body
+--- error_log eval
+qr/\[Server\] receive data:.*this is a warning message for test./
+--- wait: 1
+
+
+
+=== TEST 2: log an error level message
+--- config
+    location /tg {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            core.log.error("this is an error message for test.\n")
+        }
+    }
+--- request
+GET /tg
+--- response_body
+--- error_log eval
+qr/\[Server\] receive data:.*this is an error message for test./
+--- wait: 1
+
+
+
+=== TEST 3: log an info level message

Review comment:
       more test case about when user set different address or wrong address

##########
File path: apisix/plugins/error-log-logger.lua
##########
@@ -0,0 +1,140 @@
+--
+-- 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 plugin_name = "error-log-logger"
+local errlog = require "ngx.errlog"
+local ngx = ngx
+local tcp = ngx.socket.tcp
+local select = select
+local type = type
+local string = string
+
+local timer
+local schema = {
+    type = "object",
+    properties = {},
+    additionalProperties = false,
+}
+
+local log_level = {
+    STDERR =    ngx.STDERR,
+    EMERG  =    ngx.EMERG,
+    ALERT  =    ngx.ALERT,
+    CRIT   =    ngx.CRIT,
+    ERR    =    ngx.ERR,
+    ERROR  =    ngx.ERR,
+    WARN   =    ngx.WARN,
+    NOTICE =     ngx.NOTICE,
+    INFO   =    ngx.INFO,
+    DEBUG  =    ngx.DEBUG
+}
+
+local _M = {
+    version = 0.1,
+    priority = 1091,
+    name = plugin_name,
+    schema = schema,
+}
+
+
+function _M.check_schema(conf)
+    return core.schema.check(schema, conf)
+end
+
+
+local function try_attr(t, ...)
+    local count = select('#', ...)
+    for i = 1, count do
+        local attr = select(i, ...)
+        t = t[attr]
+        if type(t) ~= "table" then
+            return false
+        end
+    end
+
+    return true
+end
+
+
+local function report()
+    local local_conf = core.config.local_conf()
+    local host, port
+    local timeout = 3
+    local keepalive = 3
+    local level = "warn"
+    if try_attr(local_conf, "plugin_attr", plugin_name) then
+        local attr = local_conf.plugin_attr[plugin_name]
+        host = attr.host
+        port = attr.port
+        level = attr.loglevel or level
+        timeout = attr.timeout or timeout
+        keepalive = attr.keepalive or keepalive
+    end
+    level = log_level[string.upper(level)]
+
+    local status, err = errlog.set_filter_level(level)
+    if not status then
+        core.log.warn("failed to set filter level by ngx.errlog, the error is :", err)
+        return
+    end
+
+    local sock, soc_err = tcp()
+    if not sock then
+        core.log.warn("failed to init the socket " .. soc_err)
+        return
+    end
+    sock:settimeout(timeout*1000)
+    local ok, err = sock:connect(host, port)
+    if not ok then
+        core.log.warn("connect to the server failed for " .. err)
+        return
+    end
+    local logs = errlog.get_logs(10)
+    while ( logs and #logs>0 ) do
+        for i = 1, #logs, 3 do
+            if logs[i] <= level then --ommit the lower log producted at the initial
+                local bytes, err = sock:send(logs[i + 2])

Review comment:
       https://github.com/apache/apisix/blob/master/apisix/utils/batch-processor.lua
   
   I think we can use this way to send the error log.

##########
File path: apisix/plugins/error-log-logger.lua
##########
@@ -0,0 +1,140 @@
+--
+-- 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 plugin_name = "error-log-logger"
+local errlog = require "ngx.errlog"
+local ngx = ngx
+local tcp = ngx.socket.tcp
+local select = select
+local type = type
+local string = string
+
+local timer
+local schema = {
+    type = "object",
+    properties = {},
+    additionalProperties = false,
+}
+
+local log_level = {
+    STDERR =    ngx.STDERR,
+    EMERG  =    ngx.EMERG,
+    ALERT  =    ngx.ALERT,
+    CRIT   =    ngx.CRIT,
+    ERR    =    ngx.ERR,
+    ERROR  =    ngx.ERR,
+    WARN   =    ngx.WARN,
+    NOTICE =     ngx.NOTICE,
+    INFO   =    ngx.INFO,
+    DEBUG  =    ngx.DEBUG
+}
+
+local _M = {
+    version = 0.1,
+    priority = 1091,
+    name = plugin_name,
+    schema = schema,
+}
+
+
+function _M.check_schema(conf)
+    return core.schema.check(schema, conf)
+end
+
+
+local function try_attr(t, ...)
+    local count = select('#', ...)
+    for i = 1, count do
+        local attr = select(i, ...)
+        t = t[attr]
+        if type(t) ~= "table" then
+            return false
+        end
+    end
+
+    return true
+end
+
+
+local function report()
+    local local_conf = core.config.local_conf()
+    local host, port
+    local timeout = 3
+    local keepalive = 3
+    local level = "warn"
+    if try_attr(local_conf, "plugin_attr", plugin_name) then
+        local attr = local_conf.plugin_attr[plugin_name]
+        host = attr.host
+        port = attr.port
+        level = attr.loglevel or level
+        timeout = attr.timeout or timeout
+        keepalive = attr.keepalive or keepalive
+    end
+    level = log_level[string.upper(level)]
+
+    local status, err = errlog.set_filter_level(level)
+    if not status then
+        core.log.warn("failed to set filter level by ngx.errlog, the error is :", err)
+        return
+    end
+
+    local sock, soc_err = tcp()
+    if not sock then
+        core.log.warn("failed to init the socket " .. soc_err)
+        return
+    end
+    sock:settimeout(timeout*1000)
+    local ok, err = sock:connect(host, port)
+    if not ok then
+        core.log.warn("connect to the server failed for " .. err)
+        return
+    end
+    local logs = errlog.get_logs(10)
+    while ( logs and #logs>0 ) do
+        for i = 1, #logs, 3 do
+            if logs[i] <= level then --ommit the lower log producted at the initial
+                local bytes, err = sock:send(logs[i + 2])
+                if not bytes then
+                    core.log.info("send data  failed for " , err, ", the data:", logs[i + 2] )
+                    return
+                end
+            end
+        end
+        logs = errlog.get_logs(10)
+	end
+    sock:setkeepalive(keepalive*1000)

Review comment:
       code style: need a space, `keepalive * 1000`




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org