franz 1.6.9 → 2.0.0
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/Readme.md +34 -6
- data/VERSION +1 -1
- data/bin/franz +29 -8
- data/franz.gemspec +2 -0
- data/lib/franz/output/kafka.rb +135 -0
- data/lib/franz/output/rabbitmq.rb +101 -0
- data/lib/franz/output/stdout.rb +70 -0
- data/lib/franz/output.rb +3 -120
- metadata +33 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3daab71d5207b3f6b7e32c4799ada5d1c1597f64
         | 
| 4 | 
            +
              data.tar.gz: f5440a6bff60093e02925e6e0e81804a97ff730c
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: a8e79de017d808a8ea074a313d7fa80de03946174dcb9533afee6d2480a595f882abd829f5c75b100a5ee0f725c36d9dfd438407f7377ad051035fc18e045fd2
         | 
| 7 | 
            +
              data.tar.gz: 5911d098549d5afa4061bb6e7327355bdabc094d096b3c9b1bc9fa7d839b95e20b07bf857ec04f8b645e05a08b384cc89dd705917e0cb51d1298396391a97ca8
         | 
    
        data/Readme.md
    CHANGED
    
    | @@ -9,9 +9,9 @@ doing the bulk of the log processing. Using this setup, RabbitMQ and logstash | |
| 9 9 | 
             
            may be scaled and restarted independently, so new configurations may be applied
         | 
| 10 10 | 
             
            without interrupting those precious log hosts.
         | 
| 11 11 |  | 
| 12 | 
            -
            Even so, Franz was designed to be interruped. Before exiting, Franz  | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 12 | 
            +
            Even so, Franz was designed to be interruped. Before exiting, Franz keeps a log
         | 
| 13 | 
            +
            of checkpoints, which are used to restore application state in the event of a
         | 
| 14 | 
            +
            crash.
         | 
| 15 15 |  | 
| 16 16 | 
             
            He's also got a couple of improvements over logstash. Let's discuss!
         | 
| 17 17 |  | 
| @@ -19,7 +19,7 @@ He's also got a couple of improvements over logstash. Let's discuss! | |
| 19 19 | 
             
            ## Improvements
         | 
| 20 20 |  | 
| 21 21 | 
             
            First let me say logstash is an awesome hunk of software thanks to the hard
         | 
| 22 | 
            -
            work of Jordan Sissel and the entire logstash community. | 
| 22 | 
            +
            work of Jordan Sissel and the entire logstash community.
         | 
| 23 23 |  | 
| 24 24 | 
             
            ### Multiline Flush
         | 
| 25 25 |  | 
| @@ -141,8 +141,12 @@ It's kinda like a JSON version of the Logstash config language: | |
| 141 141 | 
             
                    "play_catchup?": true    // Pick up where we left off
         | 
| 142 142 | 
             
                  },
         | 
| 143 143 |  | 
| 144 | 
            -
             | 
| 144 | 
            +
             | 
| 145 | 
            +
                  // If you provide both RabbitMQ and Kafka configurations, Franz will
         | 
| 146 | 
            +
                  // prefer RabbitMQ. If you provide neither, events are printed to STDOUT
         | 
| 145 147 | 
             
                  "output": {
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                    // RabbitMQ
         | 
| 146 150 | 
             
                    "rabbitmq": {
         | 
| 147 151 |  | 
| 148 152 | 
             
                      // Must be a consistently-hashed exchange!
         | 
| @@ -170,6 +174,20 @@ It's kinda like a JSON version of the Logstash config language: | |
| 170 174 | 
             
                      }
         | 
| 171 175 | 
             
                    },
         | 
| 172 176 |  | 
| 177 | 
            +
                    // Kafka (experimental)
         | 
