s3api: fix statsd prefix mutation
The 'log_route' argument of utils.get_logger() determines which global Logger instance is wrapped by the returned LogAdapter. Most middlewares (s3api being the exception) explicity set 'log_route' to equal the middleware 'brief' name e.g. 'bulk', 'tempauth' etc. However, the s3api middleware sets 'log_route' to be the config 'log_name', if that key is found in config. When a proxy pipeline is instantiated via wsgi.run_wsgi(), all middlewares and the proxy app are passed a default conf with '"log_name": "proxy-server"'. As a result, the s3api middleware calls get_logger() with log_route='proxy-server' and its LogAdapter therefore shares the same Logger instance used by proxy-server app (and any other middleware that similarly fails to explicitly differentiate 'log_route)'. Each Logger instance has a StatsdClient instance bound to it by get_logger(). The Related-Change added statsd metrics to the s3api middleware and sets 's3api' as the 'statsd_tail_prefix' when calling get_logger(). This had the unintended effect of replacing the shared Logger instance's StatsdClient with one that has prefix 's3api', such that stats emitted by the proxy app (e.g. memcache shard range hit/miss stats) would be erroneously prefixed with 's3api'. This patch modifies the s3api middleware logger instantiation to explictly set log_route='s3api', so that the s3api middleware LogAdapter now wraps a unique global Logger instance, with a unique StatsdClient instance bound to it. The 'server' attribute of the middleware's LogAdapter, which may be included in log lines by the "%(server)s" format element, is not affected by this change. Its value is derived from the config 'log_name' or the 'name' argument passed to get_logger(). Change-Id: Ia89485bae8f92f4f3d9f5375cab8ff08f70a11a7 Related-Change: I4976b3ee24e4ec498c66359f391813261d42c495
This commit is contained in:
parent
7cc8a01729
commit
bdbe8ce9f8
@ -293,8 +293,7 @@ class S3ApiMiddleware(object):
|
||||
wsgi_conf.get('ratelimit_as_client_error', False))
|
||||
|
||||
self.logger = get_logger(
|
||||
wsgi_conf, log_route=wsgi_conf.get('log_name', 's3api'),
|
||||
statsd_tail_prefix='s3api')
|
||||
wsgi_conf, log_route='s3api', statsd_tail_prefix='s3api')
|
||||
self.check_pipeline(wsgi_conf)
|
||||
|
||||
def is_s3_cors_preflight(self, env):
|
||||
|
@ -32,7 +32,7 @@ from swift.common.middleware.s3api.utils import Config
|
||||
from swift.common.middleware.keystoneauth import KeystoneAuth
|
||||
from swift.common import swob, registry
|
||||
from swift.common.swob import Request
|
||||
from swift.common.utils import md5
|
||||
from swift.common.utils import md5, get_logger
|
||||
|
||||
from keystonemiddleware.auth_token import AuthProtocol
|
||||
from keystoneauth1.access import AccessInfoV2
|
||||
@ -214,6 +214,24 @@ class TestS3ApiMiddleware(S3ApiTestCase):
|
||||
loader.assert_called_with(loadwsgi.APP, 'proxy-conf-file')
|
||||
pipeline.assert_called_with(context)
|
||||
|
||||
def test_init_logger(self):
|
||||
proxy_logger = get_logger({}, log_route='proxy-server').logger
|
||||
|
||||
s3api = S3ApiMiddleware(None, {})
|
||||
self.assertEqual('s3api', s3api.logger.name)
|
||||
self.assertEqual('s3api', s3api.logger.logger.name)
|
||||
self.assertIsNot(s3api.logger.logger, proxy_logger)
|
||||
self.assertEqual('swift', s3api.logger.server)
|
||||
self.assertIsNone(s3api.logger.logger.statsd_client)
|
||||
|
||||
s3api = S3ApiMiddleware(None, {'log_name': 'proxy-server',
|
||||
'log_statsd_host': '1.2.3.4'})
|
||||
self.assertEqual('s3api', s3api.logger.name)
|
||||
self.assertEqual('s3api', s3api.logger.logger.name)
|
||||
self.assertIsNot(s3api.logger.logger, proxy_logger)
|
||||
self.assertEqual('proxy-server', s3api.logger.server)
|
||||
self.assertEqual('s3api.', s3api.logger.logger.statsd_client._prefix)
|
||||
|
||||
def test_non_s3_request_passthrough(self):
|
||||
req = Request.blank('/something')
|
||||
status, headers, body = self.call_s3api(req)
|
||||
|
@ -73,7 +73,8 @@ from swift.common.exceptions import Timeout, MessageTimeout, \
|
||||
ConnectionTimeout, LockTimeout, ReplicationLockTimeout, \
|
||||
MimeInvalid
|
||||
from swift.common import utils
|
||||
from swift.common.utils import set_swift_dir, md5, ShardRangeList
|
||||
from swift.common.utils import set_swift_dir, md5, ShardRangeList, \
|
||||
SwiftLogFormatter
|
||||
from swift.common.container_sync_realms import ContainerSyncRealms
|
||||
from swift.common.header_key_dict import HeaderKeyDict
|
||||
from swift.common.storage_policy import POLICIES, reload_storage_policies
|
||||
@ -989,25 +990,108 @@ class TestUtils(unittest.TestCase):
|
||||
'test1\ntest3\ntest4\ntest6\n')
|
||||
|
||||
def test_get_logger_name_and_route(self):
|
||||
@contextlib.contextmanager
|
||||
def add_log_handler(logger):
|
||||
# install a handler to capture log messages formatted as per swift
|
||||
sio = StringIO()
|
||||
handler = logging.StreamHandler(sio)
|
||||
handler.setFormatter(SwiftLogFormatter(
|
||||
fmt="%(server)s: %(message)s", max_line_length=20)
|
||||
)
|
||||
logger.logger.addHandler(handler)
|
||||
yield sio
|
||||
logger.logger.removeHandler(handler)
|
||||
|
||||
logger = utils.get_logger({}, name='name', log_route='route')
|
||||
# log_route becomes the LogAdapter.name and logging.Logger.name
|
||||
self.assertEqual('route', logger.name)
|
||||
self.assertEqual('route', logger.logger.name)
|
||||
# name becomes the LogAdapter.server!
|
||||
self.assertEqual('name', logger.server)
|
||||
# LogAdapter.server is used when formatting a log message
|
||||
with add_log_handler(logger) as sio:
|
||||
logger.info('testing')
|
||||
self.assertEqual('name: testing\n', sio.getvalue())
|
||||
|
||||
logger = utils.get_logger({'log_name': 'conf-name'}, name='name',
|
||||
log_route='route')
|
||||
self.assertEqual('route', logger.name)
|
||||
self.assertEqual('name', logger.server)
|
||||
with add_log_handler(logger) as sio:
|
||||
logger.info('testing')
|
||||
self.assertEqual('name: testing\n', sio.getvalue())
|
||||
|
||||
logger = utils.get_logger({'log_name': 'conf-name'}, log_route='route')
|
||||
self.assertEqual('route', logger.name)
|
||||
self.assertEqual('conf-name', logger.server)
|
||||
with add_log_handler(logger) as sio:
|
||||
logger.info('testing')
|
||||
self.assertEqual('conf-name: testing\n', sio.getvalue())
|
||||
|
||||
logger = utils.get_logger({'log_name': 'conf-name'})
|
||||
self.assertEqual('conf-name', logger.name)
|
||||
self.assertEqual('conf-name', logger.server)
|
||||
with add_log_handler(logger) as sio:
|
||||
logger.info('testing')
|
||||
self.assertEqual('conf-name: testing\n', sio.getvalue())
|
||||
|
||||
logger = utils.get_logger({})
|
||||
self.assertEqual('swift', logger.name)
|
||||
self.assertEqual('swift', logger.server)
|
||||
with add_log_handler(logger) as sio:
|
||||
logger.info('testing')
|
||||
self.assertEqual('swift: testing\n', sio.getvalue())
|
||||
|
||||
logger = utils.get_logger({}, log_route='route')
|
||||
self.assertEqual('route', logger.name)
|
||||
self.assertEqual('swift', logger.server)
|
||||
with add_log_handler(logger) as sio:
|
||||
logger.info('testing')
|
||||
self.assertEqual('swift: testing\n', sio.getvalue())
|
||||
|
||||
# same log_route, different names...
|
||||
logger1 = utils.get_logger({'log_statsd_host': '1.2.3.4'},
|
||||
name='name1', log_route='route')
|
||||
logger2 = utils.get_logger({'log_statsd_host': '1.2.3.5'},
|
||||
name='name2', log_route='route')
|
||||
self.assertEqual('route', logger1.name)
|
||||
self.assertEqual('route', logger1.logger.name)
|
||||
self.assertEqual('name1', logger1.server)
|
||||
# oh dear, the statsd client on the common logging.Logger instance got
|
||||
# mutated when logger2 was created
|
||||
self.assertEqual('name2.', logger1.logger.statsd_client._prefix)
|
||||
self.assertEqual('route', logger2.name)
|
||||
self.assertEqual('route', logger2.logger.name)
|
||||
self.assertEqual('name2', logger2.server)
|
||||
self.assertEqual('name2.', logger2.logger.statsd_client._prefix)
|
||||
self.assertIs(logger2.logger, logger1.logger)
|
||||
with add_log_handler(logger1) as sio:
|
||||
logger1.info('testing')
|
||||
self.assertEqual('name1: testing\n', sio.getvalue())
|
||||
with add_log_handler(logger2) as sio:
|
||||
logger2.info('testing')
|
||||
self.assertEqual('name2: testing\n', sio.getvalue())
|
||||
|
||||
# different log_route, different names...
|
||||
logger1 = utils.get_logger({'log_statsd_host': '1.2.3.4'},
|
||||
name='name1', log_route='route1')
|
||||
logger2 = utils.get_logger({'log_statsd_host': '1.2.3.5'},
|
||||
name='name2', log_route='route2')
|
||||
self.assertEqual('route1', logger1.name)
|
||||
self.assertEqual('route1', logger1.logger.name)
|
||||
self.assertEqual('name1', logger1.server)
|
||||
self.assertEqual('name1.', logger1.logger.statsd_client._prefix)
|
||||
self.assertEqual('route2', logger2.name)
|
||||
self.assertEqual('route2', logger2.logger.name)
|
||||
self.assertEqual('name2', logger2.server)
|
||||
self.assertEqual('name2.', logger2.logger.statsd_client._prefix)
|
||||
self.assertIsNot(logger2.logger, logger1.logger)
|
||||
with add_log_handler(logger1) as sio:
|
||||
logger1.info('testing')
|
||||
self.assertEqual('name1: testing\n', sio.getvalue())
|
||||
with add_log_handler(logger2) as sio:
|
||||
logger2.info('testing')
|
||||
self.assertEqual('name2: testing\n', sio.getvalue())
|
||||
|
||||
@with_tempdir
|
||||
def test_get_logger_sysloghandler_plumbing(self, tempdir):
|
||||
|
Loading…
Reference in New Issue
Block a user