Top

libhoney module

libhoney is a library to allow you to send events to Honeycomb from within your python application.

Basic usage:

  • initialize libhoney with your Honeycomb writekey and dataset name
  • create an event object and populate it with fields
  • send the event object
  • close libhoney when your program is finished

Sending on a closed or uninitialized libhoney will throw a SendError exception.

You can find an example demonstrating usage in example.py

'''libhoney is a library to allow you to send events to Honeycomb from within
your python application.

Basic usage:

- initialize libhoney with your Honeycomb writekey and dataset name
- create an event object and populate it with fields
- send the event object
- close libhoney when your program is finished

Sending on a closed or uninitialized libhoney will throw a `libhoney.SendError`
exception.

You can find an example demonstrating usage in example.py'''

import atexit
import random
from six.moves.queue import Queue

import libhoney.state as state
from libhoney.client import Client
from libhoney.builder import Builder
from libhoney.event import Event
from libhoney.fields import FieldHolder
from libhoney.errors import SendError

random.seed()


def init(writekey="", dataset="", sample_rate=1,
         api_host="https://api.honeycomb.io", max_concurrent_batches=10,
         max_batch_size=100, send_frequency=0.25,
         block_on_send=False, block_on_response=False, transmission_impl=None,
         debug=False):
    '''Initialize libhoney and prepare it to send events to Honeycomb. This creates
    a global Client object that is configured with the supplied parameters. For some
    advanced used cases, you might consider creating a Client object directly, but
    `init` is the quickest path to getting data into Honeycomb.

    Note that libhoney initialization initializes a number of threads to handle
    sending payloads to Honeycomb. Be mindful of where you're calling
    `libhoney.init()` in order to ensure correct enqueueing + processing of
    events on the spawned threads.

    Args:

    - `writekey`: the authorization key for your team on Honeycomb. Find your team
            write key at [https://ui.honeycomb.io/account](https://ui.honeycomb.io/account)
    - `dataset`: the name of the default dataset to which to write
    - `sample_rate`: the default sample rate. 1 / `sample_rate` events will be sent.
    - `max_concurrent_batches`: the maximum number of concurrent threads sending events.
    - `max_batch_size`: the maximum number of events to batch before sendinga.
    - `send_frequency`: how long to wait before sending a batch of events, in seconds.
    - `block_on_send`: if true, block when send queue fills. If false, drop
            events until there's room in the queue
    - `block_on_response`: if true, block when the response queue fills. If
            false, drop response objects.
    - `transmission_impl`: if set, override the default transmission implementation (for example, TornadoTransmission)

    --------

    **Configuration recommendations**:

    **For gunicorn**, use a [`post_worker_init` config hook](http://docs.gunicorn.org/en/stable/settings.html#post-worker-init) to initialize Honeycomb:

        # conf.py
        import logging
        import os

        def post_worker_init(worker):
            logging.info(f'libhoney initialization in process pid {os.getpid()}')
            libhoney.init(writekey="YOUR_WRITE_KEY", dataset="dataset_name")

    Then start gunicorn with the `-c` option:

        gunicorn -c /path/to/conf.py
    '''
    state.G_CLIENT = Client(
        writekey=writekey,
        dataset=dataset,
        sample_rate=sample_rate,
        api_host=api_host,
        max_concurrent_batches=max_concurrent_batches,
        max_batch_size=max_batch_size,
        send_frequency=send_frequency,
        block_on_send=block_on_send,
        block_on_response=block_on_response,
        transmission_impl=transmission_impl,
        debug=debug,
    )


def responses():
    '''Returns a queue from which you can read a record of response info from
    each event sent by the global client. Responses will be dicts with the
    following keys:

    - `status_code` - the HTTP response from the api (eg. 200 or 503)
    - `duration` - how long it took to POST this event to the api, in ms
    - `metadata` - pass through the metadata you added on the initial event
    - `body` - the content returned by API (will be empty on success)
    - `error` - in an error condition, this is filled with the error message

    When a None object appears on the queue the reader should exit'''
    if state.G_CLIENT is None:
        state.warn_uninitialized()
        # return an empty queue rather than None. While not ideal, it is
        # better than returning None and introducing AttributeErrors into
        # the caller's code
        return Queue()

    return state.G_CLIENT.responses()


def add_field(name, val):
    '''Add a field to the global client. This field will be sent with every event.'''
    if state.G_CLIENT is None:
        state.warn_uninitialized()
        return
    state.G_CLIENT.add_field(name, val)


def add_dynamic_field(fn):
    '''Add a dynamic field to the global client. This function will be executed every time an
       event is created. The key/value pair of the function's name and its
       return value will be sent with every event.'''
    if state.G_CLIENT is None:
        state.warn_uninitialized()
        return
    state.G_CLIENT.add_dynamic_field(fn)


def add(data):
    '''Add takes a mappable object and adds each key/value pair to the global client.
    These key/value pairs will be sent with every event created by the global client.'''
    if state.G_CLIENT is None:
        state.warn_uninitialized()
        return
    state.G_CLIENT.add(data)

def new_event(data={}):
    ''' Creates a new event with the global client. If libhoney has not been
    initialized, sending this event will be a no-op.
    '''
    return Event(data=data, client=state.G_CLIENT)

def send_now(data):
    '''
    DEPRECATED - This will likely be removed in a future major version.

    Creates an event with the data passed in and enqueues it to be sent.
    Contrary to the name, it does not block the application when called.

    Shorthand for:

        ev = libhoney.Event()
        ev.add(data)
        ev.send()
    '''
    if state.G_CLIENT is None:
        state.warn_uninitialized()
        return
    ev = Event(client=state.G_CLIENT)
    ev.add(data)
    ev.send()

def flush():
    '''Closes and restarts the transmission, sending all enqueued events 
    created by the global client. Use this if you want to perform a blocking 
    send of all events in your application.

    Note: does not work with asynchronous Transmission implementations such
    as TornadoTransmission.
    '''
    if state.G_CLIENT:
        state.G_CLIENT.flush()

