class SemanticLogger::Formatters::Signalfx

Attributes

counter_name[RW]
dimensions[RW]
gauge_name[RW]
hash[RW]
token[RW]

Public Class Methods

new(token:, dimensions: nil, gauge_name: "Application.average", counter_name: "Application.counter", time_format: :ms, **args) click to toggle source
Calls superclass method SemanticLogger::Formatters::Base::new
# File lib/semantic_logger/formatters/signalfx.rb, line 7
def initialize(token:,
               dimensions: nil,
               gauge_name: "Application.average",
               counter_name: "Application.counter",
               time_format: :ms,
               **args)

  @token        = token
  @dimensions   = dimensions.map(&:to_sym) if dimensions
  @gauge_name   = gauge_name
  @counter_name = counter_name

  super(time_format: time_format, **args)
end

Public Instance Methods

batch(logs, logger) click to toggle source

Returns [Hash] a batch of log messages. Signalfx has a minimum resolution of 1 second. Metrics of the same type, time (second), and dimensions can be aggregated together.

# File lib/semantic_logger/formatters/signalfx.rb, line 108
def batch(logs, logger)
  self.logger = logger

  data = {}
  logs.each do |log|
    self.hash = {}
    self.log  = log

    metric; time; value; format_dimensions

    if log.duration
      gauges = (data[:gauge] ||= [])
      add_gauge(gauges, hash)

      # Also send a count metric whenever it is a gauge so that it can be counted.
      unless log.dimensions
        count_hash          = hash.dup
        count_hash[:value]  = log.metric_amount || 1
        count_hash[:metric] = counter_name
        counters            = (data[:counter] ||= [])
        add_counter(counters, count_hash)
      end
    else
      counters = (data[:counter] ||= [])
      add_counter(counters, hash)
    end
  end

  data.to_json
end
call(log, logger) click to toggle source

Returns [Hash] log message in Signalfx format.

# File lib/semantic_logger/formatters/signalfx.rb, line 80
def call(log, logger)
  self.hash   = {}
  self.log    = log
  self.logger = logger

  metric; time; value; format_dimensions

  # gauge, counter, or cumulative_counter
  data = {}
  if log.duration
    data[:gauge] = [hash]
    # Also send a count metric whenever it is a gauge so that it can be counted.
    unless log.dimensions
      count_hash          = hash.dup
      count_hash[:value]  = log.metric_amount || 1
      count_hash[:metric] = counter_name
      data[:counter]      = [count_hash]
    end
  else
    data[:counter] = [hash]
  end

  data.to_json
end
format_dimensions() click to toggle source

Dimensions for this metric

# File lib/semantic_logger/formatters/signalfx.rb, line 58
def format_dimensions
  h = (hash[:dimensions] ||= {})
  if log.dimensions
    log.dimensions.each_pair do |name, value|
      value   = value.to_s
      h[name] = value unless value.empty?
    end
  else
    log.named_tags.each_pair do |name, value|
      name  = name.to_sym
      value = value.to_s
      next if value.empty?

      h[name] = value if dimensions&.include?(name)
    end
  end
  h[:host]        = logger.host if log_host && logger.host
  h[:application] = logger.application if log_application && logger.application
  h[:environment] = logger.environment if log_environment && logger.environment
end
metric() click to toggle source

Create SignalFx friendly metric.

Strip leading '/'
Convert remaining '/' to '.'
# File lib/semantic_logger/formatters/signalfx.rb, line 25
def metric
  name = log.metric.to_s.sub(%r{\A/+}, "")
  if log.dimensions
    name.tr!("/", ".")
    hash[:metric] = name
  else
    # Extract class and action from metric name
    names = name.split("/")
    h     = (hash[:dimensions] ||= {})
    if names.size > 1
      h[:action] = names.pop
      h[:class]  = names.join("::")
    else
      h[:class]  = "Unknown"
      h[:action] = names.first || log.metric
    end

    hash[:metric] = log.duration ? gauge_name : counter_name
  end
end
time() click to toggle source

Date & time

# File lib/semantic_logger/formatters/signalfx.rb, line 47
def time
  # 1 second resolution, represented as ms.
  hash[:timestamp] = log.time.to_i * 1000
end
value() click to toggle source

Value of this metric

# File lib/semantic_logger/formatters/signalfx.rb, line 53
def value
  hash[:value] = log.metric_amount || log.duration || 1
end

Private Instance Methods

add_counter(counters, metric) click to toggle source

Sum counters with the same time (second), name, and dimensions.

# File lib/semantic_logger/formatters/signalfx.rb, line 146
def add_counter(counters, metric)
  existing = find_match(counters, metric)
  existing ? existing[:value] += metric[:value] : counters << metric
end
add_gauge(gauges, metric) click to toggle source
# File lib/semantic_logger/formatters/signalfx.rb, line 141
def add_gauge(gauges, metric)
  gauges << metric
end
find_match(list, metric) click to toggle source

Find Metrics with the same timestamp, metric name, and dimensions.

# File lib/semantic_logger/formatters/signalfx.rb, line 152
def find_match(list, metric)
  list.find do |item|
    (item[:timestamp] == metric[:timestamp]) &&
      (item[:metric] == metric[:metric]) &&
      (item[:dimensions] == metric[:dimensions])
  end
end