| 178 | 
            +
                    "kafka": {
         | 
| 179 | 
            +
                      "client_id": "hostname",
         | 
| 180 | 
            +
                      "cluster": [ "localhost:9092" ],
         | 
| 181 | 
            +
                      "type": "sync",
         | 
| 182 | 
            +
                      "compression_codec": "snappy"
         | 
| 183 | 
            +
                      "metadata_refresh_interval_ms": 600000,
         | 
| 184 | 
            +
                      "max_send_retries": 3,
         | 
| 185 | 
            +
                      "retry_backoff_ms": 100,
         | 
| 186 | 
            +
                      "required_acks": 0,
         | 
| 187 | 
            +
                      "ack_timeout_ms": 1500,
         | 
| 188 | 
            +
                      "socket_timeout_ms": 10000
         | 
| 189 | 
            +
                    }
         | 
| 190 | 
            +
             | 
| 173 191 | 
             
                    // Advanced configuration (optional)
         | 
| 174 192 | 
             
                    "stats_interval": 60, // Emit statistics periodically
         | 
| 175 193 | 
             
                    "bound": 25000,       // Limit output queue size
         | 
| @@ -193,4 +211,14 @@ At Blue Jeans, we deploy Franz with Upstart. Here's a minimal config: | |
| 193 211 | 
             
                exec franz
         | 
