Merge "make statsd_client more explicit"

This commit is contained in:
Zuul 2024-05-16 19:39:51 +00:00 committed by Gerrit Code Review
commit d47811fc3e
3 changed files with 129 additions and 123 deletions

@ -15,12 +15,12 @@
""" Statsd Client """ """ Statsd Client """
import socket
import time import time
import warnings import warnings
from contextlib import closing from contextlib import closing
from random import random from random import random
from eventlet.green import socket
import six import six

@ -29,10 +29,10 @@ from mock import patch
from swift.common import utils from swift.common import utils
from swift.common.statsd_client import StatsdClient from swift.common import statsd_client
from test.debug_logger import debug_logger from test.debug_logger import debug_logger
from test.unit.common.test_utils import MockUdpSocket, reset_logger_state from test.unit.common.test_utils import MockUdpSocket
from swift.common.swob import Response from swift.common.swob import Response
@ -53,9 +53,9 @@ class TestStatsdLogging(unittest.TestCase):
'', '',
('127.0.0.1', port))] ('127.0.0.1', port))]
self.real_getaddrinfo = utils.socket.getaddrinfo self.real_getaddrinfo = statsd_client.socket.getaddrinfo
self.getaddrinfo_patcher = mock.patch.object( self.getaddrinfo_patcher = mock.patch.object(
utils.socket, 'getaddrinfo', fake_getaddrinfo) statsd_client.socket, 'getaddrinfo', fake_getaddrinfo)
self.mock_getaddrinfo = self.getaddrinfo_patcher.start() self.mock_getaddrinfo = self.getaddrinfo_patcher.start()
self.addCleanup(self.getaddrinfo_patcher.stop) self.addCleanup(self.getaddrinfo_patcher.stop)
@ -68,7 +68,8 @@ class TestStatsdLogging(unittest.TestCase):
logger = utils.get_logger({'log_statsd_host': 'some.host.com'}, logger = utils.get_logger({'log_statsd_host': 'some.host.com'},
'some-name', log_route='some-route') 'some-name', log_route='some-route')
# white-box construction validation # white-box construction validation
self.assertIsInstance(logger.logger.statsd_client, StatsdClient) self.assertIsInstance(logger.logger.statsd_client,
statsd_client.StatsdClient)
self.assertEqual(logger.logger.statsd_client._host, 'some.host.com') self.assertEqual(logger.logger.statsd_client._host, 'some.host.com')
self.assertEqual(logger.logger.statsd_client._port, 8125) self.assertEqual(logger.logger.statsd_client._port, 8125)
self.assertEqual(logger.logger.statsd_client._prefix, 'some-name.') self.assertEqual(logger.logger.statsd_client._prefix, 'some-name.')
@ -181,18 +182,18 @@ class TestStatsdLogging(unittest.TestCase):
return [(socket.AF_INET6, 'blah', 'blah', 'blah', return [(socket.AF_INET6, 'blah', 'blah', 'blah',
('::1', int(port), 0, 0))] ('::1', int(port), 0, 0))]
with mock.patch.object(utils.socket, 'getaddrinfo', with mock.patch.object(statsd_client.socket, 'getaddrinfo',
new=stub_getaddrinfo_both_ipv4_and_ipv6): new=stub_getaddrinfo_both_ipv4_and_ipv6):
logger = utils.get_logger({ logger = utils.get_logger({
'log_statsd_host': 'localhost', 'log_statsd_host': 'localhost',
'log_statsd_port': '9876', 'log_statsd_port': '9876',
}, 'some-name', log_route='some-route') }, 'some-name', log_route='some-route')
statsd_client = logger.logger.statsd_client client = logger.logger.statsd_client
self.assertEqual(statsd_client._sock_family, socket.AF_INET) self.assertEqual(client._sock_family, socket.AF_INET)
self.assertEqual(statsd_client._target, ('localhost', 9876)) self.assertEqual(client._target, ('localhost', 9876))
got_sock = statsd_client._open_socket() got_sock = client._open_socket()
self.assertEqual(got_sock.family, socket.AF_INET) self.assertEqual(got_sock.family, socket.AF_INET)
def test_ipv4_instantiation_and_socket_creation(self): def test_ipv4_instantiation_and_socket_creation(self):
@ -234,34 +235,35 @@ class TestStatsdLogging(unittest.TestCase):
'', '',
('::1', port, 0, 0))] ('::1', port, 0, 0))]
with mock.patch.object(utils.logs.socket, with mock.patch.object(statsd_client.socket,
'getaddrinfo', fake_getaddrinfo): 'getaddrinfo', fake_getaddrinfo):
logger = utils.get_logger({ logger = utils.get_logger({
'log_statsd_host': '::1', 'log_statsd_host': '::1',
'log_statsd_port': '9876', 'log_statsd_port': '9876',
}, 'some-name', log_route='some-route') }, 'some-name', log_route='some-route')
statsd_client = logger.logger.statsd_client client = logger.logger.statsd_client
self.assertEqual([socket.AF_INET, socket.AF_INET6], calls) self.assertEqual([socket.AF_INET, socket.AF_INET6], calls)
self.assertEqual(statsd_client._sock_family, socket.AF_INET6) self.assertEqual(client._sock_family, socket.AF_INET6)
self.assertEqual(statsd_client._target, ('::1', 9876, 0, 0)) self.assertEqual(client._target, ('::1', 9876, 0, 0))
got_sock = statsd_client._open_socket() got_sock = client._open_socket()
self.assertEqual(got_sock.family, socket.AF_INET6) self.assertEqual(got_sock.family, socket.AF_INET6)
def test_bad_hostname_instantiation(self): def test_bad_hostname_instantiation(self):
with mock.patch.object(utils.socket, 'getaddrinfo', stub_err = statsd_client.socket.gaierror('whoops')
side_effect=utils.socket.gaierror("whoops")): with mock.patch.object(statsd_client.socket, 'getaddrinfo',
side_effect=stub_err):
logger = utils.get_logger({ logger = utils.get_logger({
'log_statsd_host': 'i-am-not-a-hostname-or-ip', 'log_statsd_host': 'i-am-not-a-hostname-or-ip',
'log_statsd_port': '9876', 'log_statsd_port': '9876',
}, 'some-name', log_route='some-route') }, 'some-name', log_route='some-route')
statsd_client = logger.logger.statsd_client client = logger.logger.statsd_client
self.assertEqual(statsd_client._sock_family, socket.AF_INET) self.assertEqual(client._sock_family, socket.AF_INET)
self.assertEqual(statsd_client._target, self.assertEqual(client._target,
('i-am-not-a-hostname-or-ip', 9876)) ('i-am-not-a-hostname-or-ip', 9876))
got_sock = statsd_client._open_socket() got_sock = client._open_socket()
self.assertEqual(got_sock.family, socket.AF_INET) self.assertEqual(got_sock.family, socket.AF_INET)
# Maybe the DNS server gets fixed in a bit and it starts working... or # Maybe the DNS server gets fixed in a bit and it starts working... or
# maybe the DNS record hadn't propagated yet. In any case, failed # maybe the DNS record hadn't propagated yet. In any case, failed
@ -282,18 +284,19 @@ class TestStatsdLogging(unittest.TestCase):
'', '',
('::1', port, 0, 0))] ('::1', port, 0, 0))]
with mock.patch.object(utils.socket, 'getaddrinfo', fake_getaddrinfo): with mock.patch.object(statsd_client.socket, 'getaddrinfo',
fake_getaddrinfo):
logger = utils.get_logger({ logger = utils.get_logger({
'log_statsd_host': '::1', 'log_statsd_host': '::1',
'log_statsd_port': '9876', 'log_statsd_port': '9876',
}, 'some-name', log_route='some-route') }, 'some-name', log_route='some-route')
statsd_client = logger.logger.statsd_client client = logger.logger.statsd_client
fl = debug_logger() fl = debug_logger()
statsd_client.logger = fl client.logger = fl
mock_socket = MockUdpSocket() mock_socket = MockUdpSocket()
statsd_client._open_socket = lambda *_: mock_socket client._open_socket = lambda *_: mock_socket
logger.increment('tunafish') logger.increment('tunafish')
self.assertEqual(fl.get_lines_for_level('warning'), []) self.assertEqual(fl.get_lines_for_level('warning'), [])
self.assertEqual(mock_socket.sent, self.assertEqual(mock_socket.sent,
@ -301,11 +304,11 @@ class TestStatsdLogging(unittest.TestCase):
def test_no_exception_when_cant_send_udp_packet(self): def test_no_exception_when_cant_send_udp_packet(self):
logger = utils.get_logger({'log_statsd_host': 'some.host.com'}) logger = utils.get_logger({'log_statsd_host': 'some.host.com'})
statsd_client = logger.logger.statsd_client client = logger.logger.statsd_client
fl = debug_logger() fl = debug_logger()
statsd_client.logger = fl client.logger = fl
mock_socket = MockUdpSocket(sendto_errno=errno.EPERM) mock_socket = MockUdpSocket(sendto_errno=errno.EPERM)
statsd_client._open_socket = lambda *_: mock_socket client._open_socket = lambda *_: mock_socket
logger.increment('tunafish') logger.increment('tunafish')
expected = ["Error sending UDP message to ('some.host.com', 8125): " expected = ["Error sending UDP message to ('some.host.com', 8125): "
"[Errno 1] test errno 1"] "[Errno 1] test errno 1"]
@ -316,16 +319,16 @@ class TestStatsdLogging(unittest.TestCase):
mock_socket = MockUdpSocket() mock_socket = MockUdpSocket()
# encapsulation? what's that? # encapsulation? what's that?
statsd_client = logger.logger.statsd_client client = logger.logger.statsd_client
self.assertTrue(statsd_client.random is random.random) self.assertTrue(client.random is random.random)
statsd_client._open_socket = lambda *_: mock_socket client._open_socket = lambda *_: mock_socket
statsd_client.random = lambda: 0.50001 client.random = lambda: 0.50001
logger.increment('tribbles', sample_rate=0.5) logger.increment('tribbles', sample_rate=0.5)
self.assertEqual(len(mock_socket.sent), 0) self.assertEqual(len(mock_socket.sent), 0)
statsd_client.random = lambda: 0.49999 client.random = lambda: 0.49999
logger.increment('tribbles', sample_rate=0.5) logger.increment('tribbles', sample_rate=0.5)
self.assertEqual(len(mock_socket.sent), 1) self.assertEqual(len(mock_socket.sent), 1)
@ -342,16 +345,16 @@ class TestStatsdLogging(unittest.TestCase):
mock_socket = MockUdpSocket() mock_socket = MockUdpSocket()
# encapsulation? what's that? # encapsulation? what's that?
statsd_client = logger.logger.statsd_client client = logger.logger.statsd_client
self.assertTrue(statsd_client.random is random.random) self.assertTrue(client.random is random.random)
statsd_client._open_socket = lambda *_: mock_socket client._open_socket = lambda *_: mock_socket
statsd_client.random = lambda: effective_sample_rate + 0.001 client.random = lambda: effective_sample_rate + 0.001
logger.increment('tribbles') logger.increment('tribbles')
self.assertEqual(len(mock_socket.sent), 0) self.assertEqual(len(mock_socket.sent), 0)
statsd_client.random = lambda: effective_sample_rate - 0.001 client.random = lambda: effective_sample_rate - 0.001
logger.increment('tribbles') logger.increment('tribbles')
self.assertEqual(len(mock_socket.sent), 1) self.assertEqual(len(mock_socket.sent), 1)
@ -362,7 +365,7 @@ class TestStatsdLogging(unittest.TestCase):
self.assertTrue(payload.endswith(suffix), payload) self.assertTrue(payload.endswith(suffix), payload)
effective_sample_rate = 0.587 * 0.91 effective_sample_rate = 0.587 * 0.91
statsd_client.random = lambda: effective_sample_rate - 0.001 client.random = lambda: effective_sample_rate - 0.001
logger.increment('tribbles', sample_rate=0.587) logger.increment('tribbles', sample_rate=0.587)
self.assertEqual(len(mock_socket.sent), 2) self.assertEqual(len(mock_socket.sent), 2)
@ -707,86 +710,3 @@ class TestStatsdLoggingDelegation(unittest.TestCase):
self.assertStat('alpha.beta.another.counter:3|c|@0.9912', self.assertStat('alpha.beta.another.counter:3|c|@0.9912',
self.logger.update_stats, 'another.counter', 3, self.logger.update_stats, 'another.counter', 3,
sample_rate=0.9912) sample_rate=0.9912)
@reset_logger_state
def test_thread_locals(self):
logger = utils.get_logger(None)
# test the setter
logger.thread_locals = ('id', 'ip')
self.assertEqual(logger.thread_locals, ('id', 'ip'))
# reset
logger.thread_locals = (None, None)
self.assertEqual(logger.thread_locals, (None, None))
logger.txn_id = '1234'
logger.client_ip = '1.2.3.4'
self.assertEqual(logger.thread_locals, ('1234', '1.2.3.4'))
logger.txn_id = '5678'
logger.client_ip = '5.6.7.8'
self.assertEqual(logger.thread_locals, ('5678', '5.6.7.8'))
def test_no_fdatasync(self):
called = []
class NoFdatasync(object):
pass
def fsync(fd):
called.append(fd)
with patch('swift.common.utils.os', NoFdatasync()):
with patch('swift.common.utils.fsync', fsync):
utils.fdatasync(12345)
self.assertEqual(called, [12345])
def test_yes_fdatasync(self):
called = []
class YesFdatasync(object):
def fdatasync(self, fd):
called.append(fd)
with patch('swift.common.utils.os', YesFdatasync()):
utils.fdatasync(12345)
self.assertEqual(called, [12345])
def test_fsync_bad_fullsync(self):
class FCNTL(object):
F_FULLSYNC = 123
def fcntl(self, fd, op):
raise IOError(18)
with patch('swift.common.utils.fcntl', FCNTL()):
self.assertRaises(OSError, lambda: utils.fsync(12345))
def test_fsync_f_fullsync(self):
called = []
class FCNTL(object):
F_FULLSYNC = 123
def fcntl(self, fd, op):
called[:] = [fd, op]
return 0
with patch('swift.common.utils.fcntl', FCNTL()):
utils.fsync(12345)
self.assertEqual(called, [12345, 123])
def test_fsync_no_fullsync(self):
called = []
class FCNTL(object):
pass
def fsync(fd):
called.append(fd)
with patch('swift.common.utils.fcntl', FCNTL()):
with patch('os.fsync', fsync):
utils.fsync(12345)
self.assertEqual(called, [12345])

@ -5057,6 +5057,22 @@ class TestSwiftLoggerAdapter(unittest.TestCase):
self.assertEqual(logger.thread_locals, locals2) self.assertEqual(logger.thread_locals, locals2)
logger.thread_locals = (None, None) logger.thread_locals = (None, None)
@reset_logger_state
def test_thread_locals_more(self):
logger = utils.get_logger(None)
# test the setter
logger.thread_locals = ('id', 'ip')
self.assertEqual(logger.thread_locals, ('id', 'ip'))
# reset
logger.thread_locals = (None, None)
self.assertEqual(logger.thread_locals, (None, None))
logger.txn_id = '1234'
logger.client_ip = '1.2.3.4'
self.assertEqual(logger.thread_locals, ('1234', '1.2.3.4'))
logger.txn_id = '5678'
logger.client_ip = '5.6.7.8'
self.assertEqual(logger.thread_locals, ('5678', '5.6.7.8'))
def test_exception(self): def test_exception(self):
# verify that the adapter routes exception calls to utils.LogAdapter # verify that the adapter routes exception calls to utils.LogAdapter
# for special case handling # for special case handling
@ -8276,6 +8292,76 @@ class TestShardRangeList(unittest.TestCase):
do_test([utils.ShardRange.ACTIVE])) do_test([utils.ShardRange.ACTIVE]))
class TestFsync(unittest.TestCase):
def test_no_fdatasync(self):
called = []
class NoFdatasync(object):
pass
def fsync(fd):
called.append(fd)
with patch('swift.common.utils.os', NoFdatasync()):
with patch('swift.common.utils.fsync', fsync):
utils.fdatasync(12345)
self.assertEqual(called, [12345])
def test_yes_fdatasync(self):
called = []
class YesFdatasync(object):
def fdatasync(self, fd):
called.append(fd)
with patch('swift.common.utils.os', YesFdatasync()):
utils.fdatasync(12345)
self.assertEqual(called, [12345])
def test_fsync_bad_fullsync(self):
class FCNTL(object):
F_FULLSYNC = 123
def fcntl(self, fd, op):
raise IOError(18)
with patch('swift.common.utils.fcntl', FCNTL()):
self.assertRaises(OSError, lambda: utils.fsync(12345))
def test_fsync_f_fullsync(self):
called = []
class FCNTL(object):
F_FULLSYNC = 123
def fcntl(self, fd, op):
called[:] = [fd, op]
return 0
with patch('swift.common.utils.fcntl', FCNTL()):
utils.fsync(12345)
self.assertEqual(called, [12345, 123])
def test_fsync_no_fullsync(self):
called = []
class FCNTL(object):
pass
def fsync(fd):
called.append(fd)
with patch('swift.common.utils.fcntl', FCNTL()):
with patch('os.fsync', fsync):
utils.fsync(12345)
self.assertEqual(called, [12345])
@patch('ctypes.get_errno') @patch('ctypes.get_errno')
@patch.object(utils, '_sys_posix_fallocate') @patch.object(utils, '_sys_posix_fallocate')
@patch.object(utils, '_sys_fallocate') @patch.object(utils, '_sys_fallocate')