logstash_writer 0.0.4 → 0.0.5
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 +3 -3
- data/lib/logstash_writer.rb +68 -40
- metadata +1 -1
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3b3f279724a1874b171c9080fb4aae626e844c13b845bec88a50ae832b660a85
         | 
| 4 | 
            +
              data.tar.gz: cb3463bfddf9673808fb874cc1bca6b1b51527f2eb626134bada7cacff91b856
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 41611118bbcc2992196a8c2dc49bec74ce00dc3ec0505b9682132c9e6bcf6e7a8dd48355f35f06cd393f8e2c41460a24b28c53b0c0b0f5ee48c8ec6ec9964503
         | 
| 7 | 
            +
              data.tar.gz: 5527ef9d6bf2a9ed3de0c3483bf43ed6ffd5e69a4e7b8687bdb9d33d258651bdae95a9b990617007578fb10003533840b6f397d87fa2e955836704f9eb064955
         | 
    
        data/README.md
    CHANGED
    
    | @@ -146,9 +146,9 @@ The metrics that are exposed are: | |
| 146 146 |  | 
| 147 147 | 
             
            * **`logstash_writer_connected_to_server`** -- this flag timeseries (can be
         | 
| 148 148 | 
             
              either `1` or `0`) is simply a way for you to quickly determine whether
         | 
| 149 | 
            -
              the writer has a server to talk to, if it wants one | 
| 150 | 
            -
              series will only be `0` if there's an | 
| 151 | 
            -
              server can be found to write it to.
         | 
| 149 | 
            +
              the writer has a server to talk to, if it wants one, and which server it
         | 
| 150 | 
            +
              is connected to.  That is, this time series will only be `0` if there's an
         | 
| 151 | 
            +
              event to write but no logstash server can be found to write it to.
         | 
| 152 152 |  | 
| 153 153 | 
             
            * **`logstash_writer_connect_exceptions_total`** -- a count of exceptions
         | 
| 154 154 | 
             
              raised whilst attempting to connect to a logstash server, labelled by the
         | 
    
        data/lib/logstash_writer.rb
    CHANGED
    
    | @@ -183,7 +183,7 @@ class LogstashWriter | |
