class LogStash::Outputs::Stackify

Use logstash to ship logs to Stackify

This contains the HTTP client code within it's own namespace. It is based off of the logstash-mixin-http_client plugin.

Constants

CONTENT_TYPE
MAX_ATTEMPTS
METHOD
RETRYABLE_CODES
RETRYABLE_MANTICORE_EXCEPTIONS
URL

Public Instance Methods

close() click to toggle source
# File lib/logstash/outputs/stackify.rb, line 78
def close
  http_client.close
end
multi_receive(events) click to toggle source
# File lib/logstash/outputs/stackify.rb, line 74
def multi_receive(events)
  send_events(events)
end
register() click to toggle source
# File lib/logstash/outputs/stackify.rb, line 63
def register
  @headers = {
      "Content-Type" => CONTENT_TYPE,
      "Accept" => CONTENT_TYPE,
      "X-Stackify-PV" => "V1",
      "X-Stackify-Key" => @key.to_s
  }
  @url = URL
  @logger.info("Stackify Plugin Registered")
end

Private Instance Methods

post_log_msg_group(log_msg_group, attempt) click to toggle source
# File lib/logstash/outputs/stackify.rb, line 201
def post_log_msg_group(log_msg_group, attempt)

  @logger.debug("Stackify Request, Attempt " + attempt.to_s)

  if attempt > MAX_ATTEMPTS
    @logger.warn(
        "Max attempts exceeded, dropping events",
        :attempt => attempt
    )
    return false
  end

  response =
      begin
        body =  log_msg_group.to_json
        http_client.post(@url, :body => body, :headers => @headers)
      rescue Exception => e
        if retryable_exception?(e)
          @logger.warn(
              "Attempt #{attempt}, retryable exception when making request",
              :attempt => attempt,
              :class => e.class.name,
              :message => e.message,
              :backtrace => e.backtrace
          )
          return post_log_msg_group(log_msg_group, attempt + 1)
        else
          @logger.error(
              "Attempt #{attempt}, fatal exception when making request",
              :attempt => attempt,
              :class => e.class.name,
              :message => e.message,
              :backtrace => e.backtrace
          )
          return false
        end
      end

  @logger.debug("Stackify Response: " + response.code.to_s)

  code = response.code

  if code >= 200 && code <= 299
    true
  elsif RETRYABLE_CODES.include?(code)
    @logger.warn(
        "Bad retryable response from the Stackify API",
        :attempt => attempt,
        :code => code
    )
    sleep_time = sleep_for_attempt(attempt)
    sleep(sleep_time)
    post_log_msg_group(log_msg_group, attempt + 1)
  else
    @logger.error(
        "Bad fatal response from the Stackify API",
        :attempt => attempt,
        :code => code
    )
    false
  end

end
retryable_exception?(e) click to toggle source
# File lib/logstash/outputs/stackify.rb, line 266
def retryable_exception?(e)
  RETRYABLE_MANTICORE_EXCEPTIONS.any? do |exception_class|
    e.is_a?(exception_class)
  end
end
send_events(events) click to toggle source
# File lib/logstash/outputs/stackify.rb, line 84
def send_events(events)
  begin

    default_level = 'info'
    default_servername = Socket.gethostname
    log_msg_groups = Hash.new

    events.each do |event|

      begin

        # message
        default_message = event.get('message')
        message = default_message
        unless @message_field.nil?
          message = event.get(@message_field.to_s)
          if message.nil? || message.empty?
            message = default_message
          end
        end

        # level
        level = default_level
        unless @level_field.nil?
          level = event.get(@level_field.to_s)
          if level.nil? || level.empty?
            level = default_level
          end
        end
        level = level.downcase

        # application name
        app_name = @default_app_name.to_s
        unless @app_name_field.nil?
          app_name = event.get(@app_name_field.to_s)
          if app_name.nil? || app_name.empty?
            app_name = @default_app_name.to_s
          end
        end

        # env name
        env_name = @default_env_name.to_s
        unless @env_name_field.nil?
          env_name = event.get(@env_name_field.to_s)
          if env_name.nil? || env_name.empty?
            env_name = @default_env_name.to_s
          end
        end

        # server name
        server_name = default_servername
        unless @server_name_field.nil?
          server_name = event.get(@server_name_field.to_s)
          if server_name.nil? || server_name.empty?
            server_name = default_servername
          end
        end

        # timestamp
        timestamp = nil
        begin
          timestamp = (event.get('@timestamp').to_f * 1000).to_i
        rescue StandardError => e
          @logger.warn('Error parsing @timestamp: ' + e.to_s)
        end
        if timestamp.nil?
          timestamp = DateTime.now.strftime('%Q')
        end

        # generate key
        log_msg_group_key = app_name + '|' + env_name + '|' + server_name

        # setup log msg group if needed
        unless log_msg_groups.key?(log_msg_group_key)
          log_msg_group = Hash.new
          log_msg_group['Env'] = env_name
          log_msg_group['ServerName'] = server_name
          log_msg_group['AppName'] = app_name
          log_msg_group['Logger'] = 'Logstash'
          log_msg_group['Platform'] = 'Logstash'
          log_msg_group['Msgs'] = Array.new
          log_msg_groups[log_msg_group_key] = log_msg_group
        end

        # generate log msg
        log_msg = Hash.new
        log_msg['Msg'] = message
        log_msg['EpochMs'] = timestamp
        log_msg['Level'] = level

        # add to log msg group
        log_msg_groups[log_msg_group_key]['Msgs'].push(log_msg)

      rescue StandardError => e
        @logger.warn(e.to_s)
      end

    end

    # post
    log_msg_groups.each do |key, log_msg_group|

      begin
        post_log_msg_group(log_msg_group, 0)
      rescue StandardError => e
        @logger.warn(e.to_s)
      end

    end

  rescue StandardError => e
    @logger.warn(e.to_s)
  end

end
sleep_for_attempt(attempt) click to toggle source
# File lib/logstash/outputs/stackify.rb, line 272
def sleep_for_attempt(attempt)
  sleep_for = attempt ** 2
  sleep_for = sleep_for <= 60 ? sleep_for : 60
  (sleep_for / 2) + (rand(0..sleep_for) / 2)
end