reacter 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/bin/reacter +68 -0
 - data/lib/reacter/adapter.rb +6 -6
 - data/lib/reacter/adapters/amqp.rb +1 -1
 - data/lib/reacter/adapters/file.rb +21 -6
 - data/lib/reacter/agents/decider.rb +86 -23
 - data/lib/reacter/agents/logger.rb +2 -1
 - data/lib/reacter/core.rb +3 -3
 - data/lib/reacter/message.rb +15 -0
 - data/lib/reacter/parsers/collectd.rb +16 -0
 - data/lib/reacter/parsers/default.rb +4 -0
 - data/lib/reacter/parsers/graphite.rb +11 -0
 - data/lib/reacter/parsers/json.rb +6 -0
 - data/lib/reacter/parsers/tsdb.rb +21 -0
 - data/lib/reacter/util.rb +5 -0
 - metadata +4 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 85466ac92fce993fd08a3b321ac6080013cc0fba
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 02b305a9c8195c60239e4801ab0507e7fd3748df
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 74fea13a574c9385fab7783b7d220c47acb692c4701123e093c518f0806e8e9c59e04c1502a84945359709f6d881f58cbd6bd193d0702fad12d4fffee1b302d4
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 1eaeef3dc876f4b85f3e32dcb1700cfa91dffa76b35283c881c1e56e0f8407ec4c1caf73f4ebfcf9d84c61520ad15df4a2fb9dc9389e051320210373b4e14e46
         
     | 
    
        data/bin/reacter
    ADDED
    
    | 
         @@ -0,0 +1,68 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            #
         
     | 
| 
      
 3 
     | 
    
         
            +
            # reacter - a utility for consuming, transforming, and routing monitoring data from various sources
         
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
      
 5 
     | 
    
         
            +
            # DESCRIPTION
         
     | 
| 
      
 6 
     | 
    
         
            +
            #        This script creates a mirror of a source directory, manipulating
         
     | 
| 
      
 7 
     | 
    
         
            +
            #        path components based on rules defined in dirmangle.yaml.  The
         
     | 
| 
      
 8 
     | 
    
         
            +
            #        purpose of this is to provide links to (or copies of) data using
         
     | 
| 
      
 9 
     | 
    
         
            +
            #        an alternative directory tree.
         
     | 
| 
      
 10 
     | 
    
         
            +
            #
         
     | 
| 
      
 11 
     | 
    
         
            +
            #        -C, --config FILE
         
     | 
| 
      
 12 
     | 
    
         
            +
            #                The location of the configuration YAML to load
         
     | 
| 
      
 13 
     | 
    
         
            +
            #
         
     | 
| 
      
 14 
     | 
    
         
            +
            #        -o, --option NAME VALUE
         
     | 
| 
      
 15 
     | 
    
         
            +
            #                Set the option NAME to VALUE
         
     | 
| 
      
 16 
     | 
    
         
            +
            #
         
     | 
| 
      
 17 
     | 
    
         
            +
            #        -v, --verbosity LEVEL
         
     | 
| 
      
 18 
     | 
    
         
            +
            #                Set the log verbosity level [debug, info (default), warn, error, fatal]
         
     | 
| 
      
 19 
     | 
    
         
            +
            #
         
     | 
| 
      
 20 
     | 
    
         
            +
            # AUTHOR
         
     | 
| 
      
 21 
     | 
    
         
            +
            #        Gary Hetzel <garyhetzel@gmail.com>
         
     | 
| 
      
 22 
     | 
    
         
            +
            #
         
     | 
| 
      
 23 
     | 
    
         
            +
            require 'reacter'
         
     | 
| 
      
 24 
     | 
    
         
            +
            require 'optparse'
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            class Reacter
         
     | 
| 
      
 27 
     | 
    
         
            +
              class CLI
         
     | 
| 
      
 28 
     | 
    
         
            +
                class<<self
         
     | 
