Merge "Make statsd sample rate behave better."
This commit is contained in:
commit
23f33b2069
@ -416,7 +416,8 @@ configuration entries (see the sample configuration files)::
|
|||||||
|
|
||||||
log_statsd_host = localhost
|
log_statsd_host = localhost
|
||||||
log_statsd_port = 8125
|
log_statsd_port = 8125
|
||||||
log_statsd_default_sample_rate = 1
|
log_statsd_default_sample_rate = 1.0
|
||||||
|
log_statsd_sample_rate_factor = 1.0
|
||||||
log_statsd_metric_prefix = [empty-string]
|
log_statsd_metric_prefix = [empty-string]
|
||||||
|
|
||||||
If `log_statsd_host` is not set, this feature is disabled. The default values
|
If `log_statsd_host` is not set, this feature is disabled. The default values
|
||||||
@ -431,9 +432,24 @@ probability of sending a sample for any given event or timing measurement.
|
|||||||
This sample rate is sent with each sample to StatsD and used to
|
This sample rate is sent with each sample to StatsD and used to
|
||||||
multiply the value. For example, with a sample rate of 0.5, StatsD will
|
multiply the value. For example, with a sample rate of 0.5, StatsD will
|
||||||
multiply that counter's value by 2 when flushing the metric to an upstream
|
multiply that counter's value by 2 when flushing the metric to an upstream
|
||||||
monitoring system (Graphite_, Ganglia_, etc.). To get the best data, start
|
monitoring system (Graphite_, Ganglia_, etc.).
|
||||||
with the default `log_statsd_default_sample_rate` value of 1 and only lower
|
|
||||||
it as needed.
|
Some relatively high-frequency metrics have a default sample rate less than
|
||||||
|
one. If you want to override the default sample rate for all metrics whose
|
||||||
|
default sample rate is not specified in the Swift source, you may set
|
||||||
|
`log_statsd_default_sample_rate` to a value less than one. This is NOT
|
||||||
|
recommended (see next paragraph). A better way to reduce StatsD load is to
|
||||||
|
adjust `log_statsd_sample_rate_factor` to a value less than one. The
|
||||||
|
`log_statsd_sample_rate_factor` is multiplied to any sample rate (either the
|
||||||
|
global default or one specified by the actual metric logging call in the Swift
|
||||||
|
source) prior to handling. In other words, this one tunable can lower the
|
||||||
|
frequency of all StatsD logging by a proportional amount.
|
||||||
|
|
||||||
|
To get the best data, start with the default `log_statsd_default_sample_rate`
|
||||||
|
and `log_statsd_sample_rate_factor` values of 1 and only lower
|
||||||
|
`log_statsd_sample_rate_factor` if needed. The
|
||||||
|
`log_statsd_default_sample_rate` should not be used and remains for backward
|
||||||
|
compatibility only.
|
||||||
|
|
||||||
The metric prefix will be prepended to every metric sent to the StatsD server
|
The metric prefix will be prepended to every metric sent to the StatsD server
|
||||||
For example, with::
|
For example, with::
|
||||||
|
@ -24,7 +24,8 @@
|
|||||||
# You can enable StatsD logging here:
|
# You can enable StatsD logging here:
|
||||||
# log_statsd_host = localhost
|
# log_statsd_host = localhost
|
||||||
# log_statsd_port = 8125
|
# log_statsd_port = 8125
|
||||||
# log_statsd_default_sample_rate = 1
|
# log_statsd_default_sample_rate = 1.0
|
||||||
|
# log_statsd_sample_rate_factor = 1.0
|
||||||
# log_statsd_metric_prefix =
|
# log_statsd_metric_prefix =
|
||||||
# If you don't mind the extra disk space usage in overhead, you can turn this
|
# If you don't mind the extra disk space usage in overhead, you can turn this
|
||||||
# on to preallocate disk space with SQLite databases to decrease fragmentation.
|
# on to preallocate disk space with SQLite databases to decrease fragmentation.
|
||||||
|
@ -27,7 +27,8 @@
|
|||||||
# You can enable StatsD logging here:
|
# You can enable StatsD logging here:
|
||||||
# log_statsd_host = localhost
|
# log_statsd_host = localhost
|
||||||
# log_statsd_port = 8125
|
# log_statsd_port = 8125
|
||||||
# log_statsd_default_sample_rate = 1
|
# log_statsd_default_sample_rate = 1.0
|
||||||
|
# log_statsd_sample_rate_factor = 1.0
|
||||||
# log_statsd_metric_prefix =
|
# log_statsd_metric_prefix =
|
||||||
# If you don't mind the extra disk space usage in overhead, you can turn this
|
# If you don't mind the extra disk space usage in overhead, you can turn this
|
||||||
# on to preallocate disk space with SQLite databases to decrease fragmentation.
|
# on to preallocate disk space with SQLite databases to decrease fragmentation.
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
# You can enable StatsD logging here:
|
# You can enable StatsD logging here:
|
||||||
# log_statsd_host = localhost
|
# log_statsd_host = localhost
|
||||||
# log_statsd_port = 8125
|
# log_statsd_port = 8125
|
||||||
# log_statsd_default_sample_rate = 1
|
# log_statsd_default_sample_rate = 1.0
|
||||||
|
# log_statsd_sample_rate_factor = 1.0
|
||||||
# log_statsd_metric_prefix =
|
# log_statsd_metric_prefix =
|
||||||
|
|
||||||
[object-expirer]
|
[object-expirer]
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
# You can enable StatsD logging here:
|
# You can enable StatsD logging here:
|
||||||
# log_statsd_host = localhost
|
# log_statsd_host = localhost
|
||||||
# log_statsd_port = 8125
|
# log_statsd_port = 8125
|
||||||
# log_statsd_default_sample_rate = 1
|
# log_statsd_default_sample_rate = 1.0
|
||||||
|
# log_statsd_sample_rate_factor = 1.0
|
||||||
# log_statsd_metric_prefix =
|
# log_statsd_metric_prefix =
|
||||||
# eventlet_debug = false
|
# eventlet_debug = false
|
||||||
# You can set fallocate_reserve to the number of bytes you'd like fallocate to
|
# You can set fallocate_reserve to the number of bytes you'd like fallocate to
|
||||||
|
@ -26,7 +26,8 @@
|
|||||||
# You can enable StatsD logging here:
|
# You can enable StatsD logging here:
|
||||||
# log_statsd_host = localhost
|
# log_statsd_host = localhost
|
||||||
# log_statsd_port = 8125
|
# log_statsd_port = 8125
|
||||||
# log_statsd_default_sample_rate = 1
|
# log_statsd_default_sample_rate = 1.0
|
||||||
|
# log_statsd_sample_rate_factor = 1.0
|
||||||
# log_statsd_metric_prefix =
|
# log_statsd_metric_prefix =
|
||||||
# Use a comma separated list of full url (http://foo.bar:1234,https://foo.bar)
|
# Use a comma separated list of full url (http://foo.bar:1234,https://foo.bar)
|
||||||
# cors_allow_origin =
|
# cors_allow_origin =
|
||||||
@ -316,7 +317,8 @@ use = egg:swift#proxy_logging
|
|||||||
# You can use log_statsd_* from [DEFAULT] or override them here:
|
# You can use log_statsd_* from [DEFAULT] or override them here:
|
||||||
# access_log_statsd_host = localhost
|
# access_log_statsd_host = localhost
|
||||||
# access_log_statsd_port = 8125
|
# access_log_statsd_port = 8125
|
||||||
# access_log_statsd_default_sample_rate = 1
|
# access_log_statsd_default_sample_rate = 1.0
|
||||||
|
# access_log_statsd_sample_rate_factor = 1.0
|
||||||
# access_log_statsd_metric_prefix =
|
# access_log_statsd_metric_prefix =
|
||||||
# access_log_headers = False
|
# access_log_headers = False
|
||||||
# What HTTP methods are allowed for StatsD logging (comma-sep); request methods
|
# What HTTP methods are allowed for StatsD logging (comma-sep); request methods
|
||||||
|
@ -62,7 +62,7 @@ class AccountController(object):
|
|||||||
return AccountBroker(db_path, account=account, logger=self.logger)
|
return AccountBroker(db_path, account=account, logger=self.logger)
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def DELETE(self, req):
|
def DELETE(self, req):
|
||||||
"""Handle HTTP DELETE request."""
|
"""Handle HTTP DELETE request."""
|
||||||
try:
|
try:
|
||||||
@ -84,7 +84,7 @@ class AccountController(object):
|
|||||||
return HTTPNoContent(request=req)
|
return HTTPNoContent(request=req)
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def PUT(self, req):
|
def PUT(self, req):
|
||||||
"""Handle HTTP PUT request."""
|
"""Handle HTTP PUT request."""
|
||||||
try:
|
try:
|
||||||
@ -139,7 +139,7 @@ class AccountController(object):
|
|||||||
return HTTPAccepted(request=req)
|
return HTTPAccepted(request=req)
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def HEAD(self, req):
|
def HEAD(self, req):
|
||||||
"""Handle HTTP HEAD request."""
|
"""Handle HTTP HEAD request."""
|
||||||
# TODO(refactor): The account server used to provide a 'account and
|
# TODO(refactor): The account server used to provide a 'account and
|
||||||
@ -186,7 +186,7 @@ class AccountController(object):
|
|||||||
return HTTPNoContent(request=req, headers=headers, charset='utf-8')
|
return HTTPNoContent(request=req, headers=headers, charset='utf-8')
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def GET(self, req):
|
def GET(self, req):
|
||||||
"""Handle HTTP GET request."""
|
"""Handle HTTP GET request."""
|
||||||
try:
|
try:
|
||||||
@ -274,7 +274,7 @@ class AccountController(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def REPLICATE(self, req):
|
def REPLICATE(self, req):
|
||||||
"""
|
"""
|
||||||
Handle HTTP REPLICATE request.
|
Handle HTTP REPLICATE request.
|
||||||
@ -298,7 +298,7 @@ class AccountController(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def POST(self, req):
|
def POST(self, req):
|
||||||
"""Handle HTTP POST request."""
|
"""Handle HTTP POST request."""
|
||||||
try:
|
try:
|
||||||
|
@ -90,6 +90,7 @@ class ProxyLoggingMiddleware(object):
|
|||||||
for key in ('log_facility', 'log_name', 'log_level', 'log_udp_host',
|
for key in ('log_facility', 'log_name', 'log_level', 'log_udp_host',
|
||||||
'log_udp_port', 'log_statsd_host', 'log_statsd_port',
|
'log_udp_port', 'log_statsd_host', 'log_statsd_port',
|
||||||
'log_statsd_default_sample_rate',
|
'log_statsd_default_sample_rate',
|
||||||
|
'log_statsd_sample_rate_factor',
|
||||||
'log_statsd_metric_prefix'):
|
'log_statsd_metric_prefix'):
|
||||||
value = conf.get('access_' + key, conf.get(key, None))
|
value = conf.get('access_' + key, conf.get(key, None))
|
||||||
if value:
|
if value:
|
||||||
|
@ -421,12 +421,13 @@ class LoggerFileObject(object):
|
|||||||
|
|
||||||
class StatsdClient(object):
|
class StatsdClient(object):
|
||||||
def __init__(self, host, port, base_prefix='', tail_prefix='',
|
def __init__(self, host, port, base_prefix='', tail_prefix='',
|
||||||
default_sample_rate=1):
|
default_sample_rate=1, sample_rate_factor=1):
|
||||||
self._host = host
|
self._host = host
|
||||||
self._port = port
|
self._port = port
|
||||||
self._base_prefix = base_prefix
|
self._base_prefix = base_prefix
|
||||||
self.set_prefix(tail_prefix)
|
self.set_prefix(tail_prefix)
|
||||||
self._default_sample_rate = default_sample_rate
|
self._default_sample_rate = default_sample_rate
|
||||||
|
self._sample_rate_factor = sample_rate_factor
|
||||||
self._target = (self._host, self._port)
|
self._target = (self._host, self._port)
|
||||||
self.random = random
|
self.random = random
|
||||||
|
|
||||||
@ -443,6 +444,7 @@ class StatsdClient(object):
|
|||||||
def _send(self, m_name, m_value, m_type, sample_rate):
|
def _send(self, m_name, m_value, m_type, sample_rate):
|
||||||
if sample_rate is None:
|
if sample_rate is None:
|
||||||
sample_rate = self._default_sample_rate
|
sample_rate = self._default_sample_rate
|
||||||
|
sample_rate = sample_rate * self._sample_rate_factor
|
||||||
parts = ['%s%s:%s' % (self._prefix, m_name, m_value), m_type]
|
parts = ['%s%s:%s' % (self._prefix, m_name, m_value), m_type]
|
||||||
if sample_rate < 1:
|
if sample_rate < 1:
|
||||||
if self.random() < sample_rate:
|
if self.random() < sample_rate:
|
||||||
@ -474,25 +476,30 @@ class StatsdClient(object):
|
|||||||
sample_rate)
|
sample_rate)
|
||||||
|
|
||||||
|
|
||||||
def timing_stats(func):
|
def timing_stats(**dec_kwargs):
|
||||||
"""
|
"""
|
||||||
Decorator that logs timing events or errors for public methods in swift's
|
Returns a decorator that logs timing events or errors for public methods in
|
||||||
wsgi server controllers, based on response code.
|
swift's wsgi server controllers, based on response code.
|
||||||
"""
|
"""
|
||||||
method = func.func_name
|
def decorating_func(func):
|
||||||
|
method = func.func_name
|
||||||
|
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def _timing_stats(ctrl, *args, **kwargs):
|
def _timing_stats(ctrl, *args, **kwargs):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
resp = func(ctrl, *args, **kwargs)
|
resp = func(ctrl, *args, **kwargs)
|
||||||
if is_success(resp.status_int) or is_redirection(resp.status_int) or \
|
if is_success(resp.status_int) or \
|
||||||
resp.status_int == HTTP_NOT_FOUND:
|
is_redirection(resp.status_int) or \
|
||||||
ctrl.logger.timing_since(method + '.timing', start_time)
|
resp.status_int == HTTP_NOT_FOUND:
|
||||||
else:
|
ctrl.logger.timing_since(method + '.timing',
|
||||||
ctrl.logger.timing_since(method + '.errors.timing', start_time)
|
start_time, **dec_kwargs)
|
||||||
return resp
|
else:
|
||||||
|
ctrl.logger.timing_since(method + '.errors.timing',
|
||||||
|
start_time, **dec_kwargs)
|
||||||
|
return resp
|
||||||
|
|
||||||
return _timing_stats
|
return _timing_stats
|
||||||
|
return decorating_func
|
||||||
|
|
||||||
|
|
||||||
# double inheritance to support property with setter
|
# double inheritance to support property with setter
|
||||||
@ -600,14 +607,11 @@ class LogAdapter(logging.LoggerAdapter, object):
|
|||||||
|
|
||||||
def statsd_delegate(statsd_func_name):
|
def statsd_delegate(statsd_func_name):
|
||||||
"""
|
"""
|
||||||
Factory which creates methods which delegate to methods on
|
Factory to create methods which delegate to methods on
|
||||||
self.logger.statsd_client (an instance of StatsdClient). The
|
self.logger.statsd_client (an instance of StatsdClient). The
|
||||||
created methods conditionally delegate to a method whose name is given
|
created methods conditionally delegate to a method whose name is given
|
||||||
in 'statsd_func_name'. The created delegate methods are a no-op when
|
in 'statsd_func_name'. The created delegate methods are a no-op when
|
||||||
StatsD logging is not configured. The created delegate methods also
|
StatsD logging is not configured.
|
||||||
handle the defaulting of sample_rate (to either the default specified
|
|
||||||
in the config with 'log_statsd_default_sample_rate' or the value passed
|
|
||||||
into delegate function).
|
|
||||||
|
|
||||||
:param statsd_func_name: the name of a method on StatsdClient.
|
:param statsd_func_name: the name of a method on StatsdClient.
|
||||||
"""
|
"""
|
||||||
@ -665,7 +669,8 @@ def get_logger(conf, name=None, log_to_console=False, log_route=None,
|
|||||||
log_address = /dev/log
|
log_address = /dev/log
|
||||||
log_statsd_host = (disabled)
|
log_statsd_host = (disabled)
|
||||||
log_statsd_port = 8125
|
log_statsd_port = 8125
|
||||||
log_statsd_default_sample_rate = 1
|
log_statsd_default_sample_rate = 1.0
|
||||||
|
log_statsd_sample_rate_factor = 1.0
|
||||||
log_statsd_metric_prefix = (empty-string)
|
log_statsd_metric_prefix = (empty-string)
|
||||||
|
|
||||||
:param conf: Configuration dict to read settings from
|
:param conf: Configuration dict to read settings from
|
||||||
@ -697,7 +702,8 @@ def get_logger(conf, name=None, log_to_console=False, log_route=None,
|
|||||||
SysLogHandler.LOG_LOCAL0)
|
SysLogHandler.LOG_LOCAL0)
|
||||||
udp_host = conf.get('log_udp_host')
|
udp_host = conf.get('log_udp_host')
|
||||||
if udp_host:
|
if udp_host:
|
||||||
udp_port = conf.get('log_udp_port', logging.handlers.SYSLOG_UDP_PORT)
|
udp_port = int(conf.get('log_udp_port',
|
||||||
|
logging.handlers.SYSLOG_UDP_PORT))
|
||||||
handler = SysLogHandler(address=(udp_host, udp_port),
|
handler = SysLogHandler(address=(udp_host, udp_port),
|
||||||
facility=facility)
|
facility=facility)
|
||||||
else:
|
else:
|
||||||
@ -737,8 +743,11 @@ def get_logger(conf, name=None, log_to_console=False, log_route=None,
|
|||||||
base_prefix = conf.get('log_statsd_metric_prefix', '')
|
base_prefix = conf.get('log_statsd_metric_prefix', '')
|
||||||
default_sample_rate = float(conf.get(
|
default_sample_rate = float(conf.get(
|
||||||
'log_statsd_default_sample_rate', 1))
|
'log_statsd_default_sample_rate', 1))
|
||||||
|
sample_rate_factor = float(conf.get(
|
||||||
|
'log_statsd_sample_rate_factor', 1))
|
||||||
statsd_client = StatsdClient(statsd_host, statsd_port, base_prefix,
|
statsd_client = StatsdClient(statsd_host, statsd_port, base_prefix,
|
||||||
name, default_sample_rate)
|
name, default_sample_rate,
|
||||||
|
sample_rate_factor)
|
||||||
logger.statsd_client = statsd_client
|
logger.statsd_client = statsd_client
|
||||||
else:
|
else:
|
||||||
logger.statsd_client = None
|
logger.statsd_client = None
|
||||||
|
@ -163,7 +163,7 @@ class ContainerController(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def DELETE(self, req):
|
def DELETE(self, req):
|
||||||
"""Handle HTTP DELETE request."""
|
"""Handle HTTP DELETE request."""
|
||||||
try:
|
try:
|
||||||
@ -205,7 +205,7 @@ class ContainerController(object):
|
|||||||
return HTTPNotFound()
|
return HTTPNotFound()
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def PUT(self, req):
|
def PUT(self, req):
|
||||||
"""Handle HTTP PUT request."""
|
"""Handle HTTP PUT request."""
|
||||||
try:
|
try:
|
||||||
@ -268,7 +268,7 @@ class ContainerController(object):
|
|||||||
return HTTPAccepted(request=req)
|
return HTTPAccepted(request=req)
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats(sample_rate=0.1)
|
||||||
def HEAD(self, req):
|
def HEAD(self, req):
|
||||||
"""Handle HTTP HEAD request."""
|
"""Handle HTTP HEAD request."""
|
||||||
try:
|
try:
|
||||||
@ -306,7 +306,7 @@ class ContainerController(object):
|
|||||||
return HTTPNoContent(request=req, headers=headers, charset='utf-8')
|
return HTTPNoContent(request=req, headers=headers, charset='utf-8')
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def GET(self, req):
|
def GET(self, req):
|
||||||
"""Handle HTTP GET request."""
|
"""Handle HTTP GET request."""
|
||||||
try:
|
try:
|
||||||
@ -413,7 +413,7 @@ class ContainerController(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats(sample_rate=0.01)
|
||||||
def REPLICATE(self, req):
|
def REPLICATE(self, req):
|
||||||
"""
|
"""
|
||||||
Handle HTTP REPLICATE request (json-encoded RPC calls for replication.)
|
Handle HTTP REPLICATE request (json-encoded RPC calls for replication.)
|
||||||
@ -436,7 +436,7 @@ class ContainerController(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def POST(self, req):
|
def POST(self, req):
|
||||||
"""Handle HTTP POST request."""
|
"""Handle HTTP POST request."""
|
||||||
try:
|
try:
|
||||||
|
@ -563,7 +563,7 @@ class ObjectController(object):
|
|||||||
host, partition, contdevice, headers_out, objdevice)
|
host, partition, contdevice, headers_out, objdevice)
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def POST(self, request):
|
def POST(self, request):
|
||||||
"""Handle HTTP POST requests for the Swift Object Server."""
|
"""Handle HTTP POST requests for the Swift Object Server."""
|
||||||
try:
|
try:
|
||||||
@ -612,7 +612,7 @@ class ObjectController(object):
|
|||||||
return HTTPAccepted(request=request)
|
return HTTPAccepted(request=request)
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def PUT(self, request):
|
def PUT(self, request):
|
||||||
"""Handle HTTP PUT requests for the Swift Object Server."""
|
"""Handle HTTP PUT requests for the Swift Object Server."""
|
||||||
try:
|
try:
|
||||||
@ -710,7 +710,7 @@ class ObjectController(object):
|
|||||||
return resp
|
return resp
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def GET(self, request):
|
def GET(self, request):
|
||||||
"""Handle HTTP GET requests for the Swift Object Server."""
|
"""Handle HTTP GET requests for the Swift Object Server."""
|
||||||
try:
|
try:
|
||||||
@ -790,7 +790,7 @@ class ObjectController(object):
|
|||||||
return request.get_response(response)
|
return request.get_response(response)
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats(sample_rate=0.8)
|
||||||
def HEAD(self, request):
|
def HEAD(self, request):
|
||||||
"""Handle HTTP HEAD requests for the Swift Object Server."""
|
"""Handle HTTP HEAD requests for the Swift Object Server."""
|
||||||
try:
|
try:
|
||||||
@ -830,7 +830,7 @@ class ObjectController(object):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats()
|
||||||
def DELETE(self, request):
|
def DELETE(self, request):
|
||||||
"""Handle HTTP DELETE requests for the Swift Object Server."""
|
"""Handle HTTP DELETE requests for the Swift Object Server."""
|
||||||
try:
|
try:
|
||||||
@ -878,7 +878,7 @@ class ObjectController(object):
|
|||||||
return resp
|
return resp
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@timing_stats
|
@timing_stats(sample_rate=0.1)
|
||||||
def REPLICATE(self, request):
|
def REPLICATE(self, request):
|
||||||
"""
|
"""
|
||||||
Handle REPLICATE requests for the Swift Object Server. This is used
|
Handle REPLICATE requests for the Swift Object Server. This is used
|
||||||
|
@ -35,6 +35,7 @@ from shutil import rmtree
|
|||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from tempfile import TemporaryFile, NamedTemporaryFile
|
from tempfile import TemporaryFile, NamedTemporaryFile
|
||||||
|
from logging import handlers as logging_handlers
|
||||||
|
|
||||||
from eventlet import sleep
|
from eventlet import sleep
|
||||||
|
|
||||||
@ -378,6 +379,62 @@ class TestUtils(unittest.TestCase):
|
|||||||
self.assertEquals(sio.getvalue(),
|
self.assertEquals(sio.getvalue(),
|
||||||
'test1\ntest3\ntest4\ntest6\n')
|
'test1\ntest3\ntest4\ntest6\n')
|
||||||
|
|
||||||
|
def test_get_logger_sysloghandler_plumbing(self):
|
||||||
|
orig_sysloghandler = utils.SysLogHandler
|
||||||
|
syslog_handler_args = []
|
||||||
|
def syslog_handler_catcher(*args, **kwargs):
|
||||||
|
syslog_handler_args.append((args, kwargs))
|
||||||
|
return orig_sysloghandler(*args, **kwargs)
|
||||||
|
syslog_handler_catcher.LOG_LOCAL0 = orig_sysloghandler.LOG_LOCAL0
|
||||||
|
syslog_handler_catcher.LOG_LOCAL3 = orig_sysloghandler.LOG_LOCAL3
|
||||||
|
|
||||||
|
try:
|
||||||
|
utils.SysLogHandler = syslog_handler_catcher
|
||||||
|
logger = utils.get_logger({
|
||||||
|
'log_facility': 'LOG_LOCAL3',
|
||||||
|
}, 'server', log_route='server')
|
||||||
|
self.assertEquals([
|
||||||
|
((), {'address': '/dev/log',
|
||||||
|
'facility': orig_sysloghandler.LOG_LOCAL3})],
|
||||||
|
syslog_handler_args)
|
||||||
|
|
||||||
|
syslog_handler_args = []
|
||||||
|
logger = utils.get_logger({
|
||||||
|
'log_facility': 'LOG_LOCAL3',
|
||||||
|
'log_address': '/foo/bar',
|
||||||
|
}, 'server', log_route='server')
|
||||||
|
self.assertEquals([
|
||||||
|
((), {'address': '/foo/bar',
|
||||||
|
'facility': orig_sysloghandler.LOG_LOCAL3}),
|
||||||
|
# Second call is because /foo/bar didn't exist (and wasn't a
|
||||||
|
# UNIX domain socket).
|
||||||
|
((), {'facility': orig_sysloghandler.LOG_LOCAL3})],
|
||||||
|
syslog_handler_args)
|
||||||
|
|
||||||
|
# Using UDP with default port
|
||||||
|
syslog_handler_args = []
|
||||||
|
logger = utils.get_logger({
|
||||||
|
'log_udp_host': 'syslog.funtimes.com',
|
||||||
|
}, 'server', log_route='server')
|
||||||
|
self.assertEquals([
|
||||||
|
((), {'address': ('syslog.funtimes.com',
|
||||||
|
logging.handlers.SYSLOG_UDP_PORT),
|
||||||
|
'facility': orig_sysloghandler.LOG_LOCAL0})],
|
||||||
|
syslog_handler_args)
|
||||||
|
|
||||||
|
# Using UDP with non-default port
|
||||||
|
syslog_handler_args = []
|
||||||
|
logger = utils.get_logger({
|
||||||
|
'log_udp_host': 'syslog.funtimes.com',
|
||||||
|
'log_udp_port': '2123',
|
||||||
|
}, 'server', log_route='server')
|
||||||
|
self.assertEquals([
|
||||||
|
((), {'address': ('syslog.funtimes.com', 2123),
|
||||||
|
'facility': orig_sysloghandler.LOG_LOCAL0})],
|
||||||
|
syslog_handler_args)
|
||||||
|
finally:
|
||||||
|
utils.SysLogHandler = orig_sysloghandler
|
||||||
|
|
||||||
def test_clean_logger_exception(self):
|
def test_clean_logger_exception(self):
|
||||||
# setup stream logging
|
# setup stream logging
|
||||||
sio = StringIO()
|
sio = StringIO()
|
||||||
@ -1101,8 +1158,9 @@ class TestStatsdLogging(unittest.TestCase):
|
|||||||
def test_get_logger_statsd_client_non_defaults(self):
|
def test_get_logger_statsd_client_non_defaults(self):
|
||||||
logger = utils.get_logger({
|
logger = utils.get_logger({
|
||||||
'log_statsd_host': 'another.host.com',
|
'log_statsd_host': 'another.host.com',
|
||||||
'log_statsd_port': 9876,
|
'log_statsd_port': '9876',
|
||||||
'log_statsd_default_sample_rate': 0.75,
|
'log_statsd_default_sample_rate': '0.75',
|
||||||
|
'log_statsd_sample_rate_factor': '0.81',
|
||||||
'log_statsd_metric_prefix': 'tomato.sauce',
|
'log_statsd_metric_prefix': 'tomato.sauce',
|
||||||
}, 'some-name', log_route='some-route')
|
}, 'some-name', log_route='some-route')
|
||||||
self.assertEqual(logger.logger.statsd_client._prefix,
|
self.assertEqual(logger.logger.statsd_client._prefix,
|
||||||
@ -1116,6 +1174,8 @@ class TestStatsdLogging(unittest.TestCase):
|
|||||||
self.assertEqual(logger.logger.statsd_client._port, 9876)
|
self.assertEqual(logger.logger.statsd_client._port, 9876)
|
||||||
self.assertEqual(logger.logger.statsd_client._default_sample_rate,
|
self.assertEqual(logger.logger.statsd_client._default_sample_rate,
|
||||||
0.75)
|
0.75)
|
||||||
|
self.assertEqual(logger.logger.statsd_client._sample_rate_factor,
|
||||||
|
0.81)
|
||||||
|
|
||||||
def test_sample_rates(self):
|
def test_sample_rates(self):
|
||||||
logger = utils.get_logger({'log_statsd_host': 'some.host.com'})
|
logger = utils.get_logger({'log_statsd_host': 'some.host.com'})
|
||||||
@ -1138,6 +1198,42 @@ class TestStatsdLogging(unittest.TestCase):
|
|||||||
payload = mock_socket.sent[0][0]
|
payload = mock_socket.sent[0][0]
|
||||||
self.assertTrue(payload.endswith("|@0.5"))
|
self.assertTrue(payload.endswith("|@0.5"))
|
||||||
|
|
||||||
|
def test_sample_rates_with_sample_rate_factor(self):
|
||||||
|
logger = utils.get_logger({
|
||||||
|
'log_statsd_host': 'some.host.com',
|
||||||
|
'log_statsd_default_sample_rate': '0.82',
|
||||||
|
'log_statsd_sample_rate_factor': '0.91',
|
||||||
|
})
|
||||||
|
effective_sample_rate = 0.82 * 0.91
|
||||||
|
|
||||||
|
mock_socket = MockUdpSocket()
|
||||||
|
# encapsulation? what's that?
|
||||||
|
statsd_client = logger.logger.statsd_client
|
||||||
|
self.assertTrue(statsd_client.random is random.random)
|
||||||
|
|
||||||
|
statsd_client._open_socket = lambda *_: mock_socket
|
||||||
|
statsd_client.random = lambda: effective_sample_rate + 0.001
|
||||||
|
|
||||||
|
logger.increment('tribbles')
|
||||||
|
self.assertEqual(len(mock_socket.sent), 0)
|
||||||
|
|
||||||
|
statsd_client.random = lambda: effective_sample_rate - 0.001
|
||||||
|
logger.increment('tribbles')
|
||||||
|
self.assertEqual(len(mock_socket.sent), 1)
|
||||||
|
|
||||||
|
payload = mock_socket.sent[0][0]
|
||||||
|
self.assertTrue(payload.endswith("|@%s" % effective_sample_rate),
|
||||||
|
payload)
|
||||||
|
|
||||||
|
effective_sample_rate = 0.587 * 0.91
|
||||||
|
statsd_client.random = lambda: effective_sample_rate - 0.001
|
||||||
|
logger.increment('tribbles', sample_rate=0.587)
|
||||||
|
self.assertEqual(len(mock_socket.sent), 2)
|
||||||
|
|
||||||
|
payload = mock_socket.sent[1][0]
|
||||||
|
self.assertTrue(payload.endswith("|@%s" % effective_sample_rate),
|
||||||
|
payload)
|
||||||
|
|
||||||
def test_timing_stats(self):
|
def test_timing_stats(self):
|
||||||
class MockController(object):
|
class MockController(object):
|
||||||
def __init__(self, status):
|
def __init__(self, status):
|
||||||
@ -1150,7 +1246,7 @@ class TestStatsdLogging(unittest.TestCase):
|
|||||||
self.called = 'timing'
|
self.called = 'timing'
|
||||||
self.args = args
|
self.args = args
|
||||||
|
|
||||||
@utils.timing_stats
|
@utils.timing_stats()
|
||||||
def METHOD(controller):
|
def METHOD(controller):
|
||||||
return Response(status=controller.status)
|
return Response(status=controller.status)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user