| 183 183 | 
             
                      @logger.error("LogstashWriter") { (["Worker thread terminated with exception: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n  ") }
         | 
| 184 184 | 
             
                    end
         | 
| 185 185 | 
             
                    @worker_thread = nil
         | 
| 186 | 
            -
                    @socket_mutex.synchronize { (@ | 
| 186 | 
            +
                    @socket_mutex.synchronize { (@current_target.close; @current_target = nil) if @current_target }
         | 
| 187 187 | 
             
                  end
         | 
| 188 188 | 
             
                end
         | 
| 189 189 |  | 
| @@ -201,11 +201,11 @@ class LogstashWriter | |
| 201 201 | 
             
              #
         | 
| 202 202 | 
             
              def force_disconnect!
         | 
| 203 203 | 
             
                @socket_mutex.synchronize do
         | 
| 204 | 
            -
                  return if @ | 
| 204 | 
            +
                  return if @current_target.nil?
         | 
| 205 205 |  | 
| 206 | 
            -
                  @logger.info("LogstashWriter") { "Forced disconnect from #{describe_peer | 
| 207 | 
            -
                  @ | 
| 208 | 
            -
                  @ | 
| 206 | 
            +
                  @logger.info("LogstashWriter") { "Forced disconnect from #{@current_target.describe_peer}" }
         | 
| 207 | 
            +
                  @current_target.close
         | 
| 208 | 
            +
                  @current_target = nil
         | 
| 209 209 | 
             
                end
         | 
| 210 210 |  | 
| 211 211 | 
             
                nil
         | 
| @@ -237,20 +237,14 @@ class LogstashWriter | |
| 237 237 | 
             
                        event = @queue.shift
         | 
| 238 238 | 
             
                      end
         | 
| 239 239 |  | 
| 240 | 
            -
                       | 
| 241 | 
            -
                         | 
| 242 | 
            -
                        stat_sent( | 
| 240 | 
            +
                      current_target do |t|
         | 
| 241 | 
            +
                        t.socket.puts event[:content].to_json
         | 
| 242 | 
            +
                        stat_sent(t.to_s, event[:arrival_timestamp])
         | 
| 243 243 | 
             
                        @metrics[:write_loop_ok].set({}, 1)
         | 
| 244 244 | 
             
                        error_wait = INITIAL_RETRY_WAIT
         | 
| 245 245 | 
             
                      end
         | 
| 246 246 | 
             
                    rescue StandardError => ex
         | 
| 247 247 | 
             
                      @logger.error("LogstashWriter") { (["Exception in write_loop: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n  ") }
         | 
| 248 | 
            -
                      # If there was some sort of error, there's a non-trivial chance the
         | 
| 249 | 
            -
                      # socket has gone *boom*, so let's invalidate it and go around again
         | 
| 250 | 
            -
                      if @current_socket
         | 
| 251 | 
            -
                        @current_socket.close
         | 
| 252 | 
            -
                        @current_socket = nil
         | 
| 253 | 
            -
                      end
         | 
| 254 248 | 
             
                      @queue_mutex.synchronize { @queue.unshift(event) if event }
         | 
| 255 249 | 
             
                      @metrics[:write_loop_exception].increment(class: ex.class.to_s)
         | 
| 256 250 | 
             
                      @metrics[:write_loop_ok].set({}, 0)
         | 
| @@ -265,14 +259,14 @@ class LogstashWriter | |
| 265 259 | 
             
                end
         | 
| 266 260 | 
             
              end
         | 
| 267 261 |  | 
| 268 | 
            -
              # Yield a  | 
| 262 | 
            +
              # Yield a Target connected to the server we currently believe to be
         | 
| 269 263 | 
             
              # accepting log entries, so that something can send log entries to it.
         | 
| 270 264 | 
             
              #
         | 
| 271 265 | 
             
              # The yielding allows us to centralise all error detection and handling
         | 
| 272 266 | 
             
              # within this one method, and retry sending just by calling `yield` again
         | 
| 273 267 | 
             
              # when we've connected to another server.
         | 
| 274 268 | 
             
              #
         | 
| 275 | 
            -
              def  | 
| 269 | 
            +
              def current_target
         | 
| 276 270 | 
             
                # This could all be handled more cleanly with recursion, but I don't
         | 
| 277 271 | 
             
                # want to fill the stack if we have to retry a lot of times.  Also
         | 
| 278 272 | 
             
                # can't just use `retry` because not all of the "go around again"
         | 
| @@ -281,19 +275,25 @@ class LogstashWriter | |
| 281 275 |  | 
| 282 276 | 
             
                until done
         | 
| 283 277 | 
             
                  @socket_mutex.synchronize do
         | 
| 284 | 
            -
                    if @ | 
| 278 | 
            +
                    if @current_target
         | 
| 285 279 | 
             
                      begin
         | 
| 286 | 
            -
                         | 
| 287 | 
            -
                         | 
| 280 | 
            +
                        # Check that our socket is still good to go; if we don't do this,
         | 
| 281 | 
            +
                        # the other end can disconnect, and because we're never normally
         | 
| 282 | 
            +
                        # reading from the socket, we never get the EOFError that normally
         | 
| 283 | 
            +
                        # results, and so the socket remains in CLOSE_WAIT state *forever*.
         | 
| 284 | 
            +
                        @current_target.socket.read_nonblock(1)
         | 
| 285 | 
            +
             | 
| 286 | 
            +
                        yield @current_target
         | 
| 287 | 
            +
                        @metrics[:connected].set({ server: @current_target.describe_peer }, 1)
         | 
| 288 288 | 
             
                        done = true
         | 
| 289 | 
            -
                      rescue SystemCallError => ex
         | 
| 289 | 
            +
                      rescue SystemCallError, IOError => ex
         | 
| 290 290 | 
             
                        # Something went wrong during the send; disconnect from this
         | 
| 291 291 | 
             
                        # server and recycle
         | 
| 292 | 
            -
                        @metrics[:write_exception].increment(server: describe_peer | 
| 293 | 
            -
                        @ | 
| 294 | 
            -
                        @ | 
| 295 | 
            -
                        @ | 
| 296 | 
            -
                        @ | 
| 292 | 
            +
                        @metrics[:write_exception].increment(server: @current_target.describe_peer, class: ex.class.to_s)
         | 
| 293 | 
            +
                        @metrics[:connected].set({ server: @current_target.describe_peer }, 0)
         | 
| 294 | 
            +
                        @logger.info("LogstashWriter") { "Error while writing to current server #{@current_target.describe_peer}: #{ex.message} (#{ex.class})" }
         | 
| 295 | 
            +
                        @current_target.close
         | 
| 296 | 
            +
                        @current_target = nil
         | 
| 297 297 |  | 
| 298 298 | 
             
                        sleep INITIAL_RETRY_WAIT
         | 
| 299 299 | 
             
                      end
         | 
| @@ -313,10 +313,12 @@ class LogstashWriter | |
| 313 313 |  | 
| 314 314 | 
             
                          if next_server
         | 
| 315 315 | 
             
                            @logger.debug("LogstashWriter") { "Trying to connect to #{next_server.to_s}" }
         | 
| 316 | 
            -
                            @ | 
| 316 | 
            +
                            @current_target = next_server
         | 
| 317 | 
            +
                            # Trigger a connection attempt
         | 
| 318 | 
            +
                            @current_target.socket
         | 
| 317 319 | 
             
                          else
         | 
| 318 320 | 
             
                            @logger.debug("LogstashWriter") { "Could not connect to any server; pausing before trying again" }
         | 
| 319 | 
            -
                            @ | 
| 321 | 
            +
                            @current_target = nil
         | 
| 320 322 | 
             
                            sleep retry_delay
         | 
| 321 323 |  | 
| 322 324 | 
             
                            # Calculate a longer retry delay next time we fail to connect
         | 
| @@ -341,18 +343,6 @@ class LogstashWriter | |
| 341 343 | 
             
                end
         | 
| 342 344 | 
             
              end
         | 
| 343 345 |  | 
| 344 | 
            -
              # Generate a human-readable description of the remote end of the given
         | 
| 345 | 
            -
              # socket.
         | 
| 346 | 
            -
              #
         | 
| 347 | 
            -
              def describe_peer(s)
         | 
| 348 | 
            -
                pa = s.peeraddr
         | 
| 349 | 
            -
                if pa[0] == "AF_INET6"
         | 
| 350 | 
            -
                  "[#{pa[3]}]:#{pa[1]}"
         | 
| 351 | 
            -
                else
         | 
| 352 | 
            -
                  "#{pa[3]}:#{pa[1]}"
         | 
| 353 | 
            -
                end
         | 
| 354 | 
            -
              end
         | 
| 355 | 
            -
             | 
| 356 346 | 
             
              # Turn the server_name given in the constructor into a list of Target
         | 
| 357 347 | 
             
              # objects, suitable for iterating through to find someone to talk to.
         | 
| 358 348 | 
             
              #
         | 
| @@ -489,7 +479,17 @@ class LogstashWriter | |
| 489 479 | 
             
                #    for any reason.
         | 
| 490 480 | 
             
                #
         | 
| 491 481 | 
             
                def socket
         | 
| 492 | 
            -
                  TCPSocket.new(@addr, @port)
         | 
| 482 | 
            +
                  @socket ||= TCPSocket.new(@addr, @port)
         | 
| 483 | 
            +
                end
         | 
| 484 | 
            +
             | 
| 485 | 
            +
                # Shut down the connection.
         | 
| 486 | 
            +
                #
         | 
| 487 | 
            +
                # @return [NilClass]
         | 
| 488 | 
            +
                #
         | 
| 489 | 
            +
                def close
         | 
| 490 | 
            +
                  @socket.close if @socket
         | 
| 491 | 
            +
                  @socket = nil
         | 
| 492 | 
            +
                  @describe_peer = nil
         | 
| 493 493 | 
             
                end
         | 
| 494 494 |  | 
| 495 495 | 
             
                # Simple string representation of the target.
         | 
| @@ -499,6 +499,34 @@ class LogstashWriter | |
| 499 499 | 
             
                def to_s
         | 
| 500 500 | 
             
                  "#{@addr}:#{@port}"
         | 
| 501 501 | 
             
                end
         | 
| 502 | 
            +
             | 
| 503 | 
            +
                # Provide as accurate a representation of what we're *actually* connected
         | 
| 504 | 
            +
                # to as we can, given the constraints of whether we're connected.
         | 
| 505 | 
            +
                #
         | 
| 506 | 
            +
                # To prevent unpleasantness when the other end disconnects but we still
         | 
| 507 | 
            +
                # want to know who we *were* connected to, we cache the result of our
         | 
| 508 | 
            +
                # cogitations.  Just in case.
         | 
| 509 | 
            +
                #
         | 
| 510 | 
            +
                # @return [String]
         | 
| 511 | 
            +
                #
         | 
| 512 | 
            +
                def describe_peer
         | 
| 513 | 
            +
                  @describe_peer ||= begin
         | 
| 514 | 
            +
                    if @socket
         | 
| 515 | 
            +
                      pa = @socket.peeraddr
         | 
| 516 | 
            +
                      if pa[0] == "AF_INET6"
         | 
| 517 | 
            +
                        "[#{pa[3]}]:#{pa[1]}"
         | 
| 518 | 
            +
                      else
         | 
| 519 | 
            +
                        "#{pa[3]}:#{pa[1]}"
         | 
| 520 | 
            +
                      end
         | 
| 521 | 
            +
                    else
         | 
| 522 | 
            +
                      nil
         | 
| 523 | 
            +
                    end
         | 
| 524 | 
            +
                  rescue Errno::ENOTCONN
         | 
| 525 | 
            +
                    # Peer disconnected apparently means "I forgot who I was connected
         | 
| 526 | 
            +
                    # to"... ¯\_(ツ)_/¯
         | 
| 527 | 
            +
                    nil
         | 
| 528 | 
            +
                  end || to_s
         | 
| 529 | 
            +
                end
         | 
| 502 530 | 
             
              end
         | 
| 503 531 |  | 
| 504 532 | 
             
              private_constant :Target
         |