Coverage for nova/api/openstack/requestlog.py: 92%
43 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
1# Licensed under the Apache License, Version 2.0 (the "License");
2# you may not use this file except in compliance with the License.
3# You may obtain a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS,
9# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
10# implied.
11# See the License for the specific language governing permissions and
12# limitations under the License.
13"""Simple middleware for request logging."""
15import time
17from oslo_log import log as logging
18from oslo_utils import excutils
19import webob.dec
20import webob.exc
22from nova.api.openstack import wsgi
23from nova.api import wsgi as base_wsgi
24import nova.conf
26CONF = nova.conf.CONF
28# TODO(sdague) maybe we can use a better name here for the logger
29LOG = logging.getLogger(__name__)
32class RequestLog(base_wsgi.Middleware):
33 """WSGI Middleware to write a simple request log to.
35 Borrowed from Paste Translogger
36 """
38 # This matches the placement fil
39 _log_format = ('%(REMOTE_ADDR)s "%(REQUEST_METHOD)s %(REQUEST_URI)s" '
40 'status: %(status)s len: %(len)s '
41 'microversion: %(microversion)s time: %(time).6f')
43 @staticmethod
44 def _get_uri(environ):
45 req_uri = (environ.get('SCRIPT_NAME', '') +
46 environ.get('PATH_INFO', ''))
47 if environ.get('QUERY_STRING'): 47 ↛ 48line 47 didn't jump to line 48 because the condition on line 47 was never true
48 req_uri += '?' + environ['QUERY_STRING']
49 return req_uri
51 @staticmethod
52 def _should_emit(req):
53 """Conditions under which we should skip logging.
55 If we detect we are running under eventlet wsgi processing, we
56 already are logging things, let it go. This is broken out as a
57 separate method so that it can be easily adjusted for testing.
58 """
59 if req.environ.get('eventlet.input', None) is not None: 59 ↛ 60line 59 didn't jump to line 60 because the condition on line 59 was never true
60 return False
61 return True
63 def _log_req(self, req, res, start):
64 if not self._should_emit(req):
65 return
67 # in the event that things exploded really badly deeper in the
68 # wsgi stack, res is going to be an empty dict for the
69 # fallback logging. So never count on it having attributes.
70 status = getattr(res, "status", "500 Error").split(None, 1)[0]
72 remote_address = req.environ.get('REMOTE_ADDR', '-')
74 data = {
75 'REMOTE_ADDR': remote_address,
76 'REQUEST_METHOD': req.environ['REQUEST_METHOD'],
77 'REQUEST_URI': self._get_uri(req.environ),
78 'status': status,
79 'len': getattr(res, "content_length", 0),
80 'time': time.time() - start,
81 'microversion': '-'
82 }
83 # set microversion if it exists
84 if not req.api_version_request.is_null():
85 data["microversion"] = req.api_version_request.get_string()
86 LOG.info(self._log_format, data)
88 @webob.dec.wsgify(RequestClass=wsgi.Request)
89 def __call__(self, req):
90 res = {}
91 start = time.time()
92 try:
93 res = req.get_response(self.application)
94 self._log_req(req, res, start)
95 return res
96 except Exception:
97 with excutils.save_and_reraise_exception():
98 self._log_req(req, res, start)