def close():
    '''Wait for in-flight events to be transmitted then shut down cleanly.
       Optional (will be called automatically at exit) unless your
       application is consuming from the responses queue and needs to know
       when all responses have been received.'''
    if state.G_CLIENT:
        state.G_CLIENT.close()

    # we should error on post-close sends
    state.G_CLIENT = None

atexit.register(close) # safe because it's a no-op unless init() was called

# export everything
__all__ = [
    "Builder", "Event", "Client", "FieldHolder",
    "SendError", "add", "add_dynamic_field",
    "add_field", "close", "init", "responses", "send_now",
]

Functions

def add(

data)

Add takes a mappable object and adds each key/value pair to the global client. These key/value pairs will be sent with every event created by the global client.

def add(data):
    '''Add takes a mappable object and adds each key/value pair to the global client.
    These key/value pairs will be sent with every event created by the global client.'''
    if state.G_CLIENT is None:
        state.warn_uninitialized()
        return
    state.G_CLIENT.add(data)

def add_dynamic_field(

fn)

Add a dynamic field to the global client. This function will be executed every time an event is created. The key/value pair of the function's name and its return value will be sent with every event.

def add_dynamic_field(fn):
    '''Add a dynamic field to the global client. This function will be executed every time an
       event is created. The key/value pair of the function's name and its
       return value will be sent with every event.'''
    if state.G_CLIENT is None:
        state.warn_uninitialized()
        return
    state.G_CLIENT.add_dynamic_field(fn)

def add_field(

name, val)

Add a field to the global client. This field will be sent with every event.

def add_field(name, val):
    '''Add a field to the global client. This field will be sent with every event.'''
    if state.G_CLIENT is None:
        state.warn_uninitialized()
        return
    state.G_CLIENT.add_field(name, val)

def close(

)

Wait for in-flight events to be transmitted then shut down cleanly. Optional (will be called automatically at exit) unless your application is consuming from the responses queue and needs to know when all responses have been received.

def close():
    '''Wait for in-flight events to be transmitted then shut down cleanly.
       Optional (will be called automatically at exit) unless your
       application is consuming from the responses queue and needs to know
       when all responses have been received.'''
    if state.G_CLIENT:
        state.G_CLIENT.close()

    # we should error on post-close sends
    state.G_CLIENT = None

def init(

writekey='', dataset='', sample_rate=1, api_host='https://api.honeycomb.io', max_concurrent_batches=10, max_batch_size=100, send_frequency=0.25, block_on_send=False, block_on_response=False, transmission_impl=None, debug=False)

Initialize libhoney and prepare it to send events to Honeycomb. This creates a global Client object that is configured with the supplied parameters. For some advanced used cases, you might consider creating a Client object directly, but init is the quickest path to getting data into Honeycomb.

Note that libhoney initialization initializes a number of threads to handle sending payloads to Honeycomb. Be mindful of where you're calling libhoney.init() in order to ensure correct enqueueing + processing of events on the spawned threads.

Args:

  • writekey: the authorization key for your team on Honeycomb. Find your team write key at https://ui.honeycomb.io/account
  • dataset: the name of the default dataset to which to write
  • sample_rate: the default sample rate. 1 / sample_rate events will be sent.
  • max_concurrent_batches: the maximum number of concurrent threads sending events.
  • max_batch_size: the maximum number of events to batch before sendinga.
  • send_frequency: how long to wait before sending a batch of events, in seconds.
  • block_on_send: if true, block when send queue fills. If false, drop events until there's room in the queue
  • block_on_response: if true, block when the response queue fills. If false, drop response objects.
  • transmission_impl: if set, override the default transmission implementation (for example, TornadoTransmission)

Configuration recommendations:

For gunicorn, use a post_worker_init config hook to initialize Honeycomb:

# conf.py
import logging
import os

def post_worker_init(worker):
    logging.info(f'libhoney initialization in process pid {os.getpid()}')
    libhoney.init(writekey="YOUR_WRITE_KEY", dataset="dataset_name")

Then start gunicorn with the -c option:

gunicorn -c /path/to/conf.py
def init(writekey="", dataset="", sample_rate=1,
         api_host="https://api.honeycomb.io", max_concurrent_batches=10,
         max_batch_size=100, send_frequency=0.25,
         block_on_send=False, block_on_response=False, transmission_impl=None,
         debug=False):
    '''Initialize libhoney and prepare it to send events to Honeycomb. This creates
    a global Client object that is configured with the supplied parameters. For some
    advanced used cases, you might consider creating a Client object directly, but
    `init` is the quickest path to getting data into Honeycomb.

    Note that libhoney initialization initializes a number of threads to handle
    sending payloads to Honeycomb. Be mindful of where you're calling
    `libhoney.init()` in order to ensure correct enqueueing + processing of
    events on the spawned threads.

    Args:

    - `writekey`: the authorization key for your team on Honeycomb. Find your team
            write key at [https://ui.honeycomb.io/account](https://ui.honeycomb.io/account)
    - `dataset`: the name of the default dataset to which to write
    - `sample_rate`: the default sample rate. 1 / `sample_rate` events will be sent.
    - `max_concurrent_batches`: the maximum number of concurrent threads sending events.
    - `max_batch_size`: the maximum number of events to batch before sendinga.
    - `send_frequency`: how long to wait before sending a batch of events, in seconds.
    - `block_on_send`: if true, block when send queue fills. If false, drop
            events until there's room in the queue
    - `block_on_response`: if true, block when the response queue fills. If
            false, drop response objects.
    - `transmission_impl`: if set, override the default transmission implementation (for example, TornadoTransmission)

    --------

    **Configuration recommendations**:

    **For gunicorn**, use a [`post_worker_init` config hook](http://docs.gunicorn.org/en/stable/settings.html#post-worker-init) to initialize Honeycomb:

        # conf.py
        import logging
        import os

        def post_worker_init(worker):
            logging.info(f'libhoney initialization in process pid {os.getpid()}')
            libhoney.init(writekey="YOUR_WRITE_KEY", dataset="dataset_name")

    Then start gunicorn with the `-c` option:

        gunicorn -c /path/to/conf.py
    '''
    state.G_CLIENT = Client(
        writekey=writekey,
        dataset=dataset,
        sample_rate=sample_rate,
        api_host=api_host,
        max_concurrent_batches=max_concurrent_batches,
        max_batch_size=max_batch_size,
        send_frequency=send_frequency,
        block_on_send=block_on_send,
        block_on_response=block_on_response,
        transmission_impl=transmission_impl,
        debug=debug,
    )

