We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Infinity values in log messages cause issues when parsing JSON
##### SUMMARY

When writing out structured log data using the `GelfFormatter`, periodically the field `_content_length` is output with a value of: `"_content_length": Infinity`. This is invalid JSON and causes an error when parsing from external logging tools such as FluentD

##### ISSUE TYPE
 - Bug Report

##### STACKSTORM VERSION
```shell
st2 3.0.1, on Python 2.7.5
```


##### OS / ENVIRONMENT / INSTALL METHOD
```
OS = Red Hat 7.6
Install = puppet-st2
```

##### STEPS TO REPRODUCE
Configure GELF logging as described here: https://stackstorm.com/2017/08/22/stackstorm-centralized-logging-graylog/

Monitor the `/var/log/st2/st2stream.gelf.log` and periodically lines will be written out with `"_content_length": Infinity"`

```shell
st2stream.gelf.log:{"_path": "/v1/stream", "version": "1.1", "level": 6, "timestamp": 1561477579, "_content_length": Infinity, "_python": {"name": "st2.st2common.middleware.logging", "process": 81491, "module": "logging", "funcName": "__call__", "processName": "MainProcess", "lineno": 118, "filename": "logging.py"}, "_request_id": "2e85dac9-1cb2-46d0-969a-2c93efba10fa", "timestamp_f": 1561477579.465585, "host": "stackstorm.domain.tld", "full_message": "2e85dac9-1cb2-46d0-969a-2c93efba10fa - 200 inf 15.51ms", "_remote_addr": "127.0.0.1", "_runtime": 15.51, "_status": 200, "_method": "GET", "short_message": "2e85dac9-1cb2-46d0-969a-2c93efba10fa - 200 inf 15.51ms"}
st2stream.gelf.log:{"_path": "/v1/stream", "version": "1.1", "level": 6, "timestamp": 1561479377, "_content_length": Infinity, "_python": {"name": "st2.st2common.middleware.logging", "process": 81491, "module": "logging", "funcName": "__call__", "processName": "MainProcess", "lineno": 118, "filename": "logging.py"}, "_request_id": "46199a0b-c917-4221-b826-4380e919fd5c", "timestamp_f": 1561479377.730839, "host": "stackstorm.domain.tld", "full_message": "46199a0b-c917-4221-b826-4380e919fd5c - 200 inf 12.296ms", "_remote_addr": "127.0.0.1", "_runtime": 12.296, "_status": 200, "_method": "GET", "short_message": "46199a0b-c917-4221-b826-4380e919fd5c - 200 inf 12.296ms"}
st2stream.gelf.log:{"_path": "/v1/stream", "version": "1.1", "level": 6, "timestamp": 1561481182, "_content_length": Infinity, "_python": {"name": "st2.st2common.middleware.logging", "process": 81491, "module": "logging", "funcName": "__call__", "processName": "MainProcess", "lineno": 118, "filename": "logging.py"}, "_request_id": "60bdfd46-c49a-481e-a591-dc6a5ad7eeff", "timestamp_f": 1561481182.687065, "host": "stackstorm.domain.tld", "full_message": "60bdfd46-c49a-481e-a591-dc6a5ad7eeff - 200 inf 14.903ms", "_remote_addr": "127.0.0.1", "_runtime": 14.903, "_status": 200, "_method": "GET", "short_message": "60bdfd46-c49a-481e-a591-dc6a5ad7eeff - 200 inf 14.903ms"}
```

##### EXPECTED RESULTS

Valid JSON be written to these files so that it can be parsed from an external tool

##### ANALYSIS

