diff options
author | Dolph Mathews <dolph.mathews@gmail.com> | 2013-01-29 11:12:35 -0600 |
---|---|---|
committer | Dolph Mathews <dolph.mathews@gmail.com> | 2013-01-31 08:16:21 -0600 |
commit | 378635224bdc88fef4e150405894be56acfbf0a3 (patch) | |
tree | 5750b43ec35f879dd0693a645135fca84ad9b9c9 | |
parent | 02da3afe4df65b8c469ceb430ca34dab83d6451c (diff) | |
download | keystone-378635224bdc88fef4e150405894be56acfbf0a3.tar.gz keystone-378635224bdc88fef4e150405894be56acfbf0a3.tar.xz keystone-378635224bdc88fef4e150405894be56acfbf0a3.zip |
Generate apache-style common access logs
Taking advantage of this middleware either requires enabling
verbose/debug or utilizing an external logging.conf which configures an
'access' logger.
Example output:
127.0.0.1 - - [2013-01-29T17:15:02.752214] "GET http://localhost:5000/v3/projects HTTP/1.0" 200 16
This patch also revises etc/logging.conf.sample with some more practical
defaults (e.g. supporting externally-managed log rotations) in addition
to illustrating how to generate an 'access.log' file.
DocImpact
Change-Id: I2a6048fa5fbf8661a6859d9e3a259d4cfa5fc589
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | etc/keystone.conf.sample | 13 | ||||
-rw-r--r-- | etc/logging.conf.sample | 50 | ||||
-rw-r--r-- | keystone/contrib/access/__init__.py | 17 | ||||
-rw-r--r-- | keystone/contrib/access/core.py | 61 | ||||
-rw-r--r-- | tests/test_keystoneclient.py | 4 |
6 files changed, 127 insertions, 19 deletions
@@ -23,6 +23,7 @@ AUTHORS build/ dist/ etc/keystone.conf +etc/logging.conf tests/test.db.pristine .project .pydevproject diff --git a/etc/keystone.conf.sample b/etc/keystone.conf.sample index 3b67b300..89ff9e6d 100644 --- a/etc/keystone.conf.sample +++ b/etc/keystone.conf.sample @@ -200,6 +200,9 @@ paste.filter_factory = keystone.contrib.stats:StatsMiddleware.factory [filter:stats_reporting] paste.filter_factory = keystone.contrib.stats:StatsExtension.factory +[filter:access_log] +paste.filter_factory = keystone.contrib.access:AccessLogMiddleware.factory + [app:public_service] paste.app_factory = keystone.service:public_app_factory @@ -210,13 +213,13 @@ paste.app_factory = keystone.service:v3_app_factory paste.app_factory = keystone.service:admin_app_factory [pipeline:public_api] -pipeline = sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug ec2_extension user_crud_extension public_service +pipeline = access_log sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug ec2_extension user_crud_extension public_service [pipeline:admin_api] -pipeline = sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug stats_reporting ec2_extension s3_extension crud_extension admin_service +pipeline = access_log sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug stats_reporting ec2_extension s3_extension crud_extension admin_service [pipeline:api_v3] -pipeline = sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug stats_reporting ec2_extension s3_extension service_v3 +pipeline = access_log sizelimit stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug stats_reporting ec2_extension s3_extension service_v3 [app:public_version_service] paste.app_factory = keystone.service:public_version_app_factory @@ -225,10 +228,10 @@ paste.app_factory = keystone.service:public_version_app_factory paste.app_factory = keystone.service:admin_version_app_factory [pipeline:public_version_api] -pipeline = sizelimit stats_monitoring url_normalize xml_body public_version_service +pipeline = access_log sizelimit stats_monitoring url_normalize xml_body public_version_service [pipeline:admin_version_api] -pipeline = sizelimit stats_monitoring url_normalize xml_body admin_version_service +pipeline = access_log sizelimit stats_monitoring url_normalize xml_body admin_version_service [composite:main] use = egg:Paste#urlmap diff --git a/etc/logging.conf.sample b/etc/logging.conf.sample index d87d3a28..6cb8c425 100644 --- a/etc/logging.conf.sample +++ b/etc/logging.conf.sample @@ -1,27 +1,48 @@ [loggers] -keys=root +keys=root,access + +[handlers] +keys=production,file,access_file,devel [formatters] -keys=normal,normal_with_name,debug +keys=minimal,normal,debug -[handlers] -keys=production,file,devel + +########### +# Loggers # +########### [logger_root] level=WARNING handlers=file +[logger_access] +level=INFO +qualname=access +handlers=access_file + + +################ +# Log Handlers # +################ + [handler_production] class=handlers.SysLogHandler level=ERROR -formatter=normal_with_name +formatter=normal args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER) [handler_file] -class=FileHandler -level=DEBUG -formatter=normal_with_name -args=('keystone.log', 'a') +class=handlers.WatchedFileHandler +level=WARNING +formatter=normal +args=('error.log',) + +[handler_access_file] +class=handlers.WatchedFileHandler +level=INFO +formatter=minimal +args=('access.log',) [handler_devel] class=StreamHandler @@ -29,10 +50,15 @@ level=NOTSET formatter=debug args=(sys.stdout,) -[formatter_normal] -format=%(asctime)s %(levelname)s %(message)s -[formatter_normal_with_name] +################## +# Log Formatters # +################## + +[formatter_minimal] +format=%(message)s + +[formatter_normal] format=(%(name)s): %(asctime)s %(levelname)s %(message)s [formatter_debug] diff --git a/keystone/contrib/access/__init__.py b/keystone/contrib/access/__init__.py new file mode 100644 index 00000000..4b782183 --- /dev/null +++ b/keystone/contrib/access/__init__.py @@ -0,0 +1,17 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 OpenStack LLC +# +# Licensed 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. + +from keystone.contrib.access.core import * diff --git a/keystone/contrib/access/core.py b/keystone/contrib/access/core.py new file mode 100644 index 00000000..f0221200 --- /dev/null +++ b/keystone/contrib/access/core.py @@ -0,0 +1,61 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 OpenStack LLC +# +# Licensed 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. + +import webob +import webob.dec + +from keystone.common import logging +from keystone.common import wsgi +from keystone import config +from keystone.openstack.common import timeutils + + +CONF = config.CONF +LOG = logging.getLogger('access') +APACHE_TIME_FORMAT = '%d/%b/%Y:%H:%M:%S' +APACHE_LOG_FORMAT = ( + '%(remote_addr)s - %(remote_user)s [%(datetime)s] "%(method)s %(url)s ' + '%(http_version)s" %(status)s %(content_length)s') + + +class AccessLogMiddleware(wsgi.Middleware): + """Writes an access log to INFO.""" + + @webob.dec.wsgify + def __call__(self, request): + data = { + 'remote_addr': request.remote_addr, + 'remote_user': request.remote_user or '-', + 'method': request.method, + 'url': request.url, + 'http_version': request.http_version, + 'status': 500, + 'content_length': '-'} + + try: + response = request.get_response(self.application) + data['status'] = response.status_int + data['content_length'] = len(response.body) or '-' + finally: + # must be calculated *after* the application has been called + now = timeutils.utcnow() + + # timeutils may not return UTC, so we can't hardcode +0000 + data['datetime'] = '%s %s' % (now.strftime(APACHE_TIME_FORMAT), + now.strftime('%z') or '+0000') + + LOG.info(APACHE_LOG_FORMAT % data) + return response diff --git a/tests/test_keystoneclient.py b/tests/test_keystoneclient.py index 32e9ef0a..b7ff1ee9 100644 --- a/tests/test_keystoneclient.py +++ b/tests/test_keystoneclient.py @@ -995,8 +995,8 @@ class KcMasterTestCase(CompatTestCase, KeystoneClientTests): rv = self.public_server.application( req.environ, responseobject.start_fake_response) - responce_json = jsonutils.loads(rv.next()) - new_token_id = responce_json['access']['token']['id'] + response_json = jsonutils.loads(rv.pop()) + new_token_id = response_json['access']['token']['id'] self.assertRaises(client_exceptions.Unauthorized, client.tenants.list) client.auth_token = new_token_id |