def responses(

)

Returns a queue from which you can read a record of response info from each event sent by the global client. Responses will be dicts with the following keys:

  • status_code - the HTTP response from the api (eg. 200 or 503)
  • duration - how long it took to POST this event to the api, in ms
  • metadata - pass through the metadata you added on the initial event
  • body - the content returned by API (will be empty on success)
  • error - in an error condition, this is filled with the error message

When a None object appears on the queue the reader should exit

def responses():
    '''Returns a queue from which you can read a record of response info from
    each event sent by the global client. Responses will be dicts with the
    following keys:

    - `status_code` - the HTTP response from the api (eg. 200 or 503)
    - `duration` - how long it took to POST this event to the api, in ms
    - `metadata` - pass through the metadata you added on the initial event
    - `body` - the content returned by API (will be empty on success)
    - `error` - in an error condition, this is filled with the error message

    When a None object appears on the queue the reader should exit'''
    if state.G_CLIENT is None:
        state.warn_uninitialized()
        # return an empty queue rather than None. While not ideal, it is
        # better than returning None and introducing AttributeErrors into
        # the caller's code
        return Queue()

    return state.G_CLIENT.responses()

def send_now(

data)

DEPRECATED - This will likely be removed in a future major version.

Creates an event with the data passed in and enqueues it to be sent. Contrary to the name, it does not block the application when called.

Shorthand for:

ev = libhoney.Event()
ev.add(data)
ev.send()
def send_now(data):
    '''
    DEPRECATED - This will likely be removed in a future major version.

    Creates an event with the data passed in and enqueues it to be sent.
    Contrary to the name, it does not block the application when called.

    Shorthand for:

        ev = libhoney.Event()
        ev.add(data)
        ev.send()
    '''
    if state.G_CLIENT is None:
        state.warn_uninitialized()
        return
    ev = Event(client=state.G_CLIENT)
    ev.add(data)
    ev.send()

Classes

class Builder

A Builder is a scoped object to which you can add fields and dynamic fields. Events created from this builder will inherit all fields and dynamic fields from this builder and the global environment

class Builder(object):
    '''A Builder is a scoped object to which you can add fields and dynamic
       fields. Events created from this builder will inherit all fields
       and dynamic fields from this builder and the global environment'''

    def __init__(self, data={}, dyn_fields=[], fields=FieldHolder(), client=None):
        # if no client is specified, use the global client if possible
        if client is None:
            client = state.G_CLIENT

        # copy configuration from client if possible
        self.client = client
        if self.client:
            self.writekey = client.writekey
            self.dataset = client.dataset
            self.api_host = client.api_host
            self.sample_rate = client.sample_rate
        else:
            self.writekey = None
            self.dataset = None
            self.api_host = 'https://api.honeycomb.io'
            self.sample_rate = 1

        self._fields = FieldHolder()  # get an empty FH
        if self.client:
            self._fields += self.client.fields # fill it with the client fields
        self._fields.add(data)        # and anything passed in
        [self._fields.add_dynamic_field(fn) for fn in dyn_fields]
        self._fields += fields


    def add_field(self, name, val):
        self._fields.add_field(name, val)

    def add_dynamic_field(self, fn):
        '''`add_dynamic_field` adds a function to the builder. When you create an
           event from this builder, the function will be executed. The function
           name is the key and it should return one value.'''
        self._fields.add_dynamic_field(fn)

    def add(self, data):
        '''add takes a dict-like object and adds each key/value pair to the
           builder.'''
        self._fields.add(data)

    def send_now(self, data):
        '''
        DEPRECATED - This will likely be removed in a future major version.

        Creates an event with the data passed in and enqueues it to be sent.
        Contrary to the name, it does not block the application when called.

        Shorthand for:

            ev = builder.new_event()
            ev.add(data)
            ev.send()
        '''
        ev = self.new_event()
        ev.add(data)
        ev.send()

    def new_event(self):
        '''creates a new event from this builder, inheriting all fields and
           dynamic fields present in the builder'''
        ev = Event(fields=self._fields, client=self.client)
        ev.writekey = self.writekey
        ev.dataset = self.dataset
        ev.api_host = self.api_host
        ev.sample_rate = self.sample_rate
        return ev

    def clone(self):
        '''creates a new builder from this one, creating its own scope to
           which additional fields and dynamic fields can be added.'''
        c = Builder(fields=self._fields, client=self.client)
        c.writekey = self.writekey
        c.dataset = self.dataset
        c.sample_rate = self.sample_rate
        c.api_host = self.api_host
        return c

Ancestors (in MRO)

Static methods

def __init__(

self, data={}, dyn_fields=[], fields=<libhoney.fields.FieldHolder object at 0x7fec471c65f8>, client=None)

Initialize self. See help(type(self)) for accurate signature.

def __init__(self, data={}, dyn_fields=[], fields=FieldHolder(), client=None):
    # if no client is specified, use the global client if possible
    if client is None:
        client = state.G_CLIENT
    # copy configuration from client if possible
    self.client = client
    if self.client:
        self.writekey = client.writekey
        self.dataset = client.dataset
        self.api_host = client.api_host
        self.sample_rate = client.sample_rate
    else:
        self.writekey = None
        self.dataset = None
        self.api_host = 'https://api.honeycomb.io'
        self.sample_rate = 1
    self._fields = FieldHolder()  # get an empty FH
    if self.client:
        self._fields += self.client.fields # fill it with the client fields
    self._fields.add(data)        # and anything passed in
    [self._fields.add_dynamic_field(fn) for fn in dyn_fields]
    self._fields += fields

