nameko-statsd
A StatsD dependency for nameko, enabling services to send stats.
Usage
To use the dependency you simply declare it on the service and then you can use it within any of the service methods (entrypoints, simple methods, etc.).
from nameko_statsd import StatsD, ServiceBase
class Service(ServiceBase):
statsd = StatsD('prod1')
@entrypoint
@statsd.timer('process_data')
def process_data(self):
...
@rpc
def get_data(self):
self.statsd.incr('get_data')
...
def simple_method(self, value):
self.statsd.gauge(value)
...
The statsd.StatsClient
instance exposes a set of methods that you can
access without having to go through the client itself. The dependency
acts as a pass-through for them. They are: incr
, decr
, gauge
,
set
, and timing
.
In the above code example, you can see how we access incr
and gauge
.
You can also decorate any method in the service with the timer
decorator,
as shown in the example. This allows you to time any method without having
to change its logic.
Configuration
The library expects the following values to be in the config file you use for your service (you need one configuration block per different statsd server). For example, if we had two statsd servers, prod1 and prod2, we would have something like this:
STATSD:
prod1:
host: "host1"
port: 8125
prefix: "prefix-1"
maxudpsize: 512
enabled: true
prod2:
host: "host2"
port: 8125
prefix: "prefix-2"
maxudpsize: 512
enabled: false
The first four values are passed directly to statsd.StatsClient
on
creation. The last one, enabled
, will activate/deactivate all stats,
according to how it is set (true
/false
). In this example,
production 1 is enabled while production 2 is not.
Minimum setup
At the time of writing a Nameko service, you don’t have access to the config values. This means that when we write our service we don’t have access to the actual dependencies (they are injected later).
In order to give the users of this library the ability to decorate
methods with the timer
decorator, we need to do a little wiring
behind the scenes. The only thing required for the end user is to write
the service class so that it inherits from nameko_statsd.ServiceBase
.
The type of nameko_statsd.ServiceBase
is a custom metaclass that
provides the necessary wirings to any nameko_statsd.StatsD
dependency.
If you cannot inherit from nameko_statsd.ServiceBase
for any reason,
all you have to do is to make sure you pass a name
argument to any
nameko_statsd.StatsD
dependency, the value of which has to match the
attribute name of the dependency itself.
The following configuration:
class MyService(ServiceBase):
statsd = StatsD('prod1')
...
is equivalent to (notice it inherits from object
):
class MyService(object):
statsd = StatsD('prod1', name='statsd')
...
The StatsD.timer
decorator
You can pass any arguments to the decorator, they will be given to the
statsd.StatsClient().timer
decorator.
So, for example:
class MyService(ServiceBase):
statsd = StatsD('prod1')
@entrypoint
@statsd.timer('my_stat', rate=5)
def method(...):
# method body
@statsd.timer('another-stat')
def another_method(...):
# method body
is equivalent to the following:
class MyService(ServiceBase):
statsd = StatsD('prod1')
@entrypoint
def method(...):
with self.statsd.client.timer('my_stat', rate=5):
# method body
def another_method(...):
with self.statsd.client.timer('another-stat'):
# method body
About the lazy client
When you attach a nameko_statsd.StatsD
dependency to your service, no
client is created. Only when you use the dependency explicitly or when
you run a method that has been decorated with the timer
decorator,
a client is created.
This lazy feature means you can attach as many nameko_statsd.StatsD
dependencies to your service as you fancy, and no client will be created
unless it is actually used.