The only place in the code where i can find `content_length` being set to Infinity is here: https://github.com/StackStorm/st2/blob/master/st2common/st2common/middleware/logging.py#L102


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `st2common/st2common/middleware/logging.py`
Content:
```
1 # Copyright 2019 Extreme Networks, Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 from __future__ import absolute_import
16 
17 import time
18 import types
19 import itertools
20 
21 from oslo_config import cfg
22 
23 from st2common.constants.api import REQUEST_ID_HEADER
24 from st2common.constants.auth import QUERY_PARAM_ATTRIBUTE_NAME
25 from st2common.constants.auth import QUERY_PARAM_API_KEY_ATTRIBUTE_NAME
26 from st2common.constants.secrets import MASKED_ATTRIBUTE_VALUE
27 from st2common.constants.secrets import MASKED_ATTRIBUTES_BLACKLIST
28 from st2common import log as logging
29 from st2common.router import Request, NotFoundException
30 
31 LOG = logging.getLogger(__name__)
32 
33 SECRET_QUERY_PARAMS = [
34     QUERY_PARAM_ATTRIBUTE_NAME,
35     QUERY_PARAM_API_KEY_ATTRIBUTE_NAME
36 ] + MASKED_ATTRIBUTES_BLACKLIST
37 
38 try:
39     clock = time.perf_counter
40 except AttributeError:
41     clock = time.time
42 
43 
44 class LoggingMiddleware(object):
45     """
46     Logs all incoming requests and outgoing responses
47     """
48 
49     def __init__(self, app, router):
50         self.app = app
51         self.router = router
52 
53     def __call__(self, environ, start_response):
54         start_time = clock()
55         status_code = []
56         content_length = []
57 
58         request = Request(environ)
59 
60         query_params = request.GET.dict_of_lists()
61 
62         # Mask secret / sensitive query params
63         secret_query_params = SECRET_QUERY_PARAMS + cfg.CONF.log.mask_secrets_blacklist
64         for param_name in secret_query_params:
65             if param_name in query_params:
66                 query_params[param_name] = MASKED_ATTRIBUTE_VALUE
67 
68         # Log the incoming request
69         values = {
70             'method': request.method,
71             'path': request.path,
72             'remote_addr': request.remote_addr,
73             'query': query_params,
74             'request_id': request.headers.get(REQUEST_ID_HEADER, None)
75         }
76 
77         LOG.info('%(request_id)s - %(method)s %(path)s with query=%(query)s' %
78                  values, extra=values)
79 
80         def custom_start_response(status, headers, exc_info=None):
81             status_code.append(int(status.split(' ')[0]))
82 
83             for name, value in headers:
84                 if name.lower() == 'content-length':
85                     content_length.append(int(value))
86                     break
87 
88             return start_response(status, headers, exc_info)
89 
90         retval = self.app(environ, custom_start_response)
91 
92         try:
93             endpoint, path_vars = self.router.match(request)
94         except NotFoundException:
95             endpoint = {}
96 
97         log_result = endpoint.get('x-log-result', True)
98 
99         if isinstance(retval, (types.GeneratorType, itertools.chain)):
100             # Note: We don't log the result when return value is a generator, because this would
101             # result in calling str() on the generator and as such, exhausting it
102             content_length = [float('inf')]
103             log_result = False
104 
105         # Log the response
106         values = {
107             'method': request.method,
108             'path': request.path,
109             'remote_addr': request.remote_addr,
110             'status': status_code[0],
111             'runtime': float("{0:.3f}".format((clock() - start_time) * 10**3)),
112             'content_length': content_length[0] if content_length else len(b''.join(retval)),
113             'request_id': request.headers.get(REQUEST_ID_HEADER, None)
114         }
115 
116         log_msg = '%(request_id)s - %(status)s %(content_length)s %(runtime)sms' % (values)
117         LOG.info(log_msg, extra=values)
118 
119         if log_result:
120             values['result'] = retval[0]
121             log_msg = ('%(request_id)s - %(status)s %(content_length)s %(runtime)sms\n%(result)s' %
122                       (values))
123             LOG.debug(log_msg, extra=values)
124 
125         return retval
126
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/st2common/st2common/middleware/logging.py b/st2common/st2common/middleware/logging.py
--- a/st2common/st2common/middleware/logging.py
+++ b/st2common/st2common/middleware/logging.py
@@ -99,7 +99,7 @@
         if isinstance(retval, (types.GeneratorType, itertools.chain)):
             # Note: We don't log the result when return value is a generator, because this would
             # result in calling str() on the generator and as such, exhausting it
-            content_length = [float('inf')]
+            content_length = [0]
             log_result = False
 
         # Log the response

{"golden_diff": "diff --git a/st2common/st2common/middleware/logging.py b/st2common/st2common/middleware/logging.py\n--- a/st2common/st2common/middleware/logging.py\n+++ b/st2common/st2common/middleware/logging.py\n@@ -99,7 +99,7 @@\n         if isinstance(retval, (types.GeneratorType, itertools.chain)):\n             # Note: We don't log the result when return value is a generator, because this would\n             # result in calling str() on the generator and as such, exhausting it\n-            content_length = [float('inf')]\n+            content_length = [0]\n             log_result = False\n \n         # Log the response\n", "issue": "Infinity values in log messages cause issues when parsing JSON\n##### SUMMARY\r\n\r\nWhen writing out structured log data using the `GelfFormatter`, periodically the field `_content_length` is output with a value of: `\"_content_length\": Infinity`. This is invalid JSON and causes an error when parsing from external logging tools such as FluentD\r\n\r\n##### ISSUE TYPE\r\n - Bug Report\r\n\r\n##### STACKSTORM VERSION\r\n```shell\r\nst2 3.0.1, on Python 2.7.5\r\n```\r\n\r\n\r\n##### OS / ENVIRONMENT / INSTALL METHOD\r\n```\r\nOS = Red Hat 7.6\r\nInstall = puppet-st2\r\n```\r\n\r\n##### STEPS TO REPRODUCE\r\nConfigure GELF logging as described here: https://stackstorm.com/2017/08/22/stackstorm-centralized-logging-graylog/\r\n\r\nMonitor the `/var/log/st2/st2stream.gelf.log` and periodically lines will be written out with `\"_content_length\": Infinity\"`\r\n\r\n```shell\r\nst2stream.gelf.log:{\"_path\": \"/v1/stream\", \"version\": \"1.1\", \"level\": 6, \"timestamp\": 1561477579, \"_content_length\": Infinity, \"_python\": {\"name\": \"st2.st2common.middleware.logging\", \"process\": 81491, \"module\": \"logging\", \"funcName\": \"__call__\", \"processName\": \"MainProcess\", \"lineno\": 118, \"filename\": \"logging.py\"}, \"_request_id\": \"2e85dac9-1cb2-46d0-969a-2c93efba10fa\", \"timestamp_f\": 1561477579.465585, \"host\": \"stackstorm.domain.tld\", \"full_message\": \"2e85dac9-1cb2-46d0-969a-2c93efba10fa - 200 inf 15.51ms\", \"_remote_addr\": \"127.0.0.1\", \"_runtime\": 15.51, \"_status\": 200, \"_method\": \"GET\", \"short_message\": \"2e85dac9-1cb2-46d0-969a-2c93efba10fa - 200 inf 15.51ms\"}\r\nst2stream.gelf.log:{\"_path\": \"/v1/stream\", \"version\": \"1.1\", \"level\": 6, \"timestamp\": 1561479377, \"_content_length\": Infinity, \"_python\": {\"name\": \"st2.st2common.middleware.logging\", \"process\": 81491, \"module\": \"logging\", \"funcName\": \"__call__\", \"processName\": \"MainProcess\", \"lineno\": 118, \"filename\": \"logging.py\"}, \"_request_id\": \"46199a0b-c917-4221-b826-4380e919fd5c\", \"timestamp_f\": 1561479377.730839, \"host\": \"stackstorm.domain.tld\", \"full_message\": \"46199a0b-c917-4221-b826-4380e919fd5c - 200 inf 12.296ms\", \"_remote_addr\": \"127.0.0.1\", \"_runtime\": 12.296, \"_status\": 200, \"_method\": \"GET\", \"short_message\": \"46199a0b-c917-4221-b826-4380e919fd5c - 200 inf 12.296ms\"}\r\nst2stream.gelf.log:{\"_path\": \"/v1/stream\", \"version\": \"1.1\", \"level\": 6, \"timestamp\": 1561481182, \"_content_length\": Infinity, \"_python\": {\"name\": \"st2.st2common.middleware.logging\", \"process\": 81491, \"module\": \"logging\", \"funcName\": \"__call__\", \"processName\": \"MainProcess\", \"lineno\": 118, \"filename\": \"logging.py\"}, \"_request_id\": \"60bdfd46-c49a-481e-a591-dc6a5ad7eeff\", \"timestamp_f\": 1561481182.687065, \"host\": \"stackstorm.domain.tld\", \"full_message\": \"60bdfd46-c49a-481e-a591-dc6a5ad7eeff - 200 inf 14.903ms\", \"_remote_addr\": \"127.0.0.1\", \"_runtime\": 14.903, \"_status\": 200, \"_method\": \"GET\", \"short_message\": \"60bdfd46-c49a-481e-a591-dc6a5ad7eeff - 200 inf 14.903ms\"}\r\n```\r\n\r\n##### EXPECTED RESULTS\r\n\r\nValid JSON be written to these files so that it can be parsed from an external tool\r\n\r\n##### ANALYSIS\r\n\r\nThe only place in the code where i can find `content_length` being set to Infinity is here: https://github.com/StackStorm/st2/blob/master/st2common/st2common/middleware/logging.py#L102\r\n\n", "before_files": [{"content": "# Copyright 2019 Extreme Networks, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\n\nimport time\nimport types\nimport itertools\n\nfrom oslo_config import cfg\n\nfrom st2common.constants.api import REQUEST_ID_HEADER\nfrom st2common.constants.auth import QUERY_PARAM_ATTRIBUTE_NAME\nfrom st2common.constants.auth import QUERY_PARAM_API_KEY_ATTRIBUTE_NAME\nfrom st2common.constants.secrets import MASKED_ATTRIBUTE_VALUE\nfrom st2common.constants.secrets import MASKED_ATTRIBUTES_BLACKLIST\nfrom st2common import log as logging\nfrom st2common.router import Request, NotFoundException\n\nLOG = logging.getLogger(__name__)\n\nSECRET_QUERY_PARAMS = [\n    QUERY_PARAM_ATTRIBUTE_NAME,\n    QUERY_PARAM_API_KEY_ATTRIBUTE_NAME\n] + MASKED_ATTRIBUTES_BLACKLIST\n\ntry:\n    clock = time.perf_counter\nexcept AttributeError:\n    clock = time.time\n\n\nclass LoggingMiddleware(object):\n    \"\"\"\n    Logs all incoming requests and outgoing responses\n    \"\"\"\n\n    def __init__(self, app, router):\n        self.app = app\n        self.router = router\n\n    def __call__(self, environ, start_response):\n        start_time = clock()\n        status_code = []\n        content_length = []\n\n        request = Request(environ)\n\n        query_params = request.GET.dict_of_lists()\n\n        # Mask secret / sensitive query params\n        secret_query_params = SECRET_QUERY_PARAMS + cfg.CONF.log.mask_secrets_blacklist\n        for param_name in secret_query_params:\n            if param_name in query_params:\n                query_params[param_name] = MASKED_ATTRIBUTE_VALUE\n\n        # Log the incoming request\n        values = {\n            'method': request.method,\n            'path': request.path,\n            'remote_addr': request.remote_addr,\n            'query': query_params,\n            'request_id': request.headers.get(REQUEST_ID_HEADER, None)\n        }\n\n        LOG.info('%(request_id)s - %(method)s %(path)s with query=%(query)s' %\n                 values, extra=values)\n\n        def custom_start_response(status, headers, exc_info=None):\n            status_code.append(int(status.split(' ')[0]))\n\n            for name, value in headers:\n                if name.lower() == 'content-length':\n                    content_length.append(int(value))\n                    break\n\n            return start_response(status, headers, exc_info)\n\n        retval = self.app(environ, custom_start_response)\n\n        try:\n            endpoint, path_vars = self.router.match(request)\n        except NotFoundException:\n            endpoint = {}\n\n        log_result = endpoint.get('x-log-result', True)\n\n        if isinstance(retval, (types.GeneratorType, itertools.chain)):\n            # Note: We don't log the result when return value is a generator, because this would\n            # result in calling str() on the generator and as such, exhausting it\n            content_length = [float('inf')]\n            log_result = False\n\n        # Log the response\n        values = {\n            'method': request.method,\n            'path': request.path,\n            'remote_addr': request.remote_addr,\n            'status': status_code[0],\n            'runtime': float(\"{0:.3f}\".format((clock() - start_time) * 10**3)),\n            'content_length': content_length[0] if content_length else len(b''.join(retval)),\n            'request_id': request.headers.get(REQUEST_ID_HEADER, None)\n        }\n\n        log_msg = '%(request_id)s - %(status)s %(content_length)s %(runtime)sms' % (values)\n        LOG.info(log_msg, extra=values)\n\n        if log_result:\n            values['result'] = retval[0]\n            log_msg = ('%(request_id)s - %(status)s %(content_length)s %(runtime)sms\\n%(result)s' %\n                      (values))\n            LOG.debug(log_msg, extra=values)\n\n        return retval\n", "path": "st2common/st2common/middleware/logging.py"}], "after_files": [{"content": "# Copyright 2019 Extreme Networks, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import absolute_import\n\nimport time\nimport types\nimport itertools\n\nfrom oslo_config import cfg\n\nfrom st2common.constants.api import REQUEST_ID_HEADER\nfrom st2common.constants.auth import QUERY_PARAM_ATTRIBUTE_NAME\nfrom st2common.constants.auth import QUERY_PARAM_API_KEY_ATTRIBUTE_NAME\nfrom st2common.constants.secrets import MASKED_ATTRIBUTE_VALUE\nfrom st2common.constants.secrets import MASKED_ATTRIBUTES_BLACKLIST\nfrom st2common import log as logging\nfrom st2common.router import Request, NotFoundException\n\nLOG = logging.getLogger(__name__)\n\nSECRET_QUERY_PARAMS = [\n    QUERY_PARAM_ATTRIBUTE_NAME,\n    QUERY_PARAM_API_KEY_ATTRIBUTE_NAME\n] + MASKED_ATTRIBUTES_BLACKLIST\n\ntry:\n    clock = time.perf_counter\nexcept AttributeError:\n    clock = time.time\n\n\nclass LoggingMiddleware(object):\n    \"\"\"\n    Logs all incoming requests and outgoing responses\n    \"\"\"\n\n    def __init__(self, app, router):\n        self.app = app\n        self.router = router\n\n    def __call__(self, environ, start_response):\n        start_time = clock()\n        status_code = []\n        content_length = []\n\n        request = Request(environ)\n\n        query_params = request.GET.dict_of_lists()\n\n        # Mask secret / sensitive query params\n        secret_query_params = SECRET_QUERY_PARAMS + cfg.CONF.log.mask_secrets_blacklist\n        for param_name in secret_query_params:\n            if param_name in query_params:\n                query_params[param_name] = MASKED_ATTRIBUTE_VALUE\n\n        # Log the incoming request\n        values = {\n            'method': request.method,\n            'path': request.path,\n            'remote_addr': request.remote_addr,\n            'query': query_params,\n            'request_id': request.headers.get(REQUEST_ID_HEADER, None)\n        }\n\n        LOG.info('%(request_id)s - %(method)s %(path)s with query=%(query)s' %\n                 values, extra=values)\n\n        def custom_start_response(status, headers, exc_info=None):\n            status_code.append(int(status.split(' ')[0]))\n\n            for name, value in headers:\n                if name.lower() == 'content-length':\n                    content_length.append(int(value))\n                    break\n\n            return start_response(status, headers, exc_info)\n\n        retval = self.app(environ, custom_start_response)\n\n        try:\n            endpoint, path_vars = self.router.match(request)\n        except NotFoundException:\n            endpoint = {}\n\n        log_result = endpoint.get('x-log-result', True)\n\n        if isinstance(retval, (types.GeneratorType, itertools.chain)):\n            # Note: We don't log the result when return value is a generator, because this would\n            # result in calling str() on the generator and as such, exhausting it\n            content_length = [0]\n            log_result = False\n\n        # Log the response\n        values = {\n            'method': request.method,\n            'path': request.path,\n            'remote_addr': request.remote_addr,\n            'status': status_code[0],\n            'runtime': float(\"{0:.3f}\".format((clock() - start_time) * 10**3)),\n            'content_length': content_length[0] if content_length else len(b''.join(retval)),\n            'request_id': request.headers.get(REQUEST_ID_HEADER, None)\n        }\n\n        log_msg = '%(request_id)s - %(status)s %(content_length)s %(runtime)sms' % (values)\n        LOG.info(log_msg, extra=values)\n\n        if log_result:\n            values['result'] = retval[0]\n            log_msg = ('%(request_id)s - %(status)s %(content_length)s %(runtime)sms\\n%(result)s' %\n                      (values))\n            LOG.debug(log_msg, extra=values)\n\n        return retval\n", "path": "st2common/st2common/middleware/logging.py"}]}