| 
      
 29 
     | 
    
         
            +
                  def run()
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @options = {}
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    (OptionParser.new do |opts|
         
     | 
| 
      
 33 
     | 
    
         
            +
                      opts.banner = "Usage: reacter [options]"
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    # -----------------------------------------------------------------------------
         
     | 
| 
      
 36 
     | 
    
         
            +
                      opts.on('-C', '--config FILE', 'Configuration YAML location') do |file|
         
     | 
| 
      
 37 
     | 
    
         
            +
                        if File.exists?(file)
         
     | 
| 
      
 38 
     | 
    
         
            +
                          @options[:configfile] = file
         
     | 
| 
      
 39 
     | 
    
         
            +
                        else
         
     | 
| 
      
 40 
     | 
    
         
            +
                          raise "Configuration file #{file} does not exist"
         
     | 
| 
      
 41 
     | 
    
         
            +
                        end
         
     | 
| 
      
 42 
     | 
    
         
            +
                      end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    # -----------------------------------------------------------------------------
         
     | 
| 
      
 45 
     | 
    
         
            +
                      @options[:options] = {}
         
     | 
| 
      
 46 
     | 
    
         
            +
                      opts.on('-o', '--option NAME=VALUE', 'Set the option NAME to VALUE') do |pair|
         
     | 
| 
      
 47 
     | 
    
         
            +
                        name, value = pair.split('=', 2)
         
     | 
| 
      
 48 
     | 
    
         
            +
                        @options[:options].set(name.strip.chomp, value) unless name.empty?
         
     | 
| 
      
 49 
     | 
    
         
            +
                      end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    # -----------------------------------------------------------------------------
         
     | 
| 
      
 52 
     | 
    
         
            +
                      opts.on('-v', '--verbosity LEVEL', 'Logging verbosity') do |level|
         
     | 
| 
      
 53 
     | 
    
         
            +
                        level = level.to_s.downcase
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                        if ['debug', 'info', 'warn', 'error', 'fatal'].include?(level)
         
     | 
| 
      
 56 
     | 
    
         
            +
                          @options[:loglevel] = level.to_sym
         
     | 
| 
      
 57 
     | 
    
         
            +
                        end
         
     | 
| 
      
 58 
     | 
    
         
            +
                      end
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end).parse!
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  # options parsed, pass them to a new Reacter
         
     | 
| 
      
 62 
     | 
    
         
            +
                    Reacter.start(@options)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
            Reacter::CLI.run()
         
     | 
    
        data/lib/reacter/adapter.rb
    CHANGED
    
    | 
         @@ -14,8 +14,8 @@ class Reacter 
     | 
|
| 
       14 
14 
     | 
    
         
             
                attr :config
         
     | 
| 
       15 
15 
     | 
    
         
             
                attr :type
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
                def initialize()
         
     | 
| 
       18 
     | 
    
         
            -
                  @config = Reacter.get('global.adapter', {})
         
     | 
| 
      
 17 
     | 
    
         
            +
                def initialize(config=nil)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @config = (config || Reacter.get('global.adapter', {}))
         
     | 
| 
       19 
19 
     | 
    
         
             
                  @type = @config.get('type')
         
     | 
| 
       20 
20 
     | 
    
         
             
                  Util.info("Loading adapter #{@type}...")
         
     | 
| 
       21 
21 
     | 
    
         
             
                end
         
     | 
| 
         @@ -27,12 +27,12 @@ class Reacter 
     | 
|
| 
       27 
27 
     | 
    
         | 
| 
       28 
28 
     | 
    
         
             
              # implement: send a message suitable for consumption by an instance of reacter
         
     | 
| 
       29 
29 
     | 
    
         
             
              #            in listen mode
         
     | 
| 
       30 
     | 
    
         
            -
                def send(message)
         
     | 
| 
      
 30 
     | 
    
         
            +
                def send(message, format=nil)
         
     | 
| 
       31 
31 
     | 
    
         
             
                  false
         
     | 
| 
       32 
32 
     | 
    
         
             
                end
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
              # implement: poll for a new message and return it
         
     | 
| 
       35 
     | 
    
         
            -
                def poll()
         
     | 
| 
      
 35 
     | 
    
         
            +
                def poll(&block)
         
     | 
| 
       36 
36 
     | 
    
         
             
                  false
         
     | 
| 
       37 
37 
     | 
    
         
             
                end
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
         @@ -42,11 +42,11 @@ class Reacter 
     | 
|
| 
       42 
42 
     | 
    
         
             
                end
         
     | 
| 
       43 
43 
     | 
    
         | 
| 
       44 
44 
     | 
    
         
             
                class<<self
         
     | 
| 
       45 
     | 
    
         
            -
                  def create(type)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  def create(type, config=nil)
         
     | 
| 
       46 
46 
     | 
    
         
             
                    if type
         
     | 
| 
       47 
47 
     | 
    
         
             
                      begin
         
     | 
| 
       48 
48 
     | 
    
         
             
                        require "reacter/adapters/#{type}"
         
     | 
| 
       49 
     | 
    
         
            -
                        rv = (Reacter.const_get("#{type.capitalize}Adapter").new())
         
     | 
| 
      
 49 
     | 
    
         
            +
                        rv = (Reacter.const_get("#{type.capitalize}Adapter").new(config))
         
     | 
| 
       50 
50 
     | 
    
         
             
                        return rv
         
     | 
| 
       51 
