logstash-logger 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
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