| 194 212 |  | 
| 195 213 | 
             
            We actually use the [`bjn_franz` cookbook](https://github.com/sczizzo/bjn-franz-cookbook)
         | 
| 196 | 
            -
            for Chef.
         | 
| 214 | 
            +
            for Chef.
         | 
| 215 | 
            +
             | 
| 216 | 
            +
            ### Changelog
         | 
| 217 | 
            +
             | 
| 218 | 
            +
            #### v2.0.0
         | 
| 219 | 
            +
             | 
| 220 | 
            +
            - Added new outputs: `StdOut`, `Kafka` (experimental)
         | 
| 221 | 
            +
             | 
| 222 | 
            +
            #### v1
         | 
| 223 | 
            +
             | 
| 224 | 
            +
            Intial implementation of the file-to-RabbitMQ pipeline
         | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            2.0.0
         | 
    
        data/bin/franz
    CHANGED
    
    | @@ -69,14 +69,33 @@ statz = Franz::Stats.new \ | |
| 69 69 | 
             
              interval: (config[:output][:stats_interval] || 300),
         | 
| 70 70 | 
             
              logger: logger
         | 
| 71 71 |  | 
| 72 | 
            -
             | 
| 73 | 
            -
            #  | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
               | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 72 | 
            +
             | 
| 73 | 
            +
            # Now we'll connect to our output. This creates a new thread in the background,
         | 
| 74 | 
            +
            # which will consume the events generated by our input on io
         | 
| 75 | 
            +
            if config[:output][:rabbitmq]
         | 
| 76 | 
            +
              Franz::Output::RabbitMQ.new \
         | 
| 77 | 
            +
                input: io,
         | 
| 78 | 
            +
                output: config[:output][:rabbitmq],
         | 
| 79 | 
            +
                logger: logger,
         | 
| 80 | 
            +
                tags: config[:output][:tags],
         | 
| 81 | 
            +
                statz: statz
         | 
| 82 | 
            +
             | 
| 83 | 
            +
            elsif config[:output][:kafka]
         | 
| 84 | 
            +
              Franz::Output::Kafka.new \
         | 
| 85 | 
            +
                input: io,
         | 
| 86 | 
            +
                output: config[:output][:kafka],
         | 
| 87 | 
            +
                logger: logger,
         | 
| 88 | 
            +
                tags: config[:output][:tags],
         | 
| 89 | 
            +
                statz: statz
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            else
         | 
| 92 | 
            +
              Franz::Output::StdOut.new \
         | 
| 93 | 
            +
                input: io,
         | 
| 94 | 
            +
                logger: logger,
         | 
| 95 | 
            +
                tags: config[:output][:tags],
         | 
| 96 | 
            +
                statz: statz
         | 
| 97 | 
            +
            end
         | 
| 98 | 
            +
             | 
| 80 99 |  | 
| 81 100 | 
             
            # Franz has only one kind of input, plain text files.
         | 
| 82 101 | 
             
            Franz::Input.new \
         | 
| @@ -87,6 +106,8 @@ Franz::Input.new \ | |
| 87 106 | 
             
              checkpoint_interval: config[:checkpoint_interval],
         | 
| 88 107 | 
             
              statz: statz
         | 
| 89 108 |  | 
| 109 | 
            +
             | 
| 110 | 
            +
             | 
| 90 111 | 
             
            # Ensure memory doesn't grow too large (> 1GB by default)
         | 
| 91 112 | 
             
            def mem_kb ; `ps -o rss= -p #{$$}`.strip.to_i ; end
         | 
| 92 113 |  | 
    
        data/franz.gemspec
    CHANGED
    
    | @@ -19,6 +19,8 @@ Gem::Specification.new do |s| | |
| 19 19 | 
             
              s.add_runtime_dependency 'colorize', '~> 0.7.0'
         | 
| 20 20 | 
             
              s.add_runtime_dependency 'deep_merge', '~> 1.0.0'
         | 
| 21 21 | 
             
              s.add_runtime_dependency 'eventmachine', '= 1.0.5'
         | 
| 22 | 
            +
              s.add_runtime_dependency 'poseidon', '~> 0.0.5'
         | 
| 23 | 
            +
              s.add_runtime_dependency 'snappy', '~> 0.0.11'
         | 
| 22 24 |  | 
| 23 25 | 
             
              s.files         = `git ls-files`.split("\n")
         | 
| 24 26 | 
             
              s.test_files    = `git ls-files -- test/*`.split("\n")
         | 
| @@ -0,0 +1,135 @@ | |
| 1 | 
            +
            require 'thread'
         | 
| 2 | 
            +
            require 'json'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'poseidon'
         | 
| 5 | 
            +
            require 'deep_merge'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
             | 
| 8 | 
            +
            module Franz
         | 
| 9 | 
            +
              module Output
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                # Kafka output for Franz.
         | 
| 12 | 
            +
                class Kafka
         | 
| 13 | 
            +
                  @@host = Socket.gethostname # We'll apply the hostname to all events
         | 
| 14 | 
            +
             | 
| 15 | 
            +
             | 
| 16 | 
            +
                  # Start a new output in the background. We'll consume from the input queue
         | 
| 17 | 
            +
                  # and ship events to STDOUT.
         | 
| 18 | 
            +
                  #
         | 
| 19 | 
            +
                  # @param [Hash] opts options for the output
         | 
| 20 | 
            +
                  # @option opts [Queue] :input ([]) "input" queue
         | 
| 21 | 
            +
                  # @option opts [Queue] :output ([]) "output" configuration
         | 
| 22 | 
            +
                  def initialize opts={}
         | 
| 23 | 
            +
                    opts = {
         | 
| 24 | 
            +
                      logger: Logger.new(STDOUT),
         | 
| 25 | 
            +
                      tags: [],
         | 
| 26 | 
            +
                      input: [],
         | 
| 27 | 
            +
                      output: {
         | 
| 28 | 
            +
                        flush_interval: 10,
         | 
| 29 | 
            +
                        flush_size: 500,
         | 
| 30 | 
            +
                        client_id: @@host,
         | 
| 31 | 
            +
                        cluster: %w[ localhost:9092 ],
         | 
| 32 | 
            +
                        type: 'sync',
         | 
| 33 | 
            +
                        compression_codec: 'snappy',
         | 
| 34 | 
            +
                        metadata_refresh_interval_ms: 600000,
         | 
| 35 | 
            +
                        max_send_retries: 3,
         | 
| 36 | 
            +
                        retry_backoff_ms: 100,
         | 
| 37 | 
            +
                        required_acks: 0,
         | 
| 38 | 
            +
                        ack_timeout_ms: 1500,
         | 
| 39 | 
            +
                        socket_timeout_ms: 10000
         | 
| 40 | 
            +
                      }
         | 
| 41 | 
            +
                    }.deep_merge!(opts)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    @statz = opts[:statz] || Franz::Stats.new
         | 
| 44 | 
            +
                    @statz.create :num_output, 0
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    @logger = opts[:logger]
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    @stop = false
         | 
| 49 | 
            +
                    @foreground = opts[:foreground]
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    @flush_size = opts[:output].delete :flush_size
         | 
| 52 | 
            +
                    @flush_interval = opts[:output].delete :flush_interval
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    kafka_cluster = opts[:output].delete :cluster
         | 
| 55 | 
            +
                    kafka_client_id = opts[:output].delete :client_id
         | 
| 56 | 
            +
                    kafka_config = opts[:output].map { |k,v| v.is_a?(String) ? v.to_sym : v }
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    @kafka = Poseidon::Producer.new \
         | 
| 59 | 
            +
                      kafka_cluster,
         | 
| 60 | 
            +
                      kafka_client_id,
         | 
| 61 | 
            +
                      Hash[kafka_config]
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    @lock = Mutex.new
         | 
| 64 | 
            +
                    @messages = []
         | 
| 65 | 
            +
             | 
| 66 | 
            +
             | 
| 67 | 
            +
                    @thread = Thread.new do
         | 
| 68 | 
            +
                      loop do
         | 
| 69 | 
            +
                        ready_messages = []
         | 
| 70 | 
            +
                        @lock.synchronize do
         | 
| 71 | 
            +
                          ready_messages = @messages
         | 
| 72 | 
            +
                          @messages = []
         | 
| 73 | 
            +
                        end
         | 
| 74 | 
            +
                        @kafka.send_messages ready_messages unless ready_messages.empty?
         | 
| 75 | 
            +
                        log.debug \
         | 
| 76 | 
            +
                          event: 'periodic flush',
         | 
| 77 | 
            +
                          num_messages: ready_messages.size
         | 
| 78 | 
            +
                        sleep @flush_interval
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
             | 
| 83 | 
            +
                    @thread = Thread.new do
         | 
| 84 | 
            +
                      until @stop
         | 
| 85 | 
            +
                        event = opts[:input].shift
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                        log.trace \
         | 
| 88 | 
            +
                          event: 'publish',
         | 
| 89 | 
            +
                          raw: event
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                        payload = JSON::generate(event)
         | 
| 92 | 
            +
                        @messages << Poseidon::MessageToSend.new(event[:type].to_s, payload)
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                        @statz.inc :num_output
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                        if @statz.get(:num_output) % @flush_size == 0
         | 
| 97 | 
            +
                          @kafka.send_messages @messages unless @messages.empty?
         | 
| 98 | 
            +
                          log.debug \
         | 
| 99 | 
            +
                            event: 'flush',
         | 
| 100 | 
            +
                            num_messages: @messages.size
         | 
| 101 | 
            +
                          @messages = []
         | 
| 102 | 
            +
                        end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                      end
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                    log.info event: 'output started'
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    @thread.join if @foreground
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
             | 
| 113 | 
            +
                  # Join the Output thread. Effectively only once.
         | 
| 114 | 
            +
                  def join
         | 
| 115 | 
            +
                    return if @foreground
         | 
| 116 | 
            +
                    @foreground = true
         | 
| 117 | 
            +
                    @thread.join
         | 
| 118 | 
            +
                  end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
             | 
| 121 | 
            +
                  # Stop the Output thread. Effectively only once.
         | 
| 122 | 
            +
                  def stop
         | 
| 123 | 
            +
                    return if @foreground
         | 
| 124 | 
            +
                    @foreground = true
         | 
| 125 | 
            +
                    @thread.kill
         | 
| 126 | 
            +
                    log.info event: 'output stopped'
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
             | 
| 130 | 
            +
                private
         | 
| 131 | 
            +
                  def log ; @logger end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
              end
         | 
| 135 | 
            +
            end
         | 
| @@ -0,0 +1,101 @@ | |
| 1 | 
            +
            require 'json'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bunny'
         | 
| 4 | 
            +
            require 'deep_merge'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Franz
         | 
| 8 | 
            +
              module Output
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                # RabbitMQ output for Franz. You must declare an x-consistent-hash type
         | 
| 11 | 
            +
                # exchange, as we generate random Integers for routing keys.
         | 
| 12 | 
            +
                class RabbitMQ
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  # Start a new output in the background. We'll consume from the input queue
         | 
| 15 | 
            +
                  # and ship events to the configured RabbitMQ cluster.
         | 
| 16 | 
            +
                  #
         | 
| 17 | 
            +
                  # @param [Hash] opts options for the output
         | 
| 18 | 
            +
                  # @option opts [Queue] :input ([]) "input" queue
         | 
| 19 | 
            +
                  # @option opts [Hash] :output ({}) "output" configuration
         | 
| 20 | 
            +
                  def initialize opts={}
         | 
| 21 | 
            +
                    opts = {
         | 
| 22 | 
            +
                      logger: Logger.new(STDOUT),
         | 
| 23 | 
            +
                      tags: [],
         | 
| 24 | 
            +
                      input: [],
         | 
| 25 | 
            +
                      output: {
         | 
| 26 | 
            +
                        exchange: {
         | 
| 27 | 
            +
                          name: 'test',
         | 
| 28 | 
            +
                          durable: true
         | 
| 29 | 
            +
                        },
         | 
| 30 | 
            +
                        connection: {
         | 
| 31 | 
            +
                          host: 'localhost',
         | 
| 32 | 
            +
                          port: 5672
         | 
| 33 | 
            +
                        }
         | 
| 34 | 
            +
                      }
         | 
| 35 | 
            +
                    }.deep_merge!(opts)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    @statz = opts[:statz] || Franz::Stats.new
         | 
| 38 | 
            +
                    @statz.create :num_output, 0
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    @logger = opts[:logger]
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    rabbit = Bunny.new opts[:output][:connection].merge({
         | 
| 43 | 
            +
                      network_recovery_interval: 10.0,
         | 
| 44 | 
            +
                      continuation_timeout: 10_000,
         | 
| 45 | 
            +
                      threaded: false,
         | 
| 46 | 
            +
                      logger: @logger
         | 
| 47 | 
            +
                    })
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    rabbit.start
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    channel  = rabbit.create_channel
         | 
| 52 | 
            +
                    exchange = opts[:output][:exchange].delete(:name)
         | 
| 53 | 
            +
                    exchange = channel.exchange exchange, \
         | 
| 54 | 
            +
                      { type: 'x-consistent-hash' }.merge(opts[:output][:exchange])
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    @stop = false
         | 
| 57 | 
            +
                    @foreground = opts[:foreground]
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    @thread = Thread.new do
         | 
| 60 | 
            +
                      rand = Random.new
         | 
| 61 | 
            +
                      until @stop
         | 
| 62 | 
            +
                        event = opts[:input].shift
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                        log.trace \
         | 
| 65 | 
            +
                          event: 'publish',
         | 
| 66 | 
            +
                          raw: event
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                        exchange.publish \
         | 
| 69 | 
            +
                          JSON::generate(event),
         | 
| 70 | 
            +
                          routing_key: rand.rand(10_000),
         | 
| 71 | 
            +
                          persistent: false
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                        @statz.inc :num_output
         | 
| 74 | 
            +
                      end
         | 
| 75 | 
            +
                    end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    log.info event: 'output started'
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                    @thread.join if @foreground
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  # Join the Output thread. Effectively only once.
         | 
| 83 | 
            +
                  def join
         | 
| 84 | 
            +
                    return if @foreground
         | 
| 85 | 
            +
                    @foreground = true
         | 
| 86 | 
            +
                    @thread.join
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  # Stop the Output thread. Effectively only once.
         | 
| 90 | 
            +
                  def stop
         | 
| 91 | 
            +
                    return if @foreground
         | 
| 92 | 
            +
                    @foreground = true
         | 
| 93 | 
            +
                    @thread.kill
         | 
| 94 | 
            +
                    log.info event: 'output stopped'
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                private
         | 
| 98 | 
            +
                  def log ; @logger end
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
            end
         | 
| @@ -0,0 +1,70 @@ | |
| 1 | 
            +
            require 'json'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'deep_merge'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Franz
         | 
| 7 | 
            +
              module Output
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                # STDOUT output for Franz.
         | 
| 10 | 
            +
                class StdOut
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # Start a new output in the background. We'll consume from the input queue
         | 
| 13 | 
            +
                  # and ship events to STDOUT.
         | 
| 14 | 
            +
                  #
         | 
| 15 | 
            +
                  # @param [Hash] opts options for the output
         | 
| 16 | 
            +
                  # @option opts [Queue] :input ([]) "input" queue
         | 
| 17 | 
            +
                  def initialize opts={}
         | 
| 18 | 
            +
                    opts = {
         | 
| 19 | 
            +
                      logger: Logger.new(STDOUT),
         | 
| 20 | 
            +
                      tags: [],
         | 
| 21 | 
            +
                      input: []
         | 
| 22 | 
            +
                    }.deep_merge!(opts)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    @statz = opts[:statz] || Franz::Stats.new
         | 
| 25 | 
            +
                    @statz.create :num_output, 0
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    @logger = opts[:logger]
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    @stop = false
         | 
| 30 | 
            +
                    @foreground = opts[:foreground]
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    @thread = Thread.new do
         | 
| 33 | 
            +
                      until @stop
         | 
| 34 | 
            +
                        event = opts[:input].shift
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                        log.trace \
         | 
| 37 | 
            +
                          event: 'publish',
         | 
| 38 | 
            +
                          raw: event
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                        puts JSON::generate(event)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                        @statz.inc :num_output
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    log.info event: 'output started'
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    @thread.join if @foreground
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  # Join the Output thread. Effectively only once.
         | 
| 52 | 
            +
                  def join
         | 
| 53 | 
            +
                    return if @foreground
         | 
| 54 | 
            +
                    @foreground = true
         | 
| 55 | 
            +
                    @thread.join
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  # Stop the Output thread. Effectively only once.
         | 
| 59 | 
            +
                  def stop
         | 
| 60 | 
            +
                    return if @foreground
         | 
| 61 | 
            +
                    @foreground = true
         | 
| 62 | 
            +
                    @thread.kill
         | 
| 63 | 
            +
                    log.info event: 'output stopped'
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                private
         | 
| 67 | 
            +
                  def log ; @logger end
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
            end
         | 
    
        data/lib/franz/output.rb
    CHANGED
    
    | @@ -1,121 +1,4 @@ | |
| 1 | 
            -
            require 'json'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require 'bunny'
         | 
| 4 | 
            -
            require 'deep_merge'
         | 
| 5 | 
            -
             | 
| 6 1 | 
             
            require_relative 'stats'
         | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
              # RabbitMQ output for Franz. You must declare an x-consistent-hash type
         | 
| 12 | 
            -
              # exchange, as we generate random Integers for routing keys.
         | 
| 13 | 
            -
              class Output
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                # Start a new output in the background. We'll consume from the input queue
         | 
| 16 | 
            -
                # and ship events to the configured RabbitMQ cluster.
         | 
| 17 | 
            -
                #
         | 
| 18 | 
            -
                # @param [Hash] opts options for the output
         | 
| 19 | 
            -
                # @option opts [Queue] :input ([]) "input" queue
         | 
| 20 | 
            -
                # @option opts [Hash] :output ({}) "output" configuration
         | 
| 21 | 
            -
                def initialize opts={}
         | 
| 22 | 
            -
                  opts = {
         | 
| 23 | 
            -
                    logger: Logger.new(STDOUT),
         | 
| 24 | 
            -
                    tags: [],
         | 
| 25 | 
            -
                    input: [],
         | 
| 26 | 
            -
                    output: {
         | 
| 27 | 
            -
                      exchange: {
         | 
| 28 | 
            -
                        name: 'test',
         | 
| 29 | 
            -
                        durable: true
         | 
| 30 | 
            -
                      },
         | 
| 31 | 
            -
                      connection: {
         | 
| 32 | 
            -
                        host: 'localhost',
         | 
| 33 | 
            -
                        port: 5672
         | 
| 34 | 
            -
                      }
         | 
| 35 | 
            -
                    }
         | 
| 36 | 
            -
                  }.deep_merge!(opts)
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                  @statz = opts[:statz] || Franz::Stats.new
         | 
| 39 | 
            -
                  @statz.create :num_output, 0
         | 
| 40 | 
            -
             | 
| 41 | 
            -
                  @logger = opts[:logger]
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                  rabbit = Bunny.new opts[:output][:connection].merge({
         | 
| 44 | 
            -
                    network_recovery_interval: 10.0,
         | 
| 45 | 
            -
                    continuation_timeout: 10_000,
         | 
| 46 | 
            -
                    threaded: false,
         | 
| 47 | 
            -
                    logger: @logger
         | 
| 48 | 
            -
                  })
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                  rabbit.start
         | 
| 51 | 
            -
             | 
| 52 | 
            -
                  channel  = rabbit.create_channel
         | 
| 53 | 
            -
                  exchange = opts[:output][:exchange].delete(:name)
         | 
| 54 | 
            -
                  exchange = channel.exchange exchange, \
         | 
| 55 | 
            -
                    { type: 'x-consistent-hash' }.merge(opts[:output][:exchange])
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                  @stop = false
         | 
| 58 | 
            -
                  @foreground = opts[:foreground]
         | 
| 59 | 
            -
             | 
| 60 | 
            -
                  @thread = Thread.new do
         | 
| 61 | 
            -
                    rand = Random.new
         | 
| 62 | 
            -
                    until @stop
         | 
| 63 | 
            -
                      event = opts[:input].shift
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                      if event[:path]
         | 
| 66 | 
            -
                        # Can't use sub!, because :path is frozen
         | 
| 67 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/seam-builds/rel', ''
         | 
| 68 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/seam-builds/live', ''
         | 
| 69 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/seam-builds/beta', ''
         | 
| 70 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/builds/rel', ''
         | 
| 71 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/builds/live', ''
         | 
| 72 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/builds/beta', ''
         | 
| 73 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/cobalt-builds/rel', ''
         | 
| 74 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/cobalt-builds/live', ''
         | 
| 75 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/cobalt-builds/beta', ''
         | 
| 76 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/rivet-builds', ''
         | 
| 77 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/denim/logs', ''
         | 
| 78 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/seam/logs', ''
         | 
| 79 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser/rivet/bjn/logs', ''
         | 
| 80 | 
            -
                        event[:path] = event[:path].sub '/home/denimuser', ''
         | 
| 81 | 
            -
                        event[:path] = event[:path].sub '/var/log/franz', ''
         | 
| 82 | 
            -
                        event[:path] = event[:path].sub '/var/log', ''
         | 
| 83 | 
            -
                      end
         | 
| 84 | 
            -
             | 
| 85 | 
            -
                      log.trace \
         | 
| 86 | 
            -
                        event: 'publish',
         | 
| 87 | 
            -
                        raw: event
         | 
| 88 | 
            -
             | 
| 89 | 
            -
                      exchange.publish \
         | 
| 90 | 
            -
                        JSON::generate(event),
         | 
| 91 | 
            -
                        routing_key: rand.rand(10_000),
         | 
| 92 | 
            -
                        persistent: false
         | 
| 93 | 
            -
             | 
| 94 | 
            -
                      @statz.inc :num_output
         | 
| 95 | 
            -
                    end
         | 
| 96 | 
            -
                  end
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                  log.info event: 'output started'
         | 
| 99 | 
            -
             | 
| 100 | 
            -
                  @thread.join if @foreground
         | 
| 101 | 
            -
                end
         | 
| 102 | 
            -
             | 
| 103 | 
            -
                # Join the Output thread. Effectively only once.
         | 
| 104 | 
            -
                def join
         | 
| 105 | 
            -
                  return if @foreground
         | 
| 106 | 
            -
                  @foreground = true
         | 
| 107 | 
            -
                  @thread.join
         | 
| 108 | 
            -
                end
         | 
| 109 | 
            -
             | 
| 110 | 
            -
                # Stop the Output thread. Effectively only once.
         | 
| 111 | 
            -
                def stop
         | 
| 112 | 
            -
                  return if @foreground
         | 
| 113 | 
            -
                  @foreground = true
         | 
| 114 | 
            -
                  @thread.kill
         | 
| 115 | 
            -
                  log.info event: 'output stopped'
         | 
| 116 | 
            -
                end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
              private
         | 
| 119 | 
            -
                def log ; @logger end
         | 
| 120 | 
            -
              end
         | 
| 121 | 
            -
            end
         | 
| 2 | 
            +
            require_relative 'output/kafka'
         | 
| 3 | 
            +
            require_relative 'output/stdout'
         | 
| 4 | 
            +
            require_relative 'output/rabbitmq'
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: franz
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version:  | 
| 4 | 
            +
              version: 2.0.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Sean Clemmer
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2015- | 
| 11 | 
            +
            date: 2015-03-06 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: slog
         | 
| @@ -94,6 +94,34 @@ dependencies: | |
| 94 94 | 
             
                - - '='
         | 
| 95 95 | 
             
                  - !ruby/object:Gem::Version
         | 
| 96 96 | 
             
                    version: 1.0.5
         | 
| 97 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 98 | 
            +
              name: poseidon
         | 
| 99 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 100 | 
            +
                requirements:
         | 
| 101 | 
            +
                - - "~>"
         | 
| 102 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 103 | 
            +
                    version: 0.0.5
         | 
| 104 | 
            +
              type: :runtime
         | 
| 105 | 
            +
              prerelease: false
         | 
| 106 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 107 | 
            +
                requirements:
         | 
| 108 | 
            +
                - - "~>"
         | 
| 109 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 110 | 
            +
                    version: 0.0.5
         | 
| 111 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 112 | 
            +
              name: snappy
         | 
| 113 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 114 | 
            +
                requirements:
         | 
| 115 | 
            +
                - - "~>"
         | 
| 116 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 117 | 
            +
                    version: 0.0.11
         | 
| 118 | 
            +
              type: :runtime
         | 
| 119 | 
            +
              prerelease: false
         | 
| 120 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 121 | 
            +
                requirements:
         | 
| 122 | 
            +
                - - "~>"
         | 
| 123 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 124 | 
            +
                    version: 0.0.11
         | 
| 97 125 | 
             
            description: Aggregate log file events and send them elsewhere.
         | 
| 98 126 | 
             
            email: sclemmer@bluejeans.com
         | 
| 99 127 | 
             
            executables:
         | 
| @@ -118,6 +146,9 @@ files: | |
| 118 146 | 
             
            - lib/franz/input_config.rb
         | 
| 119 147 | 
             
            - lib/franz/metadata.rb
         | 
| 120 148 | 
             
            - lib/franz/output.rb
         | 
| 149 | 
            +
            - lib/franz/output/kafka.rb
         | 
| 150 | 
            +
            - lib/franz/output/rabbitmq.rb
         | 
| 151 | 
            +
            - lib/franz/output/stdout.rb
         | 
| 121 152 | 
             
            - lib/franz/sash.rb
         | 
| 122 153 | 
             
            - lib/franz/stats.rb
         | 
| 123 154 | 
             
            - lib/franz/tail.rb
         |