class Libhoney::Client

This is a library to allow you to send events to Honeycomb from within your Ruby application.

@example Send a simple event

require 'libhoney'
honey = Libhoney.new(writekey, dataset, sample_rate)

evt = honey.event
evt.add(pglatency: 100)
honey.send(evt)

# repeat creating and sending events until your program is finished

honey.close

@example Override the default timestamp on an event

one_hour_ago = Time.now - 3600

evt = libhoney.event
evt.add_fields(useful_fields)
evt.timestamp = one_hour_ago
evt.send

Constants

API_HOST

Attributes

block_on_responses[R]
block_on_send[R]
max_batch_size[R]
max_concurrent_batches[R]
pending_work_capacity[R]
responses[R]
send_frequency[R]

Public Class Methods

new(writekey: nil, dataset: nil, sample_rate: 1, api_host: API_HOST, user_agent_addition: nil, transmission: nil, block_on_send: false, block_on_responses: false, max_batch_size: 50, send_frequency: 100, max_concurrent_batches: 10, pending_work_capacity: 1000, proxy_config: nil) click to toggle source

Instantiates libhoney and prepares it to send events to Honeycomb.

@param writekey [String] the Honeycomb API key with which to authenticate

this request (required)

@param dataset [String] the Honeycomb dataset into which to send events (required) @param sample_rate [Fixnum] cause libhoney to send 1 out of sample_rate events.

overrides the libhoney instance's value.  (e.g. setting this to +10+ will result in
a 1-in-10 chance of it being successfully emitted to Honeycomb, and the
Honeycomb query engine will interpret it as representative of 10 events)

@param api_host [String] defaults to API_HOST, override to change the

destination for these Honeycomb events.

@param transmission [Class, Object, nil] transport used to actually send events. If nil (the default),

will be initialized with a {TransmissionClient}. If Class--for example, {MockTransmissionClient}--will attempt
to create a new instance of that class with {TransmissionClient}'s usual parameters. If Object, checks
that the instance passed in implements the Transmission interface (responds to :add and :close). If the
check does not succeed, a no-op NullTransmission will be used instead.

@param block_on_send [Boolean] if more than pending_work_capacity events are written, block sending further events @param block_on_responses [Boolean] if true, block if there is no thread reading from the response queue @param pending_work_capacity [Fixnum] defaults to 1000. If the queue of

pending events exceeds 1000, this client will start dropping events.

@param proxy_config [String, Array, nil] proxy connection information

nil: (default, recommended) connection proxying will be determined from any http_proxy, https_proxy, and no_proxy environment
  variables set for the process.
String: the value must be the URI for connecting to a forwarding web proxy. Must be parsable by stdlib URI.
Array: (deprecated, removal in v2.0) the value must have one and at most four elements: e.g. ['host', port, 'username', 'password'].
  The assumption is that the TCP connection will be tunneled via HTTP, so the assumed scheme is 'http://'
  'host' is required. 'port' is optional (default:80), unless a 'username' is included. 'password' is optional.

rubocop:disable Metrics/ParameterLists

# File lib/libhoney/client.rb, line 66
def initialize(writekey: nil,
               dataset: nil,
               sample_rate: 1,
               api_host: API_HOST,
               user_agent_addition: nil,
               transmission: nil,
               block_on_send: false,
               block_on_responses: false,
               max_batch_size: 50,
               send_frequency: 100,
               max_concurrent_batches: 10,
               pending_work_capacity: 1000,
               proxy_config: nil)
  # rubocop:enable Metrics/ParameterLists
  # check for insanity
  raise Exception, 'libhoney:  max_concurrent_batches must be greater than 0' if max_concurrent_batches < 1
  raise Exception, 'libhoney:  sample rate must be greater than 0'            if sample_rate < 1

  unless Gem::Dependency.new('ruby', '>= 2.2').match?('ruby', RUBY_VERSION)
    raise Exception, 'libhoney:  Ruby versions < 2.2 are not supported'
  end

  @builder = Builder.new(self, nil)

  @builder.writekey    = writekey
  @builder.dataset     = dataset
  @builder.sample_rate = sample_rate
  @builder.api_host    = api_host

  @user_agent_addition = user_agent_addition

  @block_on_send          = block_on_send
  @block_on_responses     = block_on_responses
  @max_batch_size         = max_batch_size
  @send_frequency         = send_frequency
  @max_concurrent_batches = max_concurrent_batches
  @pending_work_capacity  = pending_work_capacity
  @responses              = SizedQueue.new(2 * @pending_work_capacity)
  @proxy_config           = parse_proxy_config(proxy_config)
  @transmission           = setup_transmission(transmission, writekey, dataset)
end

Public Instance Methods

add(data) click to toggle source

adds a group of field->values to the global Builder.

@param data [Hash<String=>any>] field->value mapping. @return [self] this Client instance @example

honey.add {
  :responseTime_ms => 100,
  :httpStatusCode => 200
}
# File lib/libhoney/client.rb, line 132
def add(data)
  @builder.add(data)
  self
end
add_dynamic_field(name, proc) click to toggle source

adds a single field->dynamic value function to the global Builder.