def add(

self, data)

add takes a dict-like object and adds each key/value pair to the builder.

def add(self, data):
    '''add takes a dict-like object and adds each key/value pair to the
       builder.'''
    self._fields.add(data)

def add_dynamic_field(

self, fn)

add_dynamic_field adds a function to the builder. When you create an event from this builder, the function will be executed. The function name is the key and it should return one value.

def add_dynamic_field(self, fn):
    '''`add_dynamic_field` adds a function to the builder. When you create an
       event from this builder, the function will be executed. The function
       name is the key and it should return one value.'''
    self._fields.add_dynamic_field(fn)

def add_field(

self, name, val)

def add_field(self, name, val):
    self._fields.add_field(name, val)

def clone(

self)

creates a new builder from this one, creating its own scope to which additional fields and dynamic fields can be added.

def clone(self):
    '''creates a new builder from this one, creating its own scope to
       which additional fields and dynamic fields can be added.'''
    c = Builder(fields=self._fields, client=self.client)
    c.writekey = self.writekey
    c.dataset = self.dataset
    c.sample_rate = self.sample_rate
    c.api_host = self.api_host
    return c

def new_event(

self)

creates a new event from this builder, inheriting all fields and dynamic fields present in the builder

def new_event(self):
    '''creates a new event from this builder, inheriting all fields and
       dynamic fields present in the builder'''
    ev = Event(fields=self._fields, client=self.client)
    ev.writekey = self.writekey
    ev.dataset = self.dataset
    ev.api_host = self.api_host
    ev.sample_rate = self.sample_rate
    return ev

def send_now(

self, data)

DEPRECATED - This will likely be removed in a future major version.

Creates an event with the data passed in and enqueues it to be sent. Contrary to the name, it does not block the application when called.

Shorthand for:

ev = builder.new_event()
ev.add(data)
ev.send()
def send_now(self, data):
    '''
    DEPRECATED - This will likely be removed in a future major version.
    Creates an event with the data passed in and enqueues it to be sent.
    Contrary to the name, it does not block the application when called.
    Shorthand for:
        ev = builder.new_event()
        ev.add(data)
        ev.send()
    '''
    ev = self.new_event()
    ev.add(data)
    ev.send()

Instance variables

var client

class Client

Instantiate a libhoney Client that can prepare and send events to Honeycomb.

Note that libhoney Clients initialize a number of threads to handle sending payloads to Honeycomb. Client initialization is heavy, and re-use of Client objects is encouraged. If you must use multiple clients, consider reducing max_concurrent_batches to reduce the number of threads per client.

When using a Client instance, you need to use the Client to generate Event and Builder objects. Examples:

c = Client(writekey="mywritekey", dataset="mydataset") ev = c.new_event() ev.add_field("foo", "bar") ev.send()

c = Client(writekey="mywritekey", dataset="mydataset") b = c.new_builder() b.add_field("foo", "bar") ev = b.new_event() ev.send()

To ensure that events are flushed before program termination, you should explicitly call close() on your Client instance.

Args:

  • writekey: the authorization key for your team on Honeycomb. Find your team write key at https://ui.honeycomb.io/account
  • dataset: the name of the default dataset to which to write
  • sample_rate: the default sample rate. 1 / sample_rate events will be sent.
  • max_concurrent_batches: the maximum number of concurrent threads sending events.
  • max_batch_size: the maximum number of events to batch before sending.
  • send_frequency: how long to wait before sending a batch of events, in seconds.
  • block_on_send: if true, block when send queue fills. If false, drop events until there's room in the queue
  • block_on_response: if true, block when the response queue fills. If false, drop response objects.
  • transmission_impl: if set, override the default transmission implementation (for example, TornadoTransmission)
  • user_agent_addition: if set, its contents will be appended to the User-Agent string, separated by a space. The expected format is product-name/version, eg "myapp/1.0"
