1
0
mirror of https://github.com/meineerde/rackstash.git synced 2026-01-10 15:51:31 +00:00
Holger Just 00786283f0 Add IO adapter
This adapter allows to write newline-separated log lines to an existing
IO object, e.g. STDOUT. It does not allow to reopen the IO device.
2017-06-16 23:04:55 +02:00

78 lines
2.7 KiB
Ruby

# Copyright 2017 Holger Just
#
# This software may be modified and distributed under the terms
# of the MIT license. See the LICENSE.txt file for details.
require 'thread'
require 'rackstash/adapters/adapter'
module Rackstash
module Adapters
# This adapter allows to write logs to an existing `IO` object, e.g.,
# `STDOUT`, an open file, a `StringIO` object, ...
#
# When writing a [12factor](https://12factor.net/logs) app, you can use this
# adapter to write formatted logs to `STDOUT` of the process to be captured
# by the environment and eventually sent to a log collector.
#
# Concurrent writes to this adapter will be serialized to ensure there are
# no overlapping writes. You still have to ensure that there are no other
# writes to the IO object from outside this adapter to ensure there that
# is no overlapping data visible on the IO object.
#
# Note that with some deployment models involving pre-forked application
# servers, e.g., Unicorn or Puma servers with multiple worker processes, the
# combined `STDOUT` stream of multiple processes can cause interleaved data
# when writing large log lines (typically > 4 KB). If you are using such a
# deployment model and expect large log lines, you should consider using a
# different adapter to ensure consistent logs.
#
# Suitable adapters include:
#
# * {Rackstash::Adapters::File} - When writing to a file, we ensure with
# explicit file locks that all data is written consistently.
# * {Rackstash::Adapters::TCP} - With a single TCP connection per adapter
# instance, the receiver can handle the log lines separately.
class IO < Adapter
register_for ->(o) { o.respond_to?(:write) && o.respond_to?(:close) }
# @param io [#write, #close] an IO object. It must at least respond to
# `write` and `close`.
def initialize(io)
unless io.respond_to?(:write) && io.respond_to?(:close)
raise TypeError, "#{io.inspect} does not look like an IO object"
end
@io = io
@mutex = Mutex.new
end
# Write a single log line with a trailing newline character to the IO
# object.
#
# @param log [#to_s] the encoded log event
# @return [nil]
def write_single(log)
@mutex.synchronize do
@io.write normalize_line(log)
end
nil
end
# Close the IO object.
#
# After closing, no further writes are possible. Further attempts to
# {#write} will result in an exception being thrown.
#
# @return [nil]
def close
@mutex.synchronize do
@io.close
end
nil
end
end
end
end