51 
     | 
    
         | 
| 
       52 
52 
     | 
    
         
             
                      rescue LoadError
         
     | 
| 
         @@ -12,18 +12,33 @@ class Reacter 
     | 
|
| 
       12 
12 
     | 
    
         
             
                    @_stdin = true
         
     | 
| 
       13 
13 
     | 
    
         
             
                  else
         
     | 
| 
       14 
14 
     | 
    
         
             
                    @_stdin = false
         
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    readfile = (@config.get('filename') || @config.get('file.read'))
         
     | 
| 
      
 17 
     | 
    
         
            +
                    writefile = @config.get('file.write')
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    @_input = File.open(File.expand_path(readfile), 'r+') if readfile
         
     | 
| 
      
 20 
     | 
    
         
            +
                    @_output = File.open(File.expand_path(writefile), 'a') if writefile
         
     | 
| 
       16 
21 
     | 
    
         
             
                  end
         
     | 
| 
       17 
22 
     | 
    
         
             
                end
         
     | 
| 
       18 
23 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
                def send(message)
         
     | 
| 
       20 
     | 
    
         
            -
                   
     | 
| 
      
 24 
     | 
    
         
            +
                def send(message, format=nil)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  if defined?(@_output)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    message = Message.dump(message, format)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @_output.puts(message) if message
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @_output.flush()
         
     | 
| 
      
 29 
     | 
    
         
            +
                  else
         
     | 
| 
      
 30 
     | 
    
         
            +
                    Util.warn("file: Attempting to send without a valid output file handle")
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
       21 
32 
     | 
    
         
             
                end
         
     | 
| 
       22 
33 
     | 
    
         | 
| 
       23 
34 
     | 
    
         
             
                def poll(&block)
         
     | 
| 
       24 
     | 
    
         
            -
                   
     | 
| 
       25 
     | 
    
         
            -
                     
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 35 
     | 
    
         
            +
                  if @_input
         
     | 
| 
      
 36 
     | 
    
         
            +
                    loop do
         
     | 
| 
      
 37 
     | 
    
         
            +
                      line = @_input.gets
         
     | 
| 
      
 38 
     | 
    
         
            +
                      yield Message.parse(line)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  else
         
     | 
| 
      
 41 
     | 
    
         
            +
                    Util.warn("file: Attempting to poll without a valid input file handle")
         
     | 
| 
       27 
42 
     | 
    
         
             
                  end
         
     | 
| 
       28 
43 
     | 
    
         
             
                end
         
     | 
| 
       29 
44 
     | 
    
         | 
| 
         @@ -7,8 +7,56 @@ class Reacter 
     | 
|
| 
       7 
7 
     | 
    
         
             
              class DeciderAgent < Agent
         
     | 
| 
       8 
8 
     | 
    
         
             
                class EmitAlert < Exception; end
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
      
 10 
     | 
    
         
            +
              # local memory persistence mechanism for state tracking
         
     | 
| 
      
 11 
     | 
    
         
            +
                class MemoryPersist
         
     | 
| 
      
 12 
     | 
    
         
            +
                  class<<self
         
     | 
| 
      
 13 
     | 
    
         
            +
                    def setup()
         
     | 
| 
      
 14 
     | 
    
         
            +
                      @_alerts = {}
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                    def init(source, metric)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    # initialize in-memory alert tracking (per source, per metric)
         
     | 
| 
      
 19 
     | 
    
         
            +
                      @_alerts[source] ||= {}
         
     | 
| 
      
 20 
     | 
    
         
            +
                      @_alerts[source][metric] ||= ({
         
     | 
| 
      
 21 
     | 
    
         
            +
                        :count           => 1,
         
     | 
| 
      
 22 
     | 
    
         
            +
                        :last_state      => nil,
         
     | 
| 
      
 23 
     | 
    
         
            +
                        :last_seen       => nil,
         
     | 
| 
      
 24 
     | 
    
         
            +
                        :new_alert       => false,
         
     | 
| 
      
 25 
     | 
    
         
            +
                        :has_ever_failed => false
         
     | 
| 
      
 26 
     | 
    
         
            +
                      })
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    def get(source, metric, key)
         
     | 
| 
      
 30 
     | 
    
         
            +
                      @_alerts[source][metric][key] rescue nil
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    def set(source, metric, key, value)
         
     | 
| 
      
 34 
     | 
    
         
            +
                      (@_alerts[source][metric][key] = value) rescue nil
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              # redis persistence mechanism for shared state tracking
         
     | 
| 
      
 40 
     | 
    
         
            +
                class RedisPersist
         
     | 
| 
      
 41 
     | 
    
         
            +
                  #require 'redis'
         
     | 
