logstash-logger 0.12.0 → 0.13.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b5f101aba35d73c9bb62e77c6a5be82321c54de
4
- data.tar.gz: 85d7303ebb637bda83b42dec2794c7551983e22e
3
+ metadata.gz: cc77de67a993f6d92e48eb51a03403545adb4a36
4
+ data.tar.gz: 155105ccaa41672d379ba39f1f58891079814ef1
5
5
  SHA512:
6
- metadata.gz: 0ce2d236f92070efa47bf40cbc708a2fa3c12d65f1e611813b6170678ce4b00e5889661f0e9b9d3ce7760f0f6eef12ea1773eb9ac98b843f8ae57f0514d4463f
7
- data.tar.gz: 5c038e89a27a7467a76da3a7715d2161ca5afdf3f1bd6ec25216c433aaedd979399cbc0d133dd96b20760970637543814ddea4f80314612df174d915f369f3d7
6
+ metadata.gz: 49065907ddd39ea7dce437da0d9e4d640667a48d056f487b23948a6a4a97bb2d8a117c6370a430b9465f739b3225a1ac8cb9419f8144928b30af78a118c18a96
7
+ data.tar.gz: 945c34c855bf8f0fe36b1a40d8b6daaf72ac452590da3e8f3d74069f3fdcc41865f6926513255bd2bca91e661130dbe7ec8a1f82d5813879ca625fd9df7ee135
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.13.0
2
+ - Support for sending messages to multiple loggers. Each one can have a different formatter. [#58](https://github.com/dwbutler/logstash-logger/pull/58)
3
+ - Fixes tagged logging support when using a custom formatter.
4
+
1
5
  ## 0.12.0
2
6
  - Support for other formatters, including custom formatters. [#56](https://github.com/dwbutler/logstash-logger/pull/56)
3
7
  - Support for CEE output.
data/Gemfile CHANGED
@@ -2,4 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- #gem 'json', :platform => :ruby_18
5
+ gem 'codecov', require: false, group: :test
data/README.md CHANGED
@@ -1,15 +1,16 @@
1
1
  # LogStashLogger
2
- [![Build Status](https://travis-ci.org/dwbutler/logstash-logger.png?branch=master)](https://travis-ci.org/dwbutler/logstash-logger) [![Code Climate](https://codeclimate.com/github/dwbutler/logstash-logger.png)](https://codeclimate.com/github/dwbutler/logstash-logger)
2
+ [![Build Status](https://travis-ci.org/dwbutler/logstash-logger.svg?branch=master)](https://travis-ci.org/dwbutler/logstash-logger) [![Code Climate](https://codeclimate.com/github/dwbutler/logstash-logger/badges/gpa.svg)](https://codeclimate.com/github/dwbutler/logstash-logger) [![codecov.io](http://codecov.io/github/dwbutler/logstash-logger/coverage.svg?branch=master)](http://codecov.io/github/dwbutler/logstash-logger?branch=master)
3
3
 
4
- This gem implements a subclass of Ruby's Logger class that logs directly to [logstash](http://logstash.net).
5
- It writes to a logstash listener over a UDP (default) or TCP connection, in logstash JSON format. This is an improvement over
4
+ LogStashLogger extends Ruby's `Logger` class to log directly to [logstash](http://logstash.net).
5
+ It supports writing to various outputs in logstash JSON format. This is an improvement over
6
6
  writing to a file or syslog since logstash can receive the structured data directly.
7
7
 
8
8
  ## Features
9
9
 
10
10
  * Can write directly to logstash over a UDP or TCP/SSL connection.
11
11
  * Can write to a file, Redis, a unix socket, stdout or stderr.
12
- * Always writes in logstash JSON format.
12
+ * Writes in logstash JSON format, but supports other formats as well.
13
+ * Can write to multiple outputs.
13
14
  * Logger can take a string message, a hash, a `LogStash::Event`, an object, or a JSON string as input.
14
15
  * Events are automatically populated with message, timestamp, host, and severity.
15
16
  * Easily integrates with Rails via configuration.
@@ -50,16 +51,53 @@ stderr_logger = LogStashLogger.new(type: :stderr)
50
51
  io_logger = LogStashLogger.new(type: :io, io: io)
51
52
 
52
53
  # Use a different formatter
53
- cee_logger = LogStashLogger.new(type: :tcp, host: 'logsene-receiver-syslog.sematext.com', port: 514, formatter: :cee_syslog)
54
- custom_formatted_logger = LogStashLogger.new(type: :redis, formatter: MyCustomFormatter)
55
- lambda_formatted_logger = LogStashLogger.new(type: :stdout, formatter: ->(severity, time, progname, msg) { "[#{progname}] #{msg}" })
56
- ruby_default_formatter_logger = LogStashLogger.new(type: :file, path: 'log/development.log', formatter: ::Logger::Formatter)
57
-
58
- # Multiple Outputs
59
- multi_logger = LogStashLogger.new([{type: :file, path: 'log/development.log'}, {type: :udp, host: 'localhost', port: 5228}])
60
-
61
- # Balancing messages between several outputs
62
- balancer_logger = LogStashLogger.new(type: :balancer, outputs: [{type: :udp, host: 'host1', port: 5228}, {type: :udp, host: 'host2', port: 5228}])
54
+ cee_logger = LogStashLogger.new(
55
+ type: :tcp,
56
+ host: 'logsene-receiver-syslog.sematext.com',
57
+ port: 514,
58
+ formatter: :cee_syslog
59
+ )
60
+
61
+ custom_formatted_logger = LogStashLogger.new(
62
+ type: :redis,
63
+ formatter: MyCustomFormatter
64
+ )
65
+
66
+ lambda_formatted_logger = LogStashLogger.new(
67
+ type: :stdout,
68
+ formatter: ->(severity, time, progname, msg) { "[#{progname}] #{msg}" }
69
+ )
70
+
71
+ ruby_default_formatter_logger = LogStashLogger.new(
72
+ type: :file,
73
+ path: 'log/development.log',
74
+ formatter: ::Logger::Formatter
75
+ )
76
+
77
+ # Send messages to multiple outputs. Each output will have the same format.
78
+ multi_delegating_logger = LogStashLogger.new(
79
+ type: :multi_delegator,
80
+ outputs: [
81
+ { type: :file, path: 'log/development.log' },
82
+ { type: :udp, host: 'localhost', port: 5228 }
83
+ ])
84
+
85
+ # Balance messages between several outputs
86
+ balancer_logger = LogStashLogger.new(
87
+ type: :balancer,
88
+ outputs: [
89
+ { type: :udp, host: 'host1', port: 5228 },
90
+ { type: :udp, host: 'host2', port: 5228 }
91
+ ])
92
+
93
+ # Send messages to multiple loggers.
94
+ # Use this if you need to send different formats to different outputs.
95
+ multi_logger = LogStashLogger.new(
96
+ type: :multi_logger,
97
+ outputs: [
98
+ { type: :file, path: 'log/development.log', formatter: ::Logger::Formatter },
99
+ { type: :tcp, host: 'localhost', port: 5228, formatter: :json }
100
+ ])
63
101
 
64
102
  # The following messages are written to UDP port 5228:
65
103
 
@@ -223,6 +261,10 @@ config.autoflush_log = true
223
261
 
224
262
  # Optional, use a URI to configure. Useful on Heroku
225
263
  config.logstash.uri = ENV['LOGSTASH_URI']
264
+
265
+ # Optional. Defaults to :json_lines. If there are multiple outputs,
266
+ # they will all share the same formatter.
267
+ config.logstash.formatter = :json_lines
226
268
  ```
227
269
 
228
270
  #### UDP
@@ -331,10 +373,14 @@ config.logstash.type = :io
331
373
  config.logstash.io = io
332
374
  ```
333
375
 
334
- #### Multiple Outputs
376
+ #### Multi Delegator
335
377
 
336
378
  ```ruby
337
- config.logstash = [
379
+ # Required
380
+ config.logstash.type = :multi_delegator
381
+
382
+ # Required
383
+ config.logstash.outputs = [
338
384
  {
339
385
  type: :file,
340
386
  path: 'log/production.log'
@@ -347,6 +393,27 @@ config.logstash = [
347
393
  ]
348
394
  ```
349
395
 
396
+ #### Multi Logger
397
+
398
+ ```ruby
399
+ # Required
400
+ config.logstash.type = :multi_logger
401
+
402
+ # Required. Each logger may have its own formatter.
403
+ config.logstash.outputs = [
404
+ {
405
+ type: :file,
406
+ path: 'log/production.log',
407
+ formatter: ::Logger::Formatter
408
+ },
409
+ {
410
+ type: :udp,
411
+ port: 5228,
412
+ host: 'localhost'
413
+ }
414
+ ]
415
+ ```
416
+
350
417
  ### Logging HTTP request data
351
418
 
352
419
  In web applications, you can log data from HTTP requests (such as headers) using the
@@ -421,6 +488,11 @@ This is most likely not a problem with LogStashLogger, but rather a different ge
421
488
  This is especially likely if you're using a threaded server such as Puma, since gems often change the log level of
422
489
  `Rails.logger` in a non thread-safe way. See [#17](https://github.com/dwbutler/logstash-logger/issues/17) for more information.
423
490
 
491
+ ### Sometimes two lines of JSON log messages get sent as one message
492
+ If you're using UDP output and writing to a logstash listener, you are most likely encountering a bug in the UDP implementation
493
+ of the logstash listener. There is no known fix at this time. See [#43](https://github.com/dwbutler/logstash-logger/issues/43)
494
+ for more information.
495
+
424
496
  ## Breaking changes
425
497
 
426
498
  ### Version 0.5+
@@ -457,6 +529,8 @@ logger = LogStashLogger.new('localhost', 5228, :tcp)
457
529
  * [Vadim Kazakov](https://github.com/yads)
458
530
  * [Anil Rhemtulla](https://github.com/AnilRh)
459
531
  * [Nikita Vorobei](https://github.com/Nikita-V)
532
+ * [fireboy1919](https://github.com/fireboy1919)
533
+ * [Mike Gunderloy](https://github.com/ffmike)
460
534
 
461
535
  ## Contributing
462
536
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "codecov", :require => false, :group => :test
5
6
  gem "rails", "~> 3.2.18"
6
7
 
7
8
  gemspec :path => "../"
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "codecov", :require => false, :group => :test
5
6
  gem "rails", "~> 4.0.0"
6
7
 
7
8
  gemspec :path => "../"
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "codecov", :require => false, :group => :test
5
6
  gem "rails", "~> 4.1.1"
6
7
 
7
8
  gemspec :path => "../"
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "codecov", :require => false, :group => :test
5
6
  gem "rails", "~> 4.2.0"
6
7
 
7
8
  gemspec :path => "../"
@@ -21,17 +21,7 @@ module LogStashLogger
21
21
 
22
22
  def self.new(opts)
23
23
  opts = opts.dup
24
-
25
- if opts.is_a?(Array)
26
- # Multiple device configs supplied... create a MultiDelegator
27
- devices = opts.map{|opt| build_device(opt)}
28
- Device::MultiDelegator.new(*devices)
29
- elsif Hash
30
- # Create a single device
31
- build_device(opts)
32
- else
33
- raise ArgumentError, "Invalid device options: must be a Hash or an Array of Hashes"
34
- end
24
+ build_device(opts)
35
25
  end
36
26
 
37
27
  def self.build_device(opts)
@@ -63,8 +53,9 @@ module LogStashLogger
63
53
  when :io then IO
64
54
  when :stdout then Stdout
65
55
  when :stderr then Stderr
56
+ when :multi_delegator then MultiDelegator
66
57
  when :balancer then Balancer
67
- else fail ArgumentError, 'Invalid type'
58
+ else fail ArgumentError, 'Invalid device type'
68
59
  end
69
60
  end
70
61
  end
@@ -13,7 +13,7 @@ module LogStashLogger
13
13
  private
14
14
 
15
15
  def create_devices(opts)
16
- opts.map { |o| Device.new(o) }
16
+ opts.map { |device_opts| Device.new(device_opts) }
17
17
  end
18
18
 
19
19
  def self.delegate_to_all(*methods)
@@ -8,14 +8,20 @@ module LogStashLogger
8
8
  class MultiDelegator < Base
9
9
  attr_reader :devices
10
10
 
11
- def initialize(*devices)
11
+ def initialize(opts)
12
12
  @io = self
13
- @devices = devices
13
+ @devices = create_devices(opts[:outputs])
14
14
  self.class.delegate(:write, :close, :flush)
15
15
  end
16
16
 
17
17
  private
18
18
 
19
+ def create_devices(opts)
20
+ opts.map do |device_opts|
21
+ Device.new(device_opts)
22
+ end
23
+ end
24
+
19
25
  def self.delegate(*methods)
20
26
  methods.each do |m|
21
27
  define_method(m) do |*args|
@@ -17,13 +17,16 @@ module LogStashLogger
17
17
  def self.build_formatter(formatter_type)
18
18
  formatter_type ||= DEFAULT_FORMATTER
19
19
 
20
- if custom_formatter_instance?(formatter_type)
20
+ formatter = if custom_formatter_instance?(formatter_type)
21
21
  formatter_type
22
22
  elsif custom_formatter_class?(formatter_type)
23
23
  formatter_type.new
24
24
  else
25
25
  formatter_klass(formatter_type).new
26
26
  end
27
+
28
+ formatter.send(:extend, ::LogStashLogger::TaggedLogging::Formatter)
29
+ formatter
27
30
  end
28
31
 
29
32
  def self.formatter_klass(formatter_type)
@@ -2,17 +2,11 @@ require 'logger'
2
2
  require 'logstash-logger/tagged_logging'
3
3
 
4
4
  module LogStashLogger
5
+ autoload :MultiLogger, 'logstash-logger/multi_logger'
6
+
5
7
  def self.new(*args)
6
8
  opts = extract_opts(*args)
7
- formatter = Formatter.new(opts.delete(:formatter))
8
- device = Device.new(opts)
9
-
10
- ::Logger.new(device).tap do |logger|
11
- logger.instance_variable_set(:@device, device)
12
- logger.extend(self)
13
- logger.extend(TaggedLogging)
14
- logger.formatter = formatter
15
- end
9
+ build_logger(opts)
16
10
  end
17
11
 
18
12
  def self.extended(base)
@@ -22,7 +16,7 @@ module LogStashLogger
22
16
  end
23
17
 
24
18
  def flush
25
- !!@device.flush
19
+ !!(@device.flush if @device.respond_to?(:flush))
26
20
  end
27
21
  end
28
22
  end
@@ -34,13 +28,14 @@ module LogStashLogger
34
28
 
35
29
  if args.length > 1
36
30
  if args.all?{|arg| arg.is_a?(Hash)}
37
- # Array of Hashes
38
- args
31
+ # Deprecated array of hashes
32
+ warn "[LogStashLogger] Passing an array of hashes to the constructor is deprecated. Please replace with an options hash: { type: :multi_delegator, outputs: [...] }"
33
+ { type: :multi_delegator, outputs: args }
39
34
  else
40
- # Deprecated host/port/type syntax
41
- puts "[LogStashLogger] (host, port, type) constructor is deprecated. Please use an options hash instead."
35
+ # Deprecated host/port/type constructor
36
+ warn "[LogStashLogger] The (host, port, type) constructor is deprecated. Please use an options hash instead."
42
37
  host, port, type = *args
43
- {host: host, port: port, type: type}
38
+ { host: host, port: port, type: type }
44
39
  end
45
40
  elsif Hash === args[0]
46
41
  args[0]
@@ -48,4 +43,22 @@ module LogStashLogger
48
43
  fail ArgumentError, "Invalid LogStashLogger options"
49
44
  end
50
45
  end
46
+
47
+ def self.build_logger(opts)
48
+ case opts[:type]
49
+ when :multi_logger
50
+ loggers = opts[:outputs].map {|logger_opts| build_logger(logger_opts) }
51
+ MultiLogger.new(loggers)
52
+ else
53
+ formatter = Formatter.new(opts.delete(:formatter))
54
+ device = Device.new(opts)
55
+
56
+ ::Logger.new(device).tap do |logger|
57
+ logger.instance_variable_set(:@device, device)
58
+ logger.extend(self)
59
+ logger.extend(TaggedLogging)
60
+ logger.formatter = formatter
61
+ end
62
+ end
63
+ end
51
64
  end
@@ -0,0 +1,135 @@
1
+ # Adapted from https://github.com/ffmike/multilogger
2
+ module LogStashLogger
3
+ class MultiLogger < ::Logger
4
+ def level=(value)
5
+ super
6
+ @loggers.each do |logger|
7
+ logger.level = value
8
+ end
9
+ end
10
+
11
+ def progname=(value)
12
+ super
13
+ @loggers.each do |logger|
14
+ logger.progname = value
15
+ end
16
+ end
17
+
18
+ def datetime_format=(datetime_format)
19
+ super
20
+ @loggers.each do |logger|
21
+ logger.datetime_format = datetime_format
22
+ end
23
+ end
24
+
25
+ def formatter=(formatter)
26
+ @loggers.each do |logger|
27
+ logger.formatter = formatter
28
+ end
29
+ end
30
+
31
+ # Array of Loggers to be logged to. These can be anything that acts reasonably like a Logger.
32
+ attr_accessor :loggers
33
+
34
+ # Any method not defined on standard Logger class, just send it on to anyone who will listen
35
+ def method_missing(name, *args, &block)
36
+ @loggers.each do |logger|
37
+ if logger.respond_to?(name)
38
+ logger.send(name, args, &block)
39
+ end
40
+ end
41
+ end
42
+
43
+ #
44
+ # === Synopsis
45
+ #
46
+ # MultiLogger.new([logger1, logger2])
47
+ #
48
+ # === Args
49
+ #
50
+ # +loggers+::
51
+ # An array of loggers. Each one gets every message that is sent to the MultiLogger instance
52
+ #
53
+ # === Description
54
+ #
55
+ # Create an instance.
56
+ #
57
+ def initialize(loggers)
58
+ @loggers = []
59
+ super(nil)
60
+ @loggers = Array(loggers)
61
+ end
62
+
63
+ # Methods that write to logs just write to each contained logger in turn
64
+ def add(severity, message = nil, progname = nil, &block)
65
+ @loggers.each do |logger|
66
+ logger.add(severity, message, progname, &block)
67
+ end
68
+ end
69
+
70
+ def <<(msg)
71
+ @loggers.each do |logger|
72
+ logger << msg
73
+ end
74
+ end
75
+
76
+ def debug(progname = nil, &block)
77
+ @loggers.each do |logger|
78
+ logger.debug(progname, &block)
79
+ end
80
+ end
81
+
82
+ def info(progname = nil, &block)
83
+ @loggers.each do |logger|
84
+ logger.info(progname, &block)
85
+ end
86
+ end
87
+
88
+ def warn(progname = nil, &block)
89
+ @loggers.each do |logger|
90
+ logger.warn(progname, &block)
91
+ end
92
+ end
93
+
94
+ def error(progname = nil, &block)
95
+ @loggers.each do |logger|
96
+ logger.error(progname, &block)
97
+ end
98
+ end
99
+
100
+ def fatal(progname = nil, &block)
101
+ @loggers.each do |logger|
102
+ logger.fatal(progname, &block)
103
+ end
104
+ end
105
+
106
+ def unknown(progname = nil, &block)
107
+ @loggers.each do |logger|
108
+ logger.unknown(progname, &block)
109
+ end
110
+ end
111
+
112
+ def close
113
+ @loggers.each do |logger|
114
+ logger.close if logger.respond_to?(:close)
115
+ end
116
+ end
117
+
118
+ module TaggedLogging
119
+ def tagged(*tags)
120
+ @loggers.each do |logger|
121
+ logger.formatter.tagged(*tags) { yield self }
122
+ end
123
+ end
124
+
125
+ def flush
126
+ @loggers.each do |logger|
127
+ logger.formatter.clear_tags!
128
+ end
129
+ super if defined?(super)
130
+ end
131
+ end
132
+
133
+ include TaggedLogging
134
+ end
135
+ end
@@ -1,3 +1,3 @@
1
1
  module LogStashLogger
2
- VERSION = "0.12.0"
2
+ VERSION = "0.13.0"
3
3
  end
@@ -24,6 +24,7 @@ Gem::Specification.new do |gem|
24
24
  gem.add_development_dependency 'rails'
25
25
  gem.add_development_dependency 'redis'
26
26
  gem.add_development_dependency 'poseidon'
27
+
27
28
  gem.add_development_dependency 'rspec', '>= 3'
28
29
  gem.add_development_dependency 'rake'
29
30
  gem.add_development_dependency 'pry'
@@ -4,10 +4,7 @@ describe LogStashLogger::Device::Balancer do
4
4
  include_context 'device'
5
5
 
6
6
  # Create a Balancer writing to both STDOUT and a StringIO
7
- let(:subject) { balancer_device }
8
-
9
- let(:stdout) { $stdout }
10
- let(:io) { StringIO.new }
7
+ subject { balancer_device }
11
8
 
12
9
  describe '#write' do
13
10
  before do
@@ -16,7 +13,7 @@ describe LogStashLogger::Device::Balancer do
16
13
 
17
14
  it "writes to one device" do
18
15
  expect(io).to receive(:write).once
19
- expect(stdout).to_not receive(:write)
16
+ expect($stdout).to_not receive(:write)
20
17
  subject.write("log message")
21
18
  end
22
19
  end
@@ -9,7 +9,7 @@ describe LogStashLogger::Device::File do
9
9
 
10
10
  context "when path is not specified" do
11
11
  it "raises an exception" do
12
- expect { described_class.new }.to raise_error
12
+ expect { described_class.new }.to raise_error(ArgumentError)
13
13
  end
14
14
  end
15
15
  end
@@ -3,8 +3,7 @@ require 'logstash-logger'
3
3
  describe LogStashLogger::Device::IO do
4
4
  include_context 'device'
5
5
 
6
- let(:subject) { io_device }
7
- let(:io) { StringIO.new }
6
+ subject { io_device }
8
7
 
9
8
  it "writes to the IO object" do
10
9
  expect(subject.to_io).to eq(io)
@@ -4,16 +4,10 @@ describe LogStashLogger::Device::MultiDelegator do
4
4
  include_context 'device'
5
5
 
6
6
  # Create a MultiDelegator writing to both STDOUT and a StringIO
7
- let(:subject) { multi_delegator_device }
7
+ subject { multi_delegator_device }
8
8
 
9
- let(:stdout) { $stdout }
10
- let(:io) { StringIO.new }
11
-
12
- it "writes to $stdout" do
13
- # MultiDelegator writes to stdout
14
- expect(stdout).to receive(:write).once
15
-
16
- # MultiDelegator writes to IO
9
+ it "writes to all outputs" do
10
+ expect($stdout).to receive(:write).once
17
11
  expect(io).to receive(:write).once
18
12
 
19
13
  subject.write("test")
@@ -9,7 +9,7 @@ describe LogStashLogger::Device::Socket do
9
9
 
10
10
  context "when port is not specified" do
11
11
  it "raises an exception" do
12
- expect { described_class.new }.to raise_error
12
+ expect { described_class.new }.to raise_error(ArgumentError)
13
13
  end
14
14
  end
15
15
  end
@@ -16,7 +16,7 @@ describe LogStashLogger::Device::Unix do
16
16
 
17
17
  context "when path is not specified" do
18
18
  it "raises an exception" do
19
- expect { described_class.new }.to raise_error
19
+ expect { described_class.new }.to raise_error(ArgumentError)
20
20
  end
21
21
  end
22
22
  end
data/spec/device_spec.rb CHANGED
@@ -40,7 +40,7 @@ describe LogStashLogger::Device do
40
40
 
41
41
  context "when uri is invalid" do
42
42
  let(:uri_config) { invalid_uri_config }
43
- specify { expect{ parse_uri_config }.to raise_error(URI::InvalidURIError) }
43
+ specify { expect { parse_uri_config }.to raise_error(URI::InvalidURIError) }
44
44
  end
45
45
  end
46
46
 
@@ -25,27 +25,54 @@ describe LogStashLogger::Formatter do
25
25
  end
26
26
 
27
27
  context "custom formatter" do
28
- it "returns a new instance of a custom formatter class" do
29
- expect(described_class.new(::Logger::Formatter)).to be_a ::Logger::Formatter
28
+ subject { described_class.new(formatter) }
29
+
30
+ context "formatter class" do
31
+ let(:formatter) { ::Logger::Formatter }
32
+
33
+ it "returns a new instance of the class" do
34
+ expect(subject).to be_a formatter
35
+ end
30
36
  end
31
37
 
32
- it "returns the same formatter instance if a custom formatter instance is passed in" do
33
- formatter = ::Logger::Formatter.new
34
- expect(described_class.new(formatter)).to eql(formatter)
38
+ context "formatter instance" do
39
+ let(:formatter) { ::Logger::Formatter.new }
40
+
41
+ it "returns the same formatter instance" do
42
+ expect(subject).to eql(formatter)
43
+ end
44
+
45
+ it "supports tagged logging" do
46
+ expect(subject).to be_a ::LogStashLogger::TaggedLogging::Formatter
47
+ end
35
48
  end
36
49
 
37
- it "returns a formatter proc if it is passed in" do
38
- formatter = proc do |severity, time, progname, msg|
39
- msg
50
+ context "formatter proc" do
51
+ let(:formatter) do
52
+ proc { |severity, time, progname, msg| msg }
53
+ end
54
+
55
+ it "returns the same formatter proc" do
56
+ expect(subject).to eql(formatter)
57
+ end
58
+
59
+ it "supports tagged logging" do
60
+ expect(subject).to be_a ::LogStashLogger::TaggedLogging::Formatter
40
61
  end
41
- expect(described_class.new(formatter)).to eql(formatter)
42
62
  end
43
63
 
44
- it "returns a formatter lambda if it is passed in" do
45
- formatter = lambda do |severity, time, progname, msg|
46
- msg
64
+ context "formatter lambda" do
65
+ let(:formatter) do
66
+ ->(severity, time, progname, msg) { msg }
67
+ end
68
+
69
+ it "returns the same formatter lambda" do
70
+ expect(subject).to eql(formatter)
71
+ end
72
+
73
+ it "supports tagged logging" do
74
+ expect(subject).to be_a ::LogStashLogger::TaggedLogging::Formatter
47
75
  end
48
- expect(described_class.new(formatter)).to eql(formatter)
49
76
  end
50
77
  end
51
78
  end
@@ -0,0 +1,52 @@
1
+ require 'logstash-logger'
2
+
3
+ describe LogStashLogger::MultiLogger do
4
+ include_context 'device'
5
+
6
+ # Create a MultiLogger writing to both STDOUT and a StringIO
7
+ subject { multi_logger }
8
+
9
+ it { is_expected.to be_a LogStashLogger::MultiLogger }
10
+
11
+ it "has multiple loggers" do
12
+ expect(subject.loggers.count).to eq(2)
13
+ end
14
+
15
+ it "has one logger per output" do
16
+ expect(subject.loggers[0].device).to be_a LogStashLogger::Device::Stdout
17
+ expect(subject.loggers[1].device).to be_a LogStashLogger::Device::IO
18
+ end
19
+
20
+ it "allows a different formatter for each logger" do
21
+ expect(subject.loggers[0].formatter).to be_a ::Logger::Formatter
22
+ expect(subject.loggers[1].formatter).to be_a LogStashLogger::Formatter::JsonLines
23
+ end
24
+
25
+ it "logs to all loggers" do
26
+ subject.loggers.each do |logger|
27
+ expect(logger).to receive(:info).with("test")
28
+ end
29
+
30
+ multi_logger.info("test")
31
+ end
32
+
33
+ context "tagged logging" do
34
+ it "forwards tags to each logger's formatter" do
35
+ subject.loggers.each do |logger|
36
+ expect(logger.formatter).to receive(:tagged).with("foo")
37
+ end
38
+
39
+ subject.tagged("foo") do |logger|
40
+ logger.debug("bar")
41
+ end
42
+ end
43
+
44
+ it "clears tags on each logger's formatter when flushing" do
45
+ subject.loggers.each do |logger|
46
+ expect(logger.formatter).to receive(:clear_tags!)
47
+ end
48
+
49
+ subject.flush
50
+ end
51
+ end
52
+ end
data/spec/rails_spec.rb CHANGED
@@ -52,21 +52,23 @@ describe LogStashLogger do
52
52
  end
53
53
  end
54
54
 
55
- context "when configured with multiple devices" do
55
+ context "when configuring a multi delegator" do
56
56
  before(:each) do
57
- app.config.logstash = [
57
+ app.config.logstash.type = :multi_delegator
58
+ app.config.logstash.outputs = [
58
59
  {
59
60
  type: :udp,
60
61
  uri: udp_uri
61
62
  },
62
63
  {
63
- type: :file
64
+ type: :file,
65
+ path: '/tmp/foo.log'
64
66
  }
65
67
  ]
66
68
  LogStashLogger.setup(app)
67
69
  end
68
70
 
69
- it "uses a multi-delegator" do
71
+ it "uses a multi delegator" do
70
72
  expect(subject.device).to be_a LogStashLogger::Device::MultiDelegator
71
73
  expect(subject.device.devices.map(&:class)).to eq([
72
74
  LogStashLogger::Device::UDP,
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,12 @@
1
1
  require 'pry'
2
2
 
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+ if ENV['CI']=='true'
6
+ require 'codecov'
7
+ SimpleCov.formatter = SimpleCov::Formatter::Codecov
8
+ end
9
+
3
10
  RSpec.configure do |config|
4
11
  config.order = "random"
5
12
 
@@ -49,12 +56,24 @@ RSpec.shared_context 'device' do
49
56
  let(:file) { Tempfile.new('test') }
50
57
  let(:file_device) { LogStashLogger::Device.new(type: :file, path: file.path)}
51
58
 
59
+ let(:io) { StringIO.new }
52
60
  let(:io_device) { LogStashLogger::Device.new(type: :io, io: io)}
53
61
 
54
62
  let(:redis_device) { LogStashLogger::Device.new(type: :redis, sync: true) }
55
63
  let(:kafka_device) { LogStashLogger::Device.new(type: :kafka, sync: true) }
56
- let(:multi_delegator_device) { LogStashLogger::Device.new([{type: :stdout}, {type: :io, io: io}]) }
57
- let(:balancer_device) { LogStashLogger::Device.new(type: :balancer, outputs: [{type: :stdout}, {type: :io, io: io}]) }
64
+
65
+ let(:outputs) { [{type: :stdout}, {type: :io, io: io}] }
66
+ let(:multi_delegator_device) { LogStashLogger::Device.new(type: :multi_delegator, outputs: outputs) }
67
+ let(:balancer_device) { LogStashLogger::Device.new(type: :balancer, outputs: outputs) }
68
+ let(:multi_logger) do
69
+ LogStashLogger.new(
70
+ type: :multi_logger,
71
+ outputs: [
72
+ { type: :stdout, formatter: ::Logger::Formatter },
73
+ { type: :io, io: io }
74
+ ]
75
+ )
76
+ end
58
77
 
59
78
  let(:udp_uri) { "udp://localhost:5228" }
60
79
  let(:tcp_uri) { "tcp://localhost:5229" }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Butler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-26 00:00:00.000000000 Z
11
+ date: 2015-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logstash-event
@@ -195,6 +195,7 @@ files:
195
195
  - lib/logstash-logger/formatter/json_lines.rb
196
196
  - lib/logstash-logger/formatter/logstash_event.rb
197
197
  - lib/logstash-logger/logger.rb
198
+ - lib/logstash-logger/multi_logger.rb
198
199
  - lib/logstash-logger/railtie.rb
199
200
  - lib/logstash-logger/tagged_logging.rb
200
201
  - lib/logstash-logger/version.rb
@@ -229,6 +230,7 @@ files:
229
230
  - spec/formatter/logstash_event_spec.rb
230
231
  - spec/formatter_spec.rb
231
232
  - spec/logger_spec.rb
233
+ - spec/multi_logger_spec.rb
232
234
  - spec/rails_spec.rb
233
235
  - spec/spec_helper.rb
234
236
  - spec/tagged_logging_spec.rb
@@ -279,6 +281,7 @@ test_files:
279
281
  - spec/formatter/logstash_event_spec.rb
280
282
  - spec/formatter_spec.rb
281
283
  - spec/logger_spec.rb
284
+ - spec/multi_logger_spec.rb
282
285
  - spec/rails_spec.rb
283
286
  - spec/spec_helper.rb
284
287
  - spec/tagged_logging_spec.rb