@param name [String] name of field to add. @param proc [#call] function that will be called to generate the value whenever an event is created. @return [self] this libhoney instance. @example

honey.add_dynamic_field("active_threads", Proc.new { Thread.list.select {|thread| thread.status == "run"}.count })
# File lib/libhoney/client.rb, line 156
def add_dynamic_field(name, proc)
  @builder.add_dynamic_field(name, proc)
  self
end
add_field(name, val) click to toggle source

adds a single field->value mapping to the global Builder.

@param name [String] name of field to add. @param val [any] value of field to add. @return [self] this Client instance @example

honey.add_field("responseTime_ms", 100)
# File lib/libhoney/client.rb, line 144
def add_field(name, val)
  @builder.add_field(name, val)
  self
end
close(drain = true) click to toggle source

Nuke the queue and wait for inflight requests to complete before returning. If you set drain=false, all queued requests will be dropped on the floor.

# File lib/libhoney/client.rb, line 117
def close(drain = true)
  return @transmission.close(drain) if @transmission

  0
end
send_dropped_response(event, msg) click to toggle source

@api private

# File lib/libhoney/client.rb, line 197
def send_dropped_response(event, msg)
  response = Response.new(error: msg,
                          metadata: event.metadata)
  begin
    @responses.enq(response, !@block_on_responses)
  rescue ThreadError
    # happens if the queue was full and block_on_responses = false.
  end
end
send_event(event) click to toggle source

Enqueue an event to send. Sampling happens here, and we will create new threads to handle work as long as we haven't gone over max_concurrent_batches and there are still events in the queue.

@param event [Event] the event to send to honeycomb @api private

# File lib/libhoney/client.rb, line 192
def send_event(event)
  @transmission.add(event)
end
send_now(data = {}) click to toggle source

@deprecated Creates and sends an event, including all global builder fields/dyn_fields, as well as anything in the optional data parameter.

Equivalent to:

ev = builder.event
ev.add(data)
ev.send

May be removed in a future major release

@param data [Hash<String=>any>] optional field->value mapping to add to the event sent. @return [self] this libhoney instance. @example empty send_now

honey.send_now # sends just the data that has been added via add/add_field/add_dynamic_field.

@example adding data at send-time

honey.send_now {
  additionalField: value
}

/

# File lib/libhoney/client.rb, line 180
def send_now(data = {})
  @builder.send_now(data)
  self
end
should_drop(sample_rate) click to toggle source

@api private

# File lib/libhoney/client.rb, line 208
def should_drop(sample_rate)
  rand(1..sample_rate) != 1
end

Private Instance Methods

parse_proxy_config(config) click to toggle source

@api private

# File lib/libhoney/client.rb, line 266
    def parse_proxy_config(config)
      case config
      when nil then nil
      when String
        URI.parse(config)
      when Array
        warn <<-WARNING
        DEPRECATION WARNING: #{self.class.name} the proxy_config parameter will require a String value, not an Array in libhoney 2.0.
        To resolve:
          + recommended: set http/https_proxy environment variables, which take precedence over any option set here, then remove proxy_config parameter from client initialization
          + set proxy_config to a String containing the forwarding proxy URI (only used if http/https_proxy are not set)
        WARNING
        host, port, user, password = config

        parsed_config = URI::HTTP.build(host: host, port: port).tap do |uri|
          uri.userinfo = "#{user}:#{password}" if user
        end
        redacted_config = parsed_config.dup.tap do |uri|
          uri.password = 'REDACTED' unless uri.password.nil? || uri.password.empty?
        end
        warn "The array config given has been assumed to mean: #{redacted_config}"
        parsed_config
      end
    rescue URI::Error => e
      warn "#{self.class.name}: unable to parse proxy_config. Detail: #{e.class}: #{e.message}"
    end
quacks_like_a_transmission?(transmission) click to toggle source
# File lib/libhoney/client.rb, line 261
def quacks_like_a_transmission?(transmission)
  transmission.respond_to?(:add) && transmission.respond_to?(:close)
end
setup_transmission(transmission, writekey, dataset) click to toggle source
# File lib/libhoney/client.rb, line 231
def setup_transmission(transmission, writekey, dataset)
  # if a provided transmission can add and close, we'll assume the user
  # has provided a working transmission with a customized configuration
  return transmission if quacks_like_a_transmission?(transmission)

  if !(writekey && dataset) # rubocop:disable Style/NegatedIf
    # if no writekey or dataset are configured, and we didn't override the
    # transmission (e.g. to a MockTransmissionClient), that's almost
    # certainly a misconfiguration, even though it's possible to override
    # them on a per-event basis. So let's handle the misconfiguration
    # early rather than potentially throwing thousands of exceptions at runtime.
    warn "#{self.class.name}: no #{writekey ? 'dataset' : 'writekey'} configured, disabling sending events"
    return NullTransmissionClient.new
  end

  case transmission
  when NilClass # the default value for new clients
    return TransmissionClient.new(**transmission_client_params)
  when Class
    # if a class has been provided, attempt to instantiate it with parameters given to the client
    t = transmission.new(**transmission_client_params)
    if quacks_like_a_transmission?(t) # rubocop:disable Style/GuardClause
      return t
    else
      warn "#{t.class.name}: does not appear to behave like a transmission, disabling sending events"
      return NullTransmissionClient.new
    end
  end
end
transmission_client_params() click to toggle source

Parameters to pass to a transmission based on client config.

# File lib/libhoney/client.rb, line 217
def transmission_client_params
  {
    max_batch_size: @max_batch_size,
    send_frequency: @send_frequency,
    max_concurrent_batches: @max_concurrent_batches,
    pending_work_capacity: @pending_work_capacity,
    responses: @responses,
    block_on_send: @block_on_send,
    block_on_responses: @block_on_responses,
    user_agent_addition: @user_agent_addition,
    proxy_config: @proxy_config
  }
end