class Datadog::Statsd
Constants
- CGROUPV1BASECONTROLLER
- COUNTER_TYPE
- CRITICAL
- DEFAULT_BUFFER_POOL_SIZE
- DEFAULT_TELEMETRY_FLUSH_INTERVAL
-
minimum flush interval for the telemetry in seconds
- DISTRIBUTION_TYPE
- EMPTY_OPTIONS
- GAUGE_TYPE
- HISTOGRAM_TYPE
- HOSTCGROUPNAMESPACEINODE
- MAX_EVENT_SIZE
- OK
- SET_TYPE
- TIMING_TYPE
- UDP_DEFAULT_BUFFER_SIZE
- UDP_DEFAULT_SENDER_QUEUE_SIZE
- UDS_DEFAULT_BUFFER_SIZE
- UDS_DEFAULT_SENDER_QUEUE_SIZE
- UNKNOWN
- VERSION
- WARNING
Attributes
A namespace to prepend to all statsd calls. Defaults to no namespace.
Default sample rate
Public Class Methods
Source
# File lib/datadog/statsd.rb, line 89 def initialize( host = nil, port = nil, socket_path: nil, namespace: nil, tags: nil, sample_rate: nil, buffer_max_payload_size: nil, buffer_max_pool_size: nil, buffer_overflowing_stategy: :drop, buffer_flush_interval: nil, sender_queue_size: nil, logger: nil, single_thread: false, delay_serialization: false, telemetry_enable: true, telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL, origin_detection: true, container_id: nil, cardinality: nil ) unless tags.nil? || tags.is_a?(Array) || tags.is_a?(Hash) raise ArgumentError, 'tags must be an array of string tags or a Hash' end @namespace = namespace @prefix = @namespace ? "#{@namespace}.".freeze : nil origin_detection_enabled = origin_detection_enabled?(origin_detection) container_id = get_container_id(container_id, origin_detection_enabled) external_data = sanitize(ENV['DD_EXTERNAL_ENV']) @serializer = Serialization::Serializer.new(prefix: @prefix, container_id: container_id, external_data: external_data, global_tags: tags, ) @cardinality = cardinality || ENV['DD_CARDINALITY'] || ENV['DATADOG_CARDINALITY'] @sample_rate = sample_rate @delay_serialization = delay_serialization @forwarder = Forwarder.new( connection_cfg: ConnectionCfg.new( host: host, port: port, socket_path: socket_path, ), global_tags: tags, logger: logger, single_thread: single_thread, buffer_max_payload_size: buffer_max_payload_size, buffer_max_pool_size: buffer_max_pool_size, buffer_overflowing_stategy: buffer_overflowing_stategy, buffer_flush_interval: buffer_flush_interval, sender_queue_size: sender_queue_size, telemetry_flush_interval: telemetry_enable ? telemetry_flush_interval : nil, container_id: container_id, external_data: external_data, cardinality: @cardinality, serializer: serializer ) end
@param [String] host your statsd host @param [Integer] port your statsd port @option [String] namespace set a namespace to be prepended to every metric name @option [Array<String>|Hash] tags tags to be added to every metric @option [Logger] logger for debugging @option [Integer] buffer_max_payload_size max bytes to buffer @option [Integer] buffer_max_pool_size max messages to buffer @option [Integer] sender_queue_size size of the sender queue in number of buffers @option [Numeric] buffer_flush_interval interval in second to flush buffer @option [String] socket_path
unix socket path @option [Float] default sample rate if not overridden @option [Boolean] single_thread flushes the metrics on the main thread instead of in a companion thread @option [Boolean] delay_serialization delays stat serialization @option [Boolean] origin_detection is origin detection enabled @option [String] container_id the container ID field, used for origin detection @option [String] cardinality the default tag cardinality to use
Source
# File lib/datadog/statsd.rb, line 171 def self.open(*args, **kwargs) instance = new(*args, **kwargs) yield instance ensure instance.close if instance end
yield a new instance to a block and close it when done for short-term use-cases that don’t want to close the socket manually TODO: replace with … once we are on ruby 2.7
Public Instance Methods
Source
# File lib/datadog/statsd.rb, line 410 def batch yield self flush(sync: true) end
Send several metrics in the same packet. They will be buffered and flushed when the block finishes.
This method exists for compatibility with v4.x versions, it is not needed anymore since the batching is now automatically done internally. It also means that an automatic flush could occur if the buffer is filled during the execution of the batch block.
This method is DEPRECATED and will be removed in future v6.x API.
@example Send several metrics in one packet:
$statsd.batch do |s| s.gauge('users.online',156) s.increment('page.views') end
Source
# File lib/datadog/statsd.rb, line 418 def close(flush: true) flush(sync: true) if flush forwarder.close end
Close the underlying socket
@param [Boolean, true] flush Should we flush the metrics before closing
Source
# File lib/datadog/statsd.rb, line 220 def count(stat, count, opts = EMPTY_OPTIONS) opts = { sample_rate: opts } if opts.is_a?(Numeric) send_stats(stat, count, COUNTER_TYPE, opts) end
Sends an arbitrary count for the given stat to the statsd server.
@param [String] stat stat name @param [Integer] count count @param [Hash] opts the options to create the metric with @option opts [Numeric] :sample_rate sample rate, 1 for always @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn’t perform sampling. @option opts [Array<String>] :tags An array of tags @option opts [String] :cardinality The tag cardinality to use
Source
# File lib/datadog/statsd.rb, line 205 def decrement(stat, opts = EMPTY_OPTIONS) opts = { sample_rate: opts } if opts.is_a?(Numeric) decr_value = - opts.fetch(:by, 1) count(stat, decr_value, opts) end
Sends a decrement (count = -1) for the given stat to the statsd server.
@param [String] stat stat name @param [Hash] opts the options to create the metric with @option opts [Numeric] :sample_rate sample rate, 1 for always @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn’t perform sampling. @option opts [Array<String>] :tags An array of tags @option opts [Numeric] :by decrement value, default 1 @option opts [String] :cardinality The tag cardinality to use @see count
Source
# File lib/datadog/statsd.rb, line 271 def distribution(stat, value, opts = EMPTY_OPTIONS) send_stats(stat, value, DISTRIBUTION_TYPE, opts) end
Sends a value to be tracked as a distribution to the statsd server.
@param [String] stat stat name. @param [Numeric] value distribution value. @param [Hash] opts the options to create the metric with @option opts [Numeric] :sample_rate sample rate, 1 for always @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn’t perform sampling. @option opts [Array<String>] :tags An array of tags @option opts [String] :cardinality The tag cardinality to use @example Report the current user count:
$statsd.distribution('user.count', User.count)
Source
# File lib/datadog/statsd.rb, line 288 def distribution_time(stat, opts = EMPTY_OPTIONS) opts = { sample_rate: opts } if opts.is_a?(Numeric) start = now yield ensure distribution(stat, ((now - start) * 1000).round, opts) end
Reports execution time of the provided block as a distribution.
If the block fails, the stat is still reported, then the error is reraised
@param [String] stat stat name. @param [Numeric] value distribution value. @param [Hash] opts the options to create the metric with @option opts [Numeric] :sample_rate sample rate, 1 for always @option opts [Array<String>] :tags An array of tags @option opts [String] :cardinality The tag cardinality to use @example Report the time (in ms) taken to activate an account
$statsd.distribution_time('account.activate') { @account.activate! }
Source
# File lib/datadog/statsd.rb, line 389 def event(title, text, opts = EMPTY_OPTIONS) telemetry.sent(events: 1) if telemetry forwarder.send_message(serializer.to_event(title, text, opts)) end
This end point allows you to post events to the stream. You can tag them, set priority and even aggregate them with other events.
Aggregation in the stream is made on hostname/event_type/source_type/aggregation_key. If there’s no event type, for example, then that won’t matter; it will be grouped with other events that don’t have an event type.
@param [String] title Event title @param [String] text Event text. Supports newlines (\n
) @param [Hash] opts the additional data about the event @option opts [Integer, String, nil] :date_happened (nil) Assign a timestamp to the event. Default is now when none @option opts [String, nil] :hostname (nil) Assign a hostname to the event. @option opts [String, nil] :aggregation_key (nil) Assign an aggregation key to the event, to group it with some others @option opts [String, nil] :priority (‘normal’) Can be “normal” or “low” @option opts [String, nil] :source_type_name (nil) Assign a source type to the event @option opts [String, nil] :alert_type (‘info’) Can be “error”, “warning”, “info” or “success”. @option opts [Boolean, false] :truncate_if_too_long (false) Truncate the event if it is too long @option opts [Array<String>] :tags tags to be added to every metric @option opts [String] :cardinality The tag cardinality to use @example Report an awful event:
$statsd.event('Something terrible happened', 'The end is near if we do nothing', :alert_type=>'warning', :tags=>['end_of_times','urgent'])
Source
# File lib/datadog/statsd.rb, line 428 def flush(flush_telemetry: false, sync: false) forwarder.flush(flush_telemetry: flush_telemetry, sync: sync) end
Flush the buffer into the connection
Source
# File lib/datadog/statsd.rb, line 240 def gauge(stat, value, opts = EMPTY_OPTIONS) opts = { sample_rate: opts } if opts.is_a?(Numeric) send_stats(stat, value, GAUGE_TYPE, opts) end
Sends an arbitrary gauge value for the given stat to the statsd server.
This is useful for recording things like available disk space, memory usage, and the like, which have different semantics than counters.
@param [String] stat stat name. @param [Numeric] value gauge value. @param [Hash] opts the options to create the metric with @option opts [Numeric] :sample_rate sample rate, 1 for always @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn’t perform sampling. @option opts [Array<String>] :tags An array of tags @option opts [String] :cardinality The tag cardinality to use @example Report the current user count:
$statsd.gauge('user.count', User.count)
Source
# File lib/datadog/statsd.rb, line 256 def histogram(stat, value, opts = EMPTY_OPTIONS) send_stats(stat, value, HISTOGRAM_TYPE, opts) end
Sends a value to be tracked as a histogram to the statsd server.
@param [String] stat stat name. @param [Numeric] value histogram value. @param [Hash] opts the options to create the metric with @option opts [Numeric] :sample_rate sample rate, 1 for always @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn’t perform sampling. @option opts [Array<String>] :tags An array of tags @option opts [String] :cardinality The tag cardinality to use @example Report the current user count:
$statsd.histogram('user.count', User.count)
Source
# File lib/datadog/statsd.rb, line 189 def increment(stat, opts = EMPTY_OPTIONS) opts = { sample_rate: opts } if opts.is_a?(Numeric) incr_value = opts.fetch(:by, 1) count(stat, incr_value, opts) end
Sends an increment (count = 1) for the given stat to the statsd server.
@param [String] stat stat name @param [Hash] opts the options to create the metric with @option opts [Numeric] :sample_rate sample rate, 1 for always @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn’t perform sampling. @option opts [Array<String>] :tags An array of tags @option opts [Numeric] :by increment value, default 1 @option opts [String] :cardinality The tag cardinality to use @see count
Source
# File lib/datadog/statsd.rb, line 363 def service_check(name, status, opts = EMPTY_OPTIONS) telemetry.sent(service_checks: 1) if telemetry forwarder.send_message(serializer.to_service_check(name, status, opts)) end
This method allows you to send custom service check statuses.
@param [String] name Service check name @param [String] status Service check status. @param [Hash] opts the additional data about the service check @option opts [Integer, String, nil] :timestamp (nil) Assign a timestamp to the service check. Default is now when none @option opts [String, nil] :hostname (nil) Assign a hostname to the service check. @option opts [Array<String>, nil] :tags (nil) An array of tags @option opts [String, nil] :message (nil) A message to associate with this service check status @example Report a critical service check status
$statsd.service_check('my.service.check', Statsd::CRITICAL, :tags=>['urgent'])
Source
# File lib/datadog/statsd.rb, line 347 def set(stat, value, opts = EMPTY_OPTIONS) opts = { sample_rate: opts } if opts.is_a?(Numeric) send_stats(stat, value, SET_TYPE, opts) end
Sends a value to be tracked as a set to the statsd server.
@param [String] stat stat name. @param [Numeric] value set value. @param [Hash] opts the options to create the metric with @option opts [Numeric] :sample_rate sample rate, 1 for always @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn’t perform sampling. @option opts [Array<String>] :tags An array of tags @option opts [String] :cardinality The tag cardinality to use @example Record a unique visitory by id:
$statsd.set('visitors.uniques', User.id)
Source
# File lib/datadog/statsd.rb, line 444 def socket_path forwarder.socket_path end
Source
# File lib/datadog/statsd.rb, line 423 def sync_with_outbound_io forwarder.sync_with_outbound_io end
Source
# File lib/datadog/statsd.rb, line 328 def time(stat, opts = EMPTY_OPTIONS) opts = { sample_rate: opts } if opts.is_a?(Numeric) start = now yield ensure timing(stat, ((now - start) * 1000).round, opts) end
Reports execution time of the provided block using {#timing}.
If the block fails, the stat is still reported, then the error is reraised
@param [String] stat stat name @param [Hash] opts the options to create the metric with @option opts [Numeric] :sample_rate sample rate, 1 for always @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn’t perform sampling. @option opts [Array<String>] :tags An array of tags @option opts [String] :cardinality The tag cardinality to use @yield The operation to be timed @see timing
@example Report the time (in ms) taken to activate an account
$statsd.time('account.activate') { @account.activate! }
Source
# File lib/datadog/statsd.rb, line 308 def timing(stat, ms, opts = EMPTY_OPTIONS) opts = { sample_rate: opts } if opts.is_a?(Numeric) send_stats(stat, ms, TIMING_TYPE, opts) end
Sends a timing (in ms) for the given stat to the statsd server. The sample_rate
determines what percentage of the time this report is sent. The statsd server then uses the sample_rate
to correctly track the average timing for the stat.
@param [String] stat stat name @param [Integer] ms timing in milliseconds @param [Hash] opts the options to create the metric with @option opts [Numeric] :sample_rate sample rate, 1 for always @option opts [Boolean] :pre_sampled If true, the client assumes the caller has already sampled metrics at :sample_rate, and doesn’t perform sampling. @option opts [Array<String>] :tags An array of tags @option opts [String] :cardinality The tag cardinality to use
Source
# File lib/datadog/statsd.rb, line 448 def transport_type forwarder.transport_type end
Private Instance Methods
Source
# File lib/datadog/statsd/origin_detection.rb, line 88 def extract_container_info(line) parts = line.strip.split("/") return nil unless parts.last == "hostname" # Expected structure: [..., <group>, <container_id>, ..., "hostname"] container_id = nil group = nil parts.each_with_index do |part, idx| # Match the container id and include the section prior to it. if part.length == 64 && !!(part =~ /\A[0-9a-f]{64}\z/) group = parts[idx - 1] if idx >= 1 container_id = part elsif part.length > 32 && !!(part =~ /\A[0-9a-f]{32}-\d+\z/) group = parts[idx - 1] if idx >= 1 container_id = part elsif !!(part =~ /\A[0-9a-f]{8}(-[0-9a-f]{4}){4}\z/) group = parts[idx - 1] if idx >= 1 container_id = part end end return container_id unless group == "sandboxes" end
Extracts the final container info from a line in mount info
Source
# File lib/datadog/statsd/origin_detection.rb, line 32 def get_cgroup_inode(cgroup_mount_path, proc_self_cgroup_path) content = File.read(proc_self_cgroup_path) rescue nil return nil unless content controllers = parse_cgroup_node_path(content) [CGROUPV1BASECONTROLLER, ''].each do |controller| next unless controllers[controller] segments = [ cgroup_mount_path.chomp('/'), controller.strip, controllers[controller].sub(/^\//, '') ] path = segments.reject(&:empty?).join("/") inode = inode_for_path(path) return inode unless inode.nil? end nil end
Source
# File lib/datadog/statsd/origin_detection.rb, line 140 def get_container_id(user_provided_id, cgroup_fallback) return user_provided_id unless user_provided_id.nil? return nil unless cgroup_fallback container_id = read_container_id("/proc/self/cgroup") return container_id unless container_id.nil? container_id = read_mount_info("/proc/self/mountinfo") return container_id unless container_id.nil? return nil if host_cgroup_namespace? get_cgroup_inode("/sys/fs/cgroup", "/proc/self/cgroup") end
Source
# File lib/datadog/statsd/origin_detection.rb, line 9 def host_cgroup_namespace? stat = File.stat("/proc/self/ns/cgroup") rescue nil return false unless stat stat.ino == HOSTCGROUPNAMESPACEINODE end
Source
# File lib/datadog/statsd/origin_detection.rb, line 54 def inode_for_path(path) stat = File.stat(path) rescue nil return nil unless stat "in-#{stat.ino}" end
Source
# File lib/datadog/statsd.rb, line 458 def now Process.clock_gettime(Process::CLOCK_MONOTONIC) end
Source
# File lib/datadog/statsd.rb, line 480 def origin_detection_enabled?(origin_detection) if !origin_detection.nil? && !origin_detection return false end if ENV['DD_ORIGIN_DETECTION_ENABLED'] return ![ '0', 'f', 'false' ].include?( ENV['DD_ORIGIN_DETECTION_ENABLED'].downcase ) end return true end
Source
# File lib/datadog/statsd/origin_detection.rb, line 15 def parse_cgroup_node_path(lines) res = {} lines.split("\n").each do |line| tokens = line.split(':') next unless tokens.length == 3 controller = tokens[1] path = tokens[2] if controller == CGROUPV1BASECONTROLLER || controller == '' res[controller] = path end end res end
Source
# File lib/datadog/statsd/origin_detection.rb, line 60 def parse_container_id(handle) exp_line = /^\d+:[^:]*:(.+)$/ uuid = /[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}/ container = /[0-9a-f]{64}/ task = /[0-9a-f]{32}-\d+/ exp_container_id = /(#{uuid}|#{container}|#{task})(?:\.scope)?$/ handle.each_line do |line| match = line.match(exp_line) next unless match && match[1] id_match = match[1].match(exp_container_id) return id_match[1] if id_match && id_match[1] end nil end
Source
# File lib/datadog/statsd/origin_detection.rb, line 117 def parse_mount_info(handle) handle.each_line do |line| split = line.split(" ") mnt1 = split[3] mnt2 = split[4] [mnt1, mnt2].each do |line| container_id = extract_container_info(line) return container_id unless container_id.nil? end end nil end
Parse /proc/self/mountinfo to extract the container id. Often container runtimes embed the container id in the mount paths. We parse the mount with a final ‘hostname` component, which is part of the containers `etc/hostname` bind mount.
Source
# File lib/datadog/statsd/origin_detection.rb, line 78 def read_container_id(fpath) handle = File.open(fpath, 'r') rescue nil return nil unless handle id = parse_container_id(handle) handle.close id end
Source
# File lib/datadog/statsd/origin_detection.rb, line 131 def read_mount_info(path) handle = File.open(path, 'r') rescue nil return nil unless handle info = parse_mount_info(handle) handle.close info end
Source
# File lib/datadog/statsd.rb, line 501 def sanitize(external_data) external_data.gsub(/[^[:print:]]|`\|/, '') unless external_data.nil? end
Sanitize the DD_EXTERNAL_ENV input to ensure it doesn’t contain invalid characters that may break the protocol. Removing any non-printable characters and ‘|`.
Source
# File lib/datadog/statsd.rb, line 462 def send_stats(stat, delta, type, opts = EMPTY_OPTIONS) telemetry.sent(metrics: 1) if telemetry sample_rate = opts[:sample_rate] || @sample_rate || 1 cardinality = opts[:cardinality] || @cardinality if sample_rate == 1 || opts[:pre_sampled] || rand <= sample_rate full_stat = if @delay_serialization [stat, delta, type, opts[:tags], sample_rate, cardinality] else serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate, cardinality: cardinality) end forwarder.send_message(full_stat) end end