class Client(object):
    '''Instantiate a libhoney Client that can prepare and send events to Honeycomb.

    Note that libhoney Clients initialize a number of threads to handle
    sending payloads to Honeycomb. Client initialization is heavy, and re-use
    of Client objects is encouraged. If you must use multiple clients, consider
    reducing `max_concurrent_batches` to reduce the number of threads per client.

    When using a Client instance, you need to use the Client to generate Event and Builder
    objects. Examples:

    ```
    c = Client(writekey="mywritekey", dataset="mydataset")
    ev = c.new_event()
    ev.add_field("foo", "bar")
    ev.send()
    ```

    ```
    c = Client(writekey="mywritekey", dataset="mydataset")
    b = c.new_builder()
    b.add_field("foo", "bar")
    ev = b.new_event()
    ev.send()
    ```

    To ensure that events are flushed before program termination, you should explicitly call `close()`
    on your Client instance.

    Args:

    - `writekey`: the authorization key for your team on Honeycomb. Find your team
            write key at [https://ui.honeycomb.io/account](https://ui.honeycomb.io/account)
    - `dataset`: the name of the default dataset to which to write
    - `sample_rate`: the default sample rate. 1 / `sample_rate` events will be sent.
    - `max_concurrent_batches`: the maximum number of concurrent threads sending events.
    - `max_batch_size`: the maximum number of events to batch before sending.
    - `send_frequency`: how long to wait before sending a batch of events, in seconds.
    - `block_on_send`: if true, block when send queue fills. If false, drop
            events until there's room in the queue
    - `block_on_response`: if true, block when the response queue fills. If
            false, drop response objects.
    - `transmission_impl`: if set, override the default transmission implementation
            (for example, TornadoTransmission)
    - `user_agent_addition`: if set, its contents will be appended to the
            User-Agent string, separated by a space. The expected format is
            product-name/version, eg "myapp/1.0"
    '''
    def __init__(self, writekey="", dataset="", sample_rate=1,
                 api_host="https://api.honeycomb.io",
                 max_concurrent_batches=10, max_batch_size=100,
                 send_frequency=0.25, block_on_send=False,
                 block_on_response=False, transmission_impl=None,
                 user_agent_addition='', debug=False):

        self.xmit = transmission_impl
        if self.xmit is None:
            self.xmit = Transmission(
                max_concurrent_batches=max_concurrent_batches, block_on_send=block_on_send, block_on_response=block_on_response,
                user_agent_addition=user_agent_addition, debug=debug,
            )

        self.xmit.start()
        self.writekey = writekey
        self.dataset = dataset
        self.api_host = api_host
        self.sample_rate = sample_rate
        self._responses = self.xmit.get_response_queue()
        self.block_on_response = block_on_response

        self.fields = FieldHolder()

        self.debug = debug
        if debug:
            self._init_logger()

        self.log('initialized honeycomb client: writekey=%s dataset=%s',
                 writekey, dataset)
        if not writekey:
            self.log('writekey not set! set the writekey if you want to send data to honeycomb')
        if not dataset:
            self.log('dataset not set! set a value for dataset if you want to send data to honeycomb')

    # enable use in a context manager
    def __enter__(self):
        return self

    def __exit__(self, typ, value, tb):
        '''Clean up Transmission if client gets garbage collected'''
        self.close()

    def _init_logger(self):
        import logging
        self._logger = logging.getLogger('honeycomb-sdk')
        self._logger.setLevel(logging.DEBUG)
        ch = logging.StreamHandler()
        ch.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        ch.setFormatter(formatter)
        self._logger.addHandler(ch)

    def log(self, msg, *args, **kwargs):
        if self.debug:
            self._logger.debug(msg, *args, **kwargs)

    def responses(self):
        '''Returns a queue from which you can read a record of response info from
        each event sent. Responses will be dicts with the following keys:

        - `status_code` - the HTTP response from the api (eg. 200 or 503)
        - `duration` - how long it took to POST this event to the api, in ms
        - `metadata` - pass through the metadata you added on the initial event
        - `body` - the content returned by API (will be empty on success)
        - `error` - in an error condition, this is filled with the error message

        When the Client's `close` method is called, a None will be inserted on
        the queue, indicating that no further responses will be written.
        '''
        return self._responses

    def add_field(self, name, val):
        '''add a global field. This field will be sent with every event.'''
        self.fields.add_field(name, val)

    def add_dynamic_field(self, fn):
        '''add a global dynamic field. This function will be executed every time an
        event is created. The key/value pair of the function's name and its
        return value will be sent with every event.'''
        self.fields.add_dynamic_field(fn)

    def add(self, data):
        '''add takes a mappable object and adds each key/value pair to the
        global scope'''
        self.fields.add(data)

    def send(self, event):
        '''Enqueues the given event to be sent to Honeycomb.

        Should not be called directly. Instead, use Event:
            ev = client.new_event()
            ev.add(data)
            ev.send()
        '''
        if self.xmit is None:
            self.log(
                "tried to send on a closed or uninitialized libhoney client,"
                " ev = %s", event.fields())
            return

        self.log("send enqueuing event ev = %s", event.fields())
        self.xmit.send(event)

    def send_now(self, data):
        '''
        DEPRECATED - This will likely be removed in a future major version.

        Creates an event with the data passed in and enqueues it to be sent.
        Contrary to the name, it does not block the application when called.

        Shorthand for:

            ev = client.new_event()
            ev.add(data)
            ev.send()
        '''
        ev = self.new_event()
        ev.add(data)
        self.log("send_now enqueuing event ev = %s", ev.fields())
        ev.send()

    def send_dropped_response(self, event):
        '''push the dropped event down the responses queue'''
        response = {
            "status_code": 0,
            "duration": 0,
            "metadata": event.metadata,
            "body": "",
            "error": "event dropped due to sampling",
        }
        self.log("enqueuing response = %s", response)
        try:
            if self.block_on_response:
                self._responses.put(response)
            else:
                self._responses.put_nowait(response)
        except queue.Full:
            pass

    def close(self):
        '''Wait for in-flight events to be transmitted then shut down cleanly.
        Optional (will be called automatically at exit) unless your
        application is consuming from the responses queue and needs to know
        when all responses have been received.'''

        if self.xmit:
            self.xmit.close()

        # we should error on post-close sends
        self.xmit = None


    def flush(self):
        '''Closes and restarts the transmission, sending all events. Use this
        if you want to perform a blocking send of all events in your
        application.

        Note: does not work with asynchronous Transmission implementations such
        as TornadoTransmission.
        '''
        if self.xmit and isinstance(self.xmit, Transmission):
            self.xmit.close()
            self.xmit.start()

    def new_event(self, data={}):
        '''Return an Event, initialized to be sent with this client'''
        ev = Event(data=data, client=self)
        return ev

    def new_builder(self, data=None, dyn_fields=None, fields=None):
        '''Return a Builder. Events built from this builder will be sent with
        this client'''
        if data is None:
            data = {}
        if dyn_fields is None:
            dyn_fields = []
        if fields is None:
            fields = FieldHolder()
        builder = Builder(data, dyn_fields, fields, self)
        return builder

Ancestors (in MRO)

Static methods

def __init__(

self, writekey='', dataset='', sample_rate=1, api_host='https://api.honeycomb.io', max_concurrent_batches=10, max_batch_size=100, send_frequency=0.25, block_on_send=False, block_on_response=False, transmission_impl=None, user_agent_addition='', debug=False)

Initialize self. See help(type(self)) for accurate signature.

def __init__(self, writekey="", dataset="", sample_rate=1,
             api_host="https://api.honeycomb.io",
             max_concurrent_batches=10, max_batch_size=100,
             send_frequency=0.25, block_on_send=False,
             block_on_response=False, transmission_impl=None,
             user_agent_addition='', debug=False):
    self.xmit = transmission_impl
    if self.xmit is None:
        self.xmit = Transmission(
            max_concurrent_batches=max_concurrent_batches, block_on_send=block_on_send, block_on_response=block_on_response,
            user_agent_addition=user_agent_addition, debug=debug,
        )
    self.xmit.start()
    self.writekey = writekey
    self.dataset = dataset
    self.api_host = api_host
    self.sample_rate = sample_rate
    self._responses = self.xmit.get_response_queue()
    self.block_on_response = block_on_response
    self.fields = FieldHolder()
    self.debug = debug
    if debug:
        self._init_logger()
    self.log('initialized honeycomb client: writekey=%s dataset=%s',
             writekey, dataset)
    if not writekey:
        self.log('writekey not set! set the writekey if you want to send data to honeycomb')
    if not dataset:
        self.log('dataset not set! set a value for dataset if you want to send data to honeycomb')

def add(

self, data)

add takes a mappable object and adds each key/value pair to the global scope

def add(self, data):
    '''add takes a mappable object and adds each key/value pair to the
    global scope'''
    self.fields.add(data)

def add_dynamic_field(

self, fn)

add a global dynamic field. This function will be executed every time an event is created. The key/value pair of the function's name and its return value will be sent with every event.

def add_dynamic_field(self, fn):
    '''add a global dynamic field. This function will be executed every time an
    event is created. The key/value pair of the function's name and its
    return value will be sent with every event.'''
    self.fields.add_dynamic_field(fn)

def add_field(

self, name, val)

add a global field. This field will be sent with every event.

def add_field(self, name, val):
    '''add a global field. This field will be sent with every event.'''
    self.fields.add_field(name, val)

def close(

self)

Wait for in-flight events to be transmitted then shut down cleanly. Optional (will be called automatically at exit) unless your application is consuming from the responses queue and needs to know when all responses have been received.

def close(self):
    '''Wait for in-flight events to be transmitted then shut down cleanly.
    Optional (will be called automatically at exit) unless your
    application is consuming from the responses queue and needs to know
    when all responses have been received.'''
    if self.xmit:
        self.xmit.close()
    # we should error on post-close sends
    self.xmit = None

def flush(

self)

Closes and restarts the transmission, sending all events. Use this if you want to perform a blocking send of all events in your application.

Note: does not work with asynchronous Transmission implementations such as TornadoTransmission.

def flush(self):
    '''Closes and restarts the transmission, sending all events. Use this
    if you want to perform a blocking send of all events in your
    application.
    Note: does not work with asynchronous Transmission implementations such
    as TornadoTransmission.
    '''
    if self.xmit and isinstance(self.xmit, Transmission):
        self.xmit.close()
        self.xmit.start()

def log(

self, msg, *args, **kwargs)

def log(self, msg, *args, **kwargs):
    if self.debug:
        self._logger.debug(msg, *args, **kwargs)

def new_builder(

self, data=None, dyn_fields=None, fields=None)

Return a Builder. Events built from this builder will be sent with this client

def new_builder(self, data=None, dyn_fields=None, fields=None):
    '''Return a Builder. Events built from this builder will be sent with
    this client'''
    if data is None:
        data = {}
    if dyn_fields is None:
        dyn_fields = []
    if fields is None:
        fields = FieldHolder()
    builder = Builder(data, dyn_fields, fields, self)
    return builder

def new_event(

self, data={})

Return an Event, initialized to be sent with this client

def new_event(self, data={}):
    '''Return an Event, initialized to be sent with this client'''
    ev = Event(data=data, client=self)
    return ev

def responses(

self)

Returns a queue from which you can read a record of response info from each event sent. Responses will be dicts with the following keys:

  • status_code - the HTTP response from the api (eg. 200 or 503)
  • duration - how long it took to POST this event to the api, in ms
  • metadata - pass through the metadata you added on the initial event
  • body - the content returned by API (will be empty on success)
  • error - in an error condition, this is filled with the error message

When the Client's close method is called, a None will be inserted on the queue, indicating that no further responses will be written.

def responses(self):
    '''Returns a queue from which you can read a record of response info from
    each event sent. Responses will be dicts with the following keys:
    - `status_code` - the HTTP response from the api (eg. 200 or 503)
    - `duration` - how long it took to POST this event to the api, in ms
    - `metadata` - pass through the metadata you added on the initial event
    - `body` - the content returned by API (will be empty on success)
    - `error` - in an error condition, this is filled with the error message
    When the Client's `close` method is called, a None will be inserted on
    the queue, indicating that no further responses will be written.
    '''
    return self._responses

def send(

self, event)

Enqueues the given event to be sent to Honeycomb.

Should not be called directly. Instead, use Event: ev = client.new_event() ev.add(data) ev.send()

def send(self, event):
    '''Enqueues the given event to be sent to Honeycomb.
    Should not be called directly. Instead, use Event:
        ev = client.new_event()
        ev.add(data)
        ev.send()
    '''
    if self.xmit is None:
        self.log(
            "tried to send on a closed or uninitialized libhoney client,"
            " ev = %s", event.fields())
        return
    self.log("send enqueuing event ev = %s", event.fields())
    self.xmit.send(event)

def send_dropped_response(

self, event)

push the dropped event down the responses queue

def send_dropped_response(self, event):
    '''push the dropped event down the responses queue'''
    response = {
        "status_code": 0,
        "duration": 0,
        "metadata": event.metadata,
        "body": "",
        "error": "event dropped due to sampling",
    }
    self.log("enqueuing response = %s", response)
    try:
        if self.block_on_response:
            self._responses.put(response)
        else:
            self._responses.put_nowait(response)
    except queue.Full:
        pass

def send_now(

self, data)

DEPRECATED - This will likely be removed in a future major version.

Creates an event with the data passed in and enqueues it to be sent. Contrary to the name, it does not block the application when called.

Shorthand for:

ev = client.new_event()
ev.add(data)
ev.send()
def send_now(self, data):
    '''
    DEPRECATED - This will likely be removed in a future major version.
    Creates an event with the data passed in and enqueues it to be sent.
    Contrary to the name, it does not block the application when called.
    Shorthand for:
        ev = client.new_event()
        ev.add(data)
        ev.send()
    '''
    ev = self.new_event()
    ev.add(data)
    self.log("send_now enqueuing event ev = %s", ev.fields())
    ev.send()

Instance variables

var api_host

var block_on_response

var dataset

var debug

var fields

var sample_rate

var writekey

var xmit

class Event

An Event is a collection of fields that will be sent to Honeycomb.

class Event(object):
    '''An Event is a collection of fields that will be sent to Honeycomb.'''

    def __init__(self, data={}, dyn_fields=[], fields=FieldHolder(), client=None):
        if client is None:
            client = state.G_CLIENT

        # copy configuration from client
        self.client = client
        if self.client:
            self.writekey = client.writekey
            self.dataset = client.dataset
            self.api_host = client.api_host
            self.sample_rate = client.sample_rate
        else:
            self.writekey = None
            self.dataset = None
            self.api_host = 'https://api.honeycomb.io'
            self.sample_rate = 1

        # populate the event's fields
        self._fields = FieldHolder()  # get an empty FH
        if self.client:
            self._fields += self.client.fields # fill it with the client fields
        self._fields.add(data)        # and anything passed in
        [self._fields.add_dynamic_field(fn) for fn in dyn_fields]
        self._fields += fields

        # fill in other info
        self.created_at = datetime.datetime.utcnow()
        self.metadata = None
        # execute all the dynamic functions and add their data
        for fn in self._fields._dyn_fields:
            self._fields.add_field(fn.__name__, fn())


    def add_field(self, name, val):
        self._fields.add_field(name, val)

    def add_metadata(self, md):
        '''Add metadata to an event. This metadata is handed back to you in
        the response queue. It is not transmitted to Honeycomb; it is a place
        for you to put identifying information to understand which event a
        response queue object represents.'''
        self.metadata = md

    def add(self, data):
        self._fields.add(data)

    @contextmanager
    def timer(self, name):
        '''timer is a context for timing (in milliseconds) a function call.

        Example:

            ev = Event()
            with ev.timer("database_dur_ms"):
                do_database_work()

           will add a field (name, duration) indicating how long it took to run
           do_database_work()'''
        start = datetime.datetime.now()
        yield
        duration = datetime.datetime.now() - start
        # report in ms
        self.add_field(name, duration.total_seconds() * 1000)

    def send(self):
        '''send queues this event for transmission to Honeycomb.

        Will drop sampled events when sample_rate > 1,
        and ensure that the Honeycomb datastore correctly considers it
        as representing `sample_rate` number of similar events.'''
        # warn if we're not using a client instance and global libhoney
        # is not initialized. This will result in a noop, but is better
        # than crashing the caller if they forget to initialize
        if self.client is None:
            state.warn_uninitialized()
            return

        if _should_drop(self.sample_rate):
            self.client.send_dropped_response(self)
            return

        self.send_presampled()

    def send_presampled(self):
        '''send_presampled queues this event for transmission to Honeycomb.

        Caller is responsible for sampling logic - will not drop any events
        for sampling. Defining a `sample_rate` will ensure that the Honeycomb
        datastore correctly considers it as representing `sample_rate` number
        of similar events.

        Raises SendError if no fields are defined or critical attributes not
        set (writekey, dataset, api_host).'''
        if self._fields.is_empty():
            self.client.log("No metrics added to event. Won't send empty event.")
            return
        if self.api_host == "":
            self.client.log("No api_host for Honeycomb. Can't send to the Great Unknown.")
            return
        if self.writekey == "":
            self.client.log("No writekey specified. Can't send event.")
            return
        if self.dataset == "":
            self.client.log(
                "No dataset for Honeycomb. Can't send event without knowing which dataset it belongs to.")
            return

        if self.client:
            self.client.send(self)
        else:
            state.warn_uninitialized()

    def __str__(self):
        return str(self._fields)

    def fields(self):
        return self._fields._data

Ancestors (in MRO)

Static methods

def __init__(

self, data={}, dyn_fields=[], fields=<libhoney.fields.FieldHolder object at 0x7fec471b6f28>, client=None)

Initialize self. See help(type(self)) for accurate signature.

def __init__(self, data={}, dyn_fields=[], fields=FieldHolder(), client=None):
    if client is None:
        client = state.G_CLIENT
    # copy configuration from client
    self.client = client
    if self.client:
        self.writekey = client.writekey
        self.dataset = client.dataset
        self.api_host = client.api_host
        self.sample_rate = client.sample_rate
    else:
        self.writekey = None
        self.dataset = None
        self.api_host = 'https://api.honeycomb.io'
        self.sample_rate = 1
    # populate the event's fields
    self._fields = FieldHolder()  # get an empty FH
    if self.client:
        self._fields += self.client.fields # fill it with the client fields
    self._fields.add(data)        # and anything passed in
    [self._fields.add_dynamic_field(fn) for fn in dyn_fields]
    self._fields += fields
    # fill in other info
    self.created_at = datetime.datetime.utcnow()
    self.metadata = None
    # execute all the dynamic functions and add their data
    for fn in self._fields._dyn_fields:
        self._fields.add_field(fn.__name__, fn())

def add(

self, data)

def add(self, data):
    self._fields.add(data)

def add_field(

self, name, val)

def add_field(self, name, val):
    self._fields.add_field(name, val)

def add_metadata(

self, md)

Add metadata to an event. This metadata is handed back to you in the response queue. It is not transmitted to Honeycomb; it is a place for you to put identifying information to understand which event a response queue object represents.

def add_metadata(self, md):
    '''Add metadata to an event. This metadata is handed back to you in
    the response queue. It is not transmitted to Honeycomb; it is a place
    for you to put identifying information to understand which event a
    response queue object represents.'''
    self.metadata = md

def fields(

self)

def fields(self):
    return self._fields._data

def send(

self)

send queues this event for transmission to Honeycomb.

Will drop sampled events when sample_rate > 1, and ensure that the Honeycomb datastore correctly considers it as representing sample_rate number of similar events.

def send(self):
    '''send queues this event for transmission to Honeycomb.
    Will drop sampled events when sample_rate > 1,
    and ensure that the Honeycomb datastore correctly considers it
    as representing `sample_rate` number of similar events.'''
    # warn if we're not using a client instance and global libhoney
    # is not initialized. This will result in a noop, but is better
    # than crashing the caller if they forget to initialize
    if self.client is None:
        state.warn_uninitialized()
        return
    if _should_drop(self.sample_rate):
        self.client.send_dropped_response(self)
        return
    self.send_presampled()

def send_presampled(

self)

send_presampled queues this event for transmission to Honeycomb.

Caller is responsible for sampling logic - will not drop any events for sampling. Defining a sample_rate will ensure that the Honeycomb datastore correctly considers it as representing sample_rate number of similar events.

Raises SendError if no fields are defined or critical attributes not set (writekey, dataset, api_host).

def send_presampled(self):
    '''send_presampled queues this event for transmission to Honeycomb.
    Caller is responsible for sampling logic - will not drop any events
    for sampling. Defining a `sample_rate` will ensure that the Honeycomb
    datastore correctly considers it as representing `sample_rate` number
    of similar events.
    Raises SendError if no fields are defined or critical attributes not
    set (writekey, dataset, api_host).'''
    if self._fields.is_empty():
        self.client.log("No metrics added to event. Won't send empty event.")
        return
    if self.api_host == "":
        self.client.log("No api_host for Honeycomb. Can't send to the Great Unknown.")
        return
    if self.writekey == "":
        self.client.log("No writekey specified. Can't send event.")
        return
    if self.dataset == "":
        self.client.log(
            "No dataset for Honeycomb. Can't send event without knowing which dataset it belongs to.")
        return
    if self.client:
        self.client.send(self)
    else:
        state.warn_uninitialized()

def timer(

*args, **kwds)

timer is a context for timing (in milliseconds) a function call.

Example:

ev = Event()
with ev.timer("database_dur_ms"):
    do_database_work()

will add a field (name, duration) indicating how long it took to run do_database_work()

@contextmanager
def timer(self, name):
    '''timer is a context for timing (in milliseconds) a function call.
    Example:
        ev = Event()
        with ev.timer("database_dur_ms"):
            do_database_work()
       will add a field (name, duration) indicating how long it took to run
       do_database_work()'''
    start = datetime.datetime.now()
    yield
    duration = datetime.datetime.now() - start
    # report in ms
    self.add_field(name, duration.total_seconds() * 1000)

Instance variables

var client

var created_at

var metadata

class FieldHolder

A FieldHolder is the generalized class that stores fields and dynamic fields. It should not be used directly; only through the subclasses

class FieldHolder:
    '''A FieldHolder is the generalized class that stores fields and dynamic
       fields. It should not be used directly; only through the subclasses'''

    def __init__(self):
        self._data = {}
        self._dyn_fields = set()

    def __add__(self, other):
        '''adding two field holders merges the data with other overriding
           any fields they have in common'''
        self._data.update(other._data)
        self._dyn_fields.update(other._dyn_fields)
        return self

    def __eq__(self, other):
        '''two FieldHolders are equal if their datasets are equal'''
        return ((self._data, self._dyn_fields) ==
                (other._data, other._dyn_fields))

    def __ne__(self, other):
        '''two FieldHolders are equal if their datasets are equal'''
        return not self.__eq__(other)

    def add_field(self, name, val):
        self._data[name] = val

    def add_dynamic_field(self, fn):
        if not inspect.isroutine(fn):
            raise TypeError("add_dynamic_field requires function argument")
        self._dyn_fields.add(fn)

    def add(self, data):
        try:
            for k, v in data.items():
                self.add_field(k, v)
        except AttributeError:
            raise TypeError("add requires a dict-like argument")

    def is_empty(self):
        '''returns true if there is no data in this FieldHolder'''
        return len(self._data) == 0

    def __str__(self):
        '''returns a JSON blob of the fields in this holder'''
        return json.dumps(self._data, default=json_default_handler)

Ancestors (in MRO)

Static methods

def __init__(

self)

Initialize self. See help(type(self)) for accurate signature.

def __init__(self):
    self._data = {}
    self._dyn_fields = set()

def add(

self, data)

def add(self, data):
    try:
        for k, v in data.items():
            self.add_field(k, v)
    except AttributeError:
        raise TypeError("add requires a dict-like argument")

def add_dynamic_field(

self, fn)

def add_dynamic_field(self, fn):
    if not inspect.isroutine(fn):
        raise TypeError("add_dynamic_field requires function argument")
    self._dyn_fields.add(fn)

def add_field(

self, name, val)

def add_field(self, name, val):
    self._data[name] = val

def is_empty(

self)

returns true if there is no data in this FieldHolder

def is_empty(self):
    '''returns true if there is no data in this FieldHolder'''
    return len(self._data) == 0

class SendError

raised when send is called on an event that cannot be sent, such as: - when it lacks a writekey - dataset is not specified - no fields are set

class SendError(Exception):
    ''' raised when send is called on an event that cannot be sent, such as:
        - when it lacks a writekey
        - dataset is not specified
        - no fields are set
    '''
    pass

Ancestors (in MRO)

  • SendError
  • builtins.Exception
  • builtins.BaseException
  • builtins.object

Class variables

var args