| 
      
 42 
     | 
    
         
            +
                  class<<self
         
     | 
| 
      
 43 
     | 
    
         
            +
                    def setup()
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    def init(source, metric)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    def get(source, metric, key)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    def set(source, metric, key, value)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
       10 
57 
     | 
    
         
             
                STATES=[:okay, :warning, :critical]
         
     | 
| 
       11 
58 
     | 
    
         
             
                COMPARISONS=%w{not is above below}
         
     | 
| 
      
 59 
     | 
    
         
            +
                DEFAULT_PERSISTENCE='memory'
         
     | 
| 
       12 
60 
     | 
    
         | 
| 
       13 
61 
     | 
    
         
             
                def initialize()
         
     | 
| 
       14 
62 
     | 
    
         
             
                  super
         
     | 
| 
         @@ -18,7 +66,8 @@ class Reacter 
     | 
|
| 
       18 
66 
     | 
    
         
             
                    @_state_names[STATES[i]] = i
         
     | 
| 
       19 
67 
     | 
    
         
             
                  end
         
     | 
| 
       20 
68 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                  @ 
     | 
| 
      
 69 
     | 
    
         
            +
                  @_persistence = DeciderAgent.const_get(@config.get('options.persistence.type', DEFAULT_PERSISTENCE).capitalize+'Persist')
         
     | 
| 
      
 70 
     | 
    
         
            +
                  @_persistence.setup()
         
     | 
| 
       22 
71 
     | 
    
         
             
                end
         
     | 
| 
       23 
72 
     | 
    
         | 
| 
       24 
73 
     | 
    
         
             
                def received(message)
         
     | 
| 
         @@ -30,9 +79,12 @@ class Reacter 
     | 
|
| 
       30 
79 
     | 
    
         
             
                    return false unless @config['sources']['any']
         
     | 
| 
       31 
80 
     | 
    
         
             
                  end
         
     | 
| 
       32 
81 
     | 
    
         | 
| 
      
 82 
     | 
    
         
            +
                  s = message.source
         
     | 
| 
      
 83 
     | 
    
         
            +
                  m = message.metric
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
       33 
85 
     | 
    
         
             
                # build effective configuration rule for this message
         
     | 
| 
       34 
     | 
    
         
            -
                  general = (@config['sources']['any'][ 
     | 
| 
       35 
     | 
    
         
            -
                  specific = (@config['sources'][ 
     | 
| 
      
 86 
     | 
    
         
            +
                  general = (@config['sources']['any'][m] or {} rescue {})
         
     | 
| 
      
 87 
     | 
    
         
            +
                  specific = (@config['sources'][s][m] or {} rescue {})
         
     | 
| 
       36 
88 
     | 
    
         
             
                  rule = specific.deep_merge!(general)
         
     | 
| 
       37 
89 
     | 
    
         | 
| 
       38 
90 
     | 
    
         
             
                # quit early for empty rules
         
     | 
| 
         @@ -41,18 +93,17 @@ class Reacter 
     | 
|
| 
       41 
93 
     | 
    
         
             
                # quit early for invalid rules
         
     | 
| 
       42 
94 
     | 
    
         
             
                  return false unless (rule['threshold'] and rule['actions'])
         
     | 
| 
       43 
95 
     | 
    
         | 
| 
      
 96 
     | 
    
         
            +
                # passthrough mode
         
     | 
| 
      
 97 
     | 
    
         
            +
                #   if in passthrough mode, all messages that are present in the configuration
         
     | 
| 
      
 98 
     | 
    
         
            +
                #   will be passed regardless of state.  this is useful in conjunction with
         
     | 
| 
      
 99 
     | 
    
         
            +
                #   the relay agent to offload the decision making to another reacter instance
         
     | 
| 
      
 100 
     | 
    
         
            +
                  return message if (@config['options'] and @config['options']['passthrough'])
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
       44 
102 
     | 
    
         
             
                # default return value: false
         
     | 
| 
       45 
103 
     | 
    
         
             
                  rv = false
         
     | 
| 
       46 
104 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
                # initialize  
     | 
| 
       48 
     | 
    
         
            -
                   
     | 
| 
       49 
     | 
    
         
            -
                  @_alerts[message.source][message.metric] ||= ({
         
     | 
| 
       50 
     | 
    
         
            -
                    :count           => 1,
         
     | 
| 
       51 
     | 
    
         
            -
                    :last_state      => nil,
         
     | 
| 
       52 
     | 
    
         
            -
                    :last_seen       => nil,
         
     | 
| 
       53 
     | 
    
         
            -
                    :new_alert       => false,
         
     | 
| 
       54 
     | 
    
         
            -
                    :has_ever_failed => false
         
     | 
| 
       55 
     | 
    
         
            -
                  })
         
     | 
| 
      
 105 
     | 
    
         
            +
                # initialize persistence for this source/metric
         
     | 
| 
      
 106 
     | 
    
         
            +
                  persist_init(s,m)
         
     | 
| 
       56 
107 
     | 
    
         | 
| 
       57 
108 
     | 
    
         
             
                # get current state of this message according to the rule
         
     | 
| 
       58 
109 
     | 
    
         
             
                  state, comparison = state_of(message, rule)
         
     | 
| 
         @@ -60,24 +111,24 @@ class Reacter 
     | 
|
| 
       60 
111 
     | 
    
         
             
                  begin
         
     | 
| 
       61 
112 
     | 
    
         
             
                    # a non-okay alert has occurred, this metric can now be said to have failed
         
     | 
| 
       62 
113 
     | 
    
         
             
                      unless state == :okay
         
     | 
| 
       63 
     | 
    
         
            -
                         
     | 
| 
      
 114 
     | 
    
         
            +
                        persist_set(s,m, :has_ever_failed, true)
         
     | 
| 
       64 
115 
     | 
    
         
             
                      end
         
     | 
| 
       65 
116 
     | 
    
         | 
| 
       66 
117 
     | 
    
         
             
                    # the state has changed, reset counter
         
     | 
| 
       67 
     | 
    
         
            -
                      if  
     | 
| 
       68 
     | 
    
         
            -
                         
     | 
| 
       69 
     | 
    
         
            -
                         
     | 
| 
      
 118 
     | 
    
         
            +
                      if persist_get(s,m, :last_state) != state
         
     | 
| 
      
 119 
     | 
    
         
            +
                        persist_set(s,m, :new_alert, true)
         
     | 
| 
      
 120 
     | 
    
         
            +
                        persist_set(s,m, :count, 1)
         
     | 
| 
       70 
121 
     | 
    
         
             
                      end
         
     | 
| 
       71 
122 
     | 
    
         | 
| 
       72 
123 
     | 
    
         
             
                      hits = ((rule['threshold'][state.to_s][hits] rescue rule['hits']) or rule['hits'] or 1).to_i
         
     | 
| 
       73 
124 
     | 
    
         
             
                      persist = ((rule['threshold'][state.to_s]['persist'] === true) rescue false)
         
     | 
| 
       74 
125 
     | 
    
         | 
| 
       75 
126 
     | 
    
         
             
                    # only raise every n alerts
         
     | 
| 
       76 
     | 
    
         
            -
                      if  
     | 
| 
      
 127 
     | 
    
         
            +
                      if persist_get(s,m, :count) >= hits
         
     | 
| 
       77 
128 
     | 
    
         
             
                      # raise the alert if it's new or if we're persistently reminding of alerts
         
     | 
| 
       78 
     | 
    
         
            -
                        if  
     | 
| 
      
 129 
     | 
    
         
            +
                        if persist_get(s,m, :new_alert) or persist
         
     | 
| 
       79 
130 
     | 
    
         
             
                        # only emit if this metric has ever failed
         
     | 
| 
       80 
     | 
    
         
            -
                          if  
     | 
| 
      
 131 
     | 
    
         
            +
                          if persist_get(s,m, :has_ever_failed)
         
     | 
| 
       81 
132 
     | 
    
         
             
                            raise EmitAlert
         
     | 
| 
       82 
133 
     | 
    
         
             
                          end
         
     | 
| 
       83 
134 
     | 
    
         
             
                        end
         
     | 
| 
         @@ -93,12 +144,12 @@ class Reacter 
     | 
|
| 
       93 
144 
     | 
    
         
             
                    rv = message
         
     | 
| 
       94 
145 
     | 
    
         | 
| 
       95 
146 
     | 
    
         
             
                  # alert is emitted, it's not new anymore
         
     | 
| 
       96 
     | 
    
         
            -
                     
     | 
| 
      
 147 
     | 
    
         
            +
                    persist_set(s,m, :new_alert, false)
         
     | 
| 
       97 
148 
     | 
    
         | 
| 
       98 
149 
     | 
    
         
             
                  ensure
         
     | 
| 
       99 
     | 
    
         
            -
                     
     | 
| 
       100 
     | 
    
         
            -
                     
     | 
| 
       101 
     | 
    
         
            -
                     
     | 
| 
      
 150 
     | 
    
         
            +
                    persist_set(s,m, :count, persist_get(s,m, :count) + 1)
         
     | 
| 
      
 151 
     | 
    
         
            +
                    persist_set(s,m, :last_state, state)
         
     | 
| 
      
 152 
     | 
    
         
            +
                    persist_set(s,m, :last_seen, Time.now.to_i)
         
     | 
| 
       102 
153 
     | 
    
         
             
                  end
         
     | 
| 
       103 
154 
     | 
    
         | 
| 
       104 
155 
     | 
    
         
             
                  return rv
         
     | 
| 
         @@ -143,6 +194,18 @@ class Reacter 
     | 
|
| 
       143 
194 
     | 
    
         
             
                    return false
         
     | 
| 
       144 
195 
     | 
    
         
             
                  end
         
     | 
| 
       145 
196 
     | 
    
         
             
                end
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                def persist_init(source, metric)
         
     | 
| 
      
 199 
     | 
    
         
            +
                  @_persistence.init(source, metric)
         
     | 
| 
      
 200 
     | 
    
         
            +
                end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                def persist_get(source, metric, key)
         
     | 
| 
      
 203 
     | 
    
         
            +
                  @_persistence.get(source, metric, key)
         
     | 
| 
      
 204 
     | 
    
         
            +
                end
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                def persist_set(source, metric, key, value)
         
     | 
| 
      
 207 
     | 
    
         
            +
                  @_persistence.set(source, metric, key, value)
         
     | 
| 
      
 208 
     | 
    
         
            +
                end
         
     | 
| 
       146 
209 
     | 
    
         
             
              end
         
     | 
| 
       147 
210 
     | 
    
         
             
            end
         
     | 
| 
       148 
211 
     | 
    
         | 
| 
         @@ -6,7 +6,8 @@ require 'reacter/agent' 
     | 
|
| 
       6 
6 
     | 
    
         
             
            class Reacter
         
     | 
| 
       7 
7 
     | 
    
         
             
              class LoggerAgent < Agent
         
     | 
| 
       8 
8 
     | 
    
         
             
                def received(message)
         
     | 
| 
       9 
     | 
    
         
            -
                  Util.info(" 
     | 
| 
      
 9 
     | 
    
         
            +
                  Util.info("logger: [#{message.state or :unknown}] #{message.source}/#{message.metric}")
         
     | 
| 
      
 10 
     | 
    
         
            +
                  message
         
     | 
| 
       10 
11 
     | 
    
         
             
                end
         
     | 
| 
       11 
12 
     | 
    
         
             
              end
         
     | 
| 
       12 
13 
     | 
    
         
             
            end
         
     | 
    
        data/lib/reacter/core.rb
    CHANGED
    
    | 
         @@ -16,7 +16,7 @@ class Reacter 
     | 
|
| 
       16 
16 
     | 
    
         
             
                  @_adapter = nil
         
     | 
| 
       17 
17 
     | 
    
         
             
                  @_agents = []
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
                  Reacter.load_config()
         
     | 
| 
      
 19 
     | 
    
         
            +
                  Reacter.load_config(args.first || {})
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
                  load_adapters()
         
     | 
| 
       22 
22 
     | 
    
         
             
                  load_agents()
         
     | 
| 
         @@ -92,9 +92,9 @@ class Reacter 
     | 
|
| 
       92 
92 
     | 
    
         
             
              end
         
     | 
| 
       93 
93 
     | 
    
         | 
| 
       94 
94 
     | 
    
         
             
              class<<self
         
     | 
| 
       95 
     | 
    
         
            -
                def start()
         
     | 
| 
      
 95 
     | 
    
         
            +
                def start(config={})
         
     | 
| 
       96 
96 
     | 
    
         
             
                  EM.run do
         
     | 
| 
       97 
     | 
    
         
            -
                    reacter = EM.connect('127.0.0.1', 9000, Reacter::Core)
         
     | 
| 
      
 97 
     | 
    
         
            +
                    reacter = EM.connect('127.0.0.1', 9000, Reacter::Core, config[:options])
         
     | 
| 
       98 
98 
     | 
    
         
             
                    reacter.run()
         
     | 
| 
       99 
99 
     | 
    
         
             
                  end
         
     | 
| 
       100 
100 
     | 
    
         
             
                end
         
     | 
    
        data/lib/reacter/message.rb
    CHANGED
    
    | 
         @@ -5,6 +5,7 @@ class Reacter 
     | 
|
| 
       5 
5 
     | 
    
         
             
              class Message
         
     | 
| 
       6 
6 
     | 
    
         
             
                require 'hashlib'
         
     | 
| 
       7 
7 
     | 
    
         
             
                DEFAULT_SOURCE_NAME='default'
         
     | 
| 
      
 8 
     | 
    
         
            +
                DEFAULT_FORMAT=:json
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
10 
     | 
    
         
             
                def initialize(message={})
         
     | 
| 
       10 
11 
     | 
    
         
             
                  @_data = {
         
     | 
| 
         @@ -52,6 +53,20 @@ class Reacter 
     | 
|
| 
       52 
53 
     | 
    
         
             
                    end
         
     | 
| 
       53 
54 
     | 
    
         
             
                  end
         
     | 
| 
       54 
55 
     | 
    
         | 
| 
      
 56 
     | 
    
         
            +
                  def dump(message, format=nil)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    load_parsers() unless defined?(@@_parsers)
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    if @@_parsers
         
     | 
| 
      
 60 
     | 
    
         
            +
                      format = DEFAULT_FORMAT unless format
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                      if @@_parsers.has_key?(format.to_sym)
         
     | 
| 
      
 63 
     | 
    
         
            +
                        return @@_parsers[format.to_sym].dump(message)
         
     | 
| 
      
 64 
     | 
    
         
            +
                      end
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    return nil
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
       55 
70 
     | 
    
         
             
                  def parse(body)
         
     | 
| 
       56 
71 
     | 
    
         
             
                    load_parsers()
         
     | 
| 
       57 
72 
     | 
    
         | 
| 
         @@ -22,6 +22,22 @@ class Reacter 
     | 
|
| 
       22 
22 
     | 
    
         
             
                        :attributes => attributes
         
     | 
| 
       23 
23 
     | 
    
         
             
                      }
         
     | 
| 
       24 
24 
     | 
    
         
             
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    def dump(message)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      message = message.to_h if message.is_a?(Message)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      return nil unless message.is_a?(Hash)
         
     | 
| 
      
 29 
     | 
    
         
            +
                      return nil unless message[:source]
         
     | 
| 
      
 30 
     | 
    
         
            +
                      return nil unless message[:metric]
         
     | 
| 
      
 31 
     | 
    
         
            +
                      return nil unless message[:value]
         
     | 
| 
      
 32 
     | 
    
         
            +
                      return nil unless message[:time]
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                      ([
         
     | 
| 
      
 35 
     | 
    
         
            +
                        'PUTVAL',
         
     | 
| 
      
 36 
     | 
    
         
            +
                        "#{message[:source]}/#{message[:metric]}",
         
     | 
| 
      
 37 
     | 
    
         
            +
                        (message[:attributes] || {}).collect{|k,v| "#{k}=#{v}"}.join(';'),
         
     | 
| 
      
 38 
     | 
    
         
            +
                        "#{(message[:time] / 1000).to_i}:#{message[:value]}"
         
     | 
| 
      
 39 
     | 
    
         
            +
                      ].join(' ')).strip
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
       25 
41 
     | 
    
         
             
                  end
         
     | 
| 
       26 
42 
     | 
    
         
             
                end
         
     | 
| 
       27 
43 
     | 
    
         
             
              end
         
     | 
| 
         @@ -20,6 +20,17 @@ class Reacter 
     | 
|
| 
       20 
20 
     | 
    
         
             
                        :time       => (message[2].to_i * 1000)
         
     | 
| 
       21 
21 
     | 
    
         
             
                      }
         
     | 
| 
       22 
22 
     | 
    
         
             
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    def dump(message)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      message = message.to_h if message.is_a?(Message)
         
     | 
| 
      
 26 
     | 
    
         
            +
                      return nil unless message.is_a?(Hash)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      #return nil unless message[:source]
         
     | 
| 
      
 28 
     | 
    
         
            +
                      return nil unless message[:metric]
         
     | 
| 
      
 29 
     | 
    
         
            +
                      return nil unless message[:value]
         
     | 
| 
      
 30 
     | 
    
         
            +
                      return nil unless message[:time]
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                      "#{message[:metric]} #{message[:value]} #{(message[:time] / 1000).to_i}".strip
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
       23 
34 
     | 
    
         
             
                  end
         
     | 
| 
       24 
35 
     | 
    
         
             
                end
         
     | 
| 
       25 
36 
     | 
    
         
             
              end
         
     | 
    
        data/lib/reacter/parsers/json.rb
    CHANGED
    
    
    
        data/lib/reacter/parsers/tsdb.rb
    CHANGED
    
    | 
         @@ -18,6 +18,27 @@ class Reacter 
     | 
|
| 
       18 
18 
     | 
    
         
             
                        :attributes => Hash[message[4..-1].collect{|i| i.split('=',2) }]
         
     | 
| 
       19 
19 
     | 
    
         
             
                      }
         
     | 
| 
       20 
20 
     | 
    
         
             
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    def dump(message)
         
     | 
| 
      
 23 
     | 
    
         
            +
                      message = message.to_h if message.is_a?(Message)
         
     | 
| 
      
 24 
     | 
    
         
            +
                      return nil unless message.is_a?(Hash)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      return nil unless message[:source]
         
     | 
| 
      
 26 
     | 
    
         
            +
                      return nil unless message[:metric]
         
     | 
| 
      
 27 
     | 
    
         
            +
                      return nil unless message[:value]
         
     | 
| 
      
 28 
     | 
    
         
            +
                      return nil unless message[:time]
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                      attributes = {
         
     | 
| 
      
 31 
     | 
    
         
            +
                        :host => message[:source]
         
     | 
| 
      
 32 
     | 
    
         
            +
                      }.merge(message[:attributes] || {})
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                      ([
         
     | 
| 
      
 35 
     | 
    
         
            +
                        'PUT',
         
     | 
| 
      
 36 
     | 
    
         
            +
                        message[:metric],
         
     | 
| 
      
 37 
     | 
    
         
            +
                        (message[:time] / 1000).to_i,
         
     | 
| 
      
 38 
     | 
    
         
            +
                        message[:value],
         
     | 
| 
      
 39 
     | 
    
         
            +
                        attributes.collect{|k,v| "#{k}=#{v}" }.join(' ')
         
     | 
| 
      
 40 
     | 
    
         
            +
                      ].join(' ')).strip
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
       21 
42 
     | 
    
         
             
                  end
         
     | 
| 
       22 
43 
     | 
    
         
             
                end
         
     | 
| 
       23 
44 
     | 
    
         
             
              end
         
     | 
    
        data/lib/reacter/util.rb
    CHANGED
    
    | 
         @@ -4,11 +4,16 @@ class Reacter 
     | 
|
| 
       4 
4 
     | 
    
         
             
              class Util
         
     | 
| 
       5 
5 
     | 
    
         
             
                class<<self
         
     | 
| 
       6 
6 
     | 
    
         
             
                  require 'logger'
         
     | 
| 
      
 7 
     | 
    
         
            +
                  require 'socket'
         
     | 
| 
       7 
8 
     | 
    
         | 
| 
       8 
9 
     | 
    
         
             
                  @@_logger = {
         
     | 
| 
       9 
10 
     | 
    
         
             
                    :default => Logger.new(STDOUT)
         
     | 
| 
       10 
11 
     | 
    
         
             
                  }
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
      
 13 
     | 
    
         
            +
                  def signature(custom=nil)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    [%x{hostname -f}, Process.pid, custom].compact.join(':')
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
       12 
17 
     | 
    
         
             
                  def log(message, severity=:info, log=:default)
         
     | 
| 
       13 
18 
     | 
    
         
             
                    @@_logger[log] = Logger.new(STDOUT) unless @@_logger[log]
         
     | 
| 
       14 
19 
     | 
    
         
             
                    @@_logger[log].send(severity, [*message].join(' '))
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: reacter
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.0.2
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Gary Hetzel
         
     | 
| 
         @@ -55,7 +55,8 @@ dependencies: 
     | 
|
| 
       55 
55 
     | 
    
         
             
            description: A utility for consuming, transforming, and routing monitoring data from
         
     | 
| 
       56 
56 
     | 
    
         
             
              various sources
         
     | 
| 
       57 
57 
     | 
    
         
             
            email: ghetzel@outbrain.com
         
     | 
| 
       58 
     | 
    
         
            -
            executables: 
     | 
| 
      
 58 
     | 
    
         
            +
            executables:
         
     | 
| 
      
 59 
     | 
    
         
            +
            - reacter
         
     | 
| 
       59 
60 
     | 
    
         
             
            extensions: []
         
     | 
| 
       60 
61 
     | 
    
         
             
            extra_rdoc_files: []
         
     | 
| 
       61 
62 
     | 
    
         
             
            files:
         
     | 
| 
         @@ -75,6 +76,7 @@ files: 
     | 
|
| 
       75 
76 
     | 
    
         
             
            - lib/reacter/parsers/json.rb
         
     | 
| 
       76 
77 
     | 
    
         
             
            - lib/reacter/parsers/tsdb.rb
         
     | 
| 
       77 
78 
     | 
    
         
             
            - lib/reacter/util.rb
         
     | 
| 
      
 79 
     | 
    
         
            +
            - bin/reacter
         
     | 
| 
       78 
80 
     | 
    
         
             
            homepage: http://outbrain.github.com/reacter/
         
     | 
| 
       79 
81 
     | 
    
         
             
            licenses: []
         
     | 
| 
       80 
82 
     | 
    
         
             
            metadata: {}
         
     |