logger_facade 0.2.0 → 0.3.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: 0180ea9d2371adfb7dc9745b2b5c550fada71357
4
- data.tar.gz: 9969640e45a3c0f9f1be24f52834c9c5f1a37b83
3
+ metadata.gz: a44006ba93de9dceaa753368eb3b452fe733e416
4
+ data.tar.gz: 654b4861ab41420d47ed09e825af6672c7a8683b
5
5
  SHA512:
6
- metadata.gz: 20a421d2ff5f652551b050047652034c374974eca980c68787815f4910acefb7d79032d033353ab64e1dbadeb80612e044c259fdb4da0a3c879fae971df7f8cc
7
- data.tar.gz: f15e7d566a1b471521260c54d4555a1a82ff6b48d9cbfce7c6aceee9112ed7e30ccae0e5921911a8963d32aa452365d6dbe6ef18ac31b0c6c04ba8afeaa5c51a
6
+ metadata.gz: 57d9fa7a05c8819f5976d0b38296d8efa1a5a4e55a2989339ecb03ec0af7166ebb72ab3d6638350d27dbb57db693167f8b20cb7509a9ea7195bde31256aaea1c
7
+ data.tar.gz: da2c70344b93d61fc57d6d84804a959f56aa6977032ab149e8edc2aa003daa13b815fa5078c79fc66a9344194fe0477c8c6269649158fec4a89b20d66d079942
data/.rspec CHANGED
@@ -1 +1 @@
1
- -f doc --color
1
+ -f doc --color --require spec_helper
data/Gemfile.lock CHANGED
@@ -1,55 +1,60 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- logger_facade (0.2.0)
4
+ logger_facade (0.3.0)
5
5
  airbrake (~> 4.0)
6
6
  hashie (~> 3.2)
7
7
  sucker_punch (~> 1.1)
8
+ yajl-ruby (~> 1.2)
8
9
 
9
10
  GEM
10
11
  remote: https://rubygems.org/
11
12
  specs:
12
- airbrake (4.0.0)
13
+ airbrake (4.1.0)
13
14
  builder
14
15
  multi_json
15
16
  builder (3.2.2)
16
- bump (0.5.0)
17
- celluloid (0.15.2)
18
- timers (~> 1.1.0)
19
- codeclimate-test-reporter (0.3.0)
17
+ bump (0.5.1)
18
+ celluloid (0.16.0)
19
+ timers (~> 4.0.0)
20
+ codeclimate-test-reporter (0.4.4)
20
21
  simplecov (>= 0.7.1, < 1.0.0)
21
22
  coderay (1.1.0)
22
23
  diff-lcs (1.2.5)
23
24
  docile (1.1.5)
24
- hashie (3.2.0)
25
+ hashie (3.3.2)
26
+ hitimes (1.2.2)
25
27
  method_source (0.8.2)
26
28
  multi_json (1.10.1)
27
- pry (0.10.0)
29
+ pry (0.10.1)
28
30
  coderay (~> 1.1.0)
29
31
  method_source (~> 0.8.1)
30
32
  slop (~> 3.4)
31
- rake (10.3.2)
32
- rspec (3.0.0)
33
- rspec-core (~> 3.0.0)
34
- rspec-expectations (~> 3.0.0)
35
- rspec-mocks (~> 3.0.0)
36
- rspec-core (3.0.3)
37
- rspec-support (~> 3.0.0)
38
- rspec-expectations (3.0.3)
33
+ rack (1.6.0)
34
+ rake (10.4.2)
35
+ rspec (3.1.0)
36
+ rspec-core (~> 3.1.0)
37
+ rspec-expectations (~> 3.1.0)
38
+ rspec-mocks (~> 3.1.0)
39
+ rspec-core (3.1.7)
40
+ rspec-support (~> 3.1.0)
41
+ rspec-expectations (3.1.2)
39
42
  diff-lcs (>= 1.2.0, < 2.0)
40
- rspec-support (~> 3.0.0)
41
- rspec-mocks (3.0.3)
42
- rspec-support (~> 3.0.0)
43
- rspec-support (3.0.3)
44
- simplecov (0.9.0)
43
+ rspec-support (~> 3.1.0)
44
+ rspec-mocks (3.1.3)
45
+ rspec-support (~> 3.1.0)
46
+ rspec-support (3.1.2)
47
+ simplecov (0.9.1)
45
48
  docile (~> 1.1.0)
46
- multi_json
49
+ multi_json (~> 1.0)
47
50
  simplecov-html (~> 0.8.0)
48
51
  simplecov-html (0.8.0)
49
52
  slop (3.6.0)
50
- sucker_punch (1.1)
51
- celluloid (~> 0.15.2)
52
- timers (1.1.0)
53
+ sucker_punch (1.3.2)
54
+ celluloid (~> 0.16.0)
55
+ timers (4.0.1)
56
+ hitimes
57
+ yajl-ruby (1.2.1)
53
58
 
54
59
  PLATFORMS
55
60
  ruby
@@ -60,6 +65,7 @@ DEPENDENCIES
60
65
  codeclimate-test-reporter (~> 0.3)
61
66
  logger_facade!
62
67
  pry (~> 0.10)
68
+ rack (~> 1.6)
63
69
  rake (~> 10.3)
64
70
  rspec (~> 3.0)
65
71
  simplecov (~> 0.9)
data/README.md CHANGED
@@ -39,13 +39,6 @@ Set up plugins
39
39
  ```ruby
40
40
  require 'logger_facade'
41
41
 
42
- # this is default config for Console plugin
43
- config = {
44
- level: :info,
45
- time_format: '%y-%M-%d %H:%M:%S',
46
- message_format: '%time | %level | %logger - %msg'
47
- }
48
-
49
42
  # configuration is optional in console plugin
50
43
  plugin = LoggerFacade::Plugins::Console.new(config);
51
44
 
@@ -65,10 +58,52 @@ log.error(Exception.new("some caught exception"))
65
58
 
66
59
  **NOTE**: Console plugin uses check [strftime](http://www.ruby-doc.org/core-2.1.2/Time.html#method-i-strftime) formats.
67
60
 
61
+ ### With Sinatra
62
+
63
+ ```ruby
64
+ require 'logger_facade'
65
+ require 'logger_facade/middleware/rack'
66
+
67
+ plugin = LoggerFacade::Plugins::Console.new({ level: Settings.logging.console.level })
68
+ LoggerFacade::Manager.use(plugin)
69
+
70
+ set :logging, nil
71
+ use LoggerFacade::Middleware::Rack
72
+ ```
73
+
74
+ ### Injecting log with LoggerFacade::Loggable module
75
+
76
+ ```ruby
77
+ class AwesomeLog
78
+ include LoggerFacade::Loggable
79
+
80
+ def action
81
+ log.debug("This will allow you to log into Logger with class name, here is 'AwesomeLog'")
82
+ end
83
+
84
+ end
85
+
86
+ # equivalent to
87
+
88
+ class AwesomeLog
89
+
90
+ def action
91
+ log.debug("This will allow you to log into Logger with class name, here is 'AwesomeLog'")
92
+ end
93
+
94
+ private
95
+
96
+ def log
97
+ @log ||= LoggerFacade::Manager.get_logger("AwesomeLog")
98
+ end
99
+
100
+ end
101
+ ```
102
+
68
103
  ## Available plugins
69
- * Console
70
- * Airbrake (Will be developed soon)
71
- * Elasticsearch (Will be developed soon)
104
+ * [Console](#loggerfacadepluginsconsole)
105
+ * [Airbrake](#loggerfacadepluginsairbrake)
106
+ * [Logstash](loggerfacadepluginslogstash)
72
107
 
73
108
  ** Do you need some other plugin?**
74
109
 
@@ -91,30 +126,96 @@ log = LoggerFacade::Manager.getLogger("Log Name")
91
126
 
92
127
  ```ruby
93
128
  log.isDebug() # return if in debug or trace level
129
+ metadata = { metadata: "some more context" }
130
+ # log.trace(message, metadata = {})
94
131
  log.trace("trace something")
132
+ # log.debug(message, metadata = {})
95
133
  log.debug("debug something")
134
+ # log.info(message, metadata = {})
96
135
  log.info("info something")
136
+ # log.warn(message, metadata = {})
97
137
  log.warn("warn something")
98
- log.error("error something")
138
+ # log.error(message, metadata = {})
139
+ log.error("error something", metadata)
99
140
  log.error(Exception.new('some caught exception'))
100
141
  ```
101
142
 
102
- ## LoggerFacade::Plugins
143
+ ## Plugins available
144
+
145
+ ### LoggerFacade::Plugins::Console
146
+
147
+ ```ruby
148
+ # this is default config for Console plugin
149
+ config = {
150
+ level: :info,
151
+ time_format: '%y-%M-%d %H:%M:%S',
152
+ message_format: '%time | %level | %logger - %msg'
153
+ }
154
+
155
+ # configuration is optional in console plugin
156
+ plugin = LoggerFacade::Plugins::Console.new(config);
157
+ LoggerFacade::Manager.use(plugin)
158
+ ```
103
159
 
104
- The plugins must follow this contract:
160
+ ### LoggerFacade::Plugins::Airbrake
105
161
 
162
+ ```ruby
163
+ env = 'production'
164
+ plugin = LoggerFacade::Plugins::Airbrake.new(env)
165
+ plugin.configure do |config|
166
+ config.api_key = "airbrake.api_key"
167
+ config.host = "airbrake.host"
168
+ config.port = "airbrake.port"
169
+ config.secure = config.port == 443
170
+ end
171
+ LoggerFacade::Manager.use(plugin)
106
172
  ```
107
- # plugin name
108
- #name
109
-
110
- #isDebug
111
- #trace
112
- #debug
113
- #info
114
- #warn
115
- #error
173
+
174
+ ### LoggerFacade::Plugins::Logstash
175
+
176
+ ```ruby
177
+ # this is default config for Console plugin
178
+ config = { level: :debug }
179
+
180
+ # configuration is optional in Logstash plugin
181
+ plugin = LoggerFacade::Plugins::Logstash.new(config);
182
+ LoggerFacade::Manager.use(plugin)
183
+
184
+
185
+ # by default logrotation is disabled and you should consider using a proper tool such logrotate.
186
+ # but you can use some Logger (from stdlib) options.
187
+ config = {
188
+ level: :debug
189
+ device: {
190
+ shift_age: 'daily', # (optional) daily/weekly/monthly
191
+ shift_size: 1024000 # (optional) bytes
192
+ }
193
+ }
194
+
116
195
  ```
117
196
 
197
+ ## LoggerFacade::Plugins
198
+
199
+ If you wanna use a custom plugin you just need to use an object that responds to the following contract:
200
+
201
+ ```ruby
202
+ # the plugin name
203
+ plugin.name
204
+ # the plugin configuration
205
+ plugin.config
206
+ # the plugin level
207
+ plugin.level
208
+
209
+ def isDebug() # return if in debug or trace level
210
+ def trace(logger, message, metadata: {})
211
+ def debug(logger, message, metadata: {})
212
+ def info(logger, message, metadata: {})
213
+ def warn(logger, message, metadata: {})
214
+ def error(logger, message, metadata: {})
215
+ def error(logger, exception, metadata: {})
216
+ ```
217
+
218
+
118
219
  ## Contributing
119
220
 
120
221
  1. Fork it
@@ -126,18 +227,19 @@ The plugins must follow this contract:
126
227
  ## Bump versioning
127
228
 
128
229
  We use [bump gem](https://github.com/gregorym/bump) to control gem versioning.
230
+ Use --tag to generate commit and tag for each version.
129
231
 
130
232
  Bump Patch version
131
233
 
132
- $ bump patch
234
+ $ bump patch --tag
133
235
 
134
236
  Bump Minor version
135
237
 
136
- $ bump minor
238
+ $ bump minor --tag
137
239
 
138
240
  Bump Major version
139
241
 
140
- $ bump major
242
+ $ bump major --tag
141
243
 
142
244
  ## Running Specs
143
245
 
@@ -13,31 +13,33 @@ module LoggerFacade
13
13
  plugins.select { |p| p.is_debug }.size > 0
14
14
  end
15
15
 
16
- def trace(message)
17
- log(:trace, message)
16
+ def trace(message, metadata = {})
17
+ log(:trace, message, metadata)
18
18
  end
19
19
 
20
- def debug(message)
21
- log(:debug, message)
20
+ def debug(message, metadata = {})
21
+ log(:debug, message, metadata)
22
22
  end
23
23
 
24
- def info(message)
25
- log(:info, message)
24
+ def info(message, metadata = {})
25
+ log(:info, message, metadata)
26
26
  end
27
27
 
28
- def warn(message)
29
- log(:warn, message)
28
+ def warn(message, metadata = {})
29
+ log(:warn, message, metadata)
30
30
  end
31
31
 
32
- def error(message)
33
- log(:error, message)
32
+ def error(message, metadata = {})
33
+ log(:error, message, metadata)
34
34
  end
35
35
 
36
36
  private
37
37
 
38
- def log(level, message)
38
+ def log(level, message, metadata)
39
+ metadata[:timestamp] = Time.now.utc
40
+ metadata[:pid] = Process.pid
39
41
  plugins.each do |plugin|
40
- plugin.send(level, name, message)
42
+ plugin.send(level, name, message, metadata: metadata)
41
43
  end
42
44
  end
43
45
 
@@ -0,0 +1,43 @@
1
+ module LoggerFacade::Middleware
2
+ class Rack < Rack::CommonLogger
3
+ def initialize(app, logger=nil)
4
+ log = LoggerFacade::Manager.get_logger("RackLogger")
5
+ @format = '%s "%s %s" %d %s %0.4f'
6
+ super(app, log)
7
+ end
8
+
9
+ private
10
+
11
+ def log(env, status, header, began_at)
12
+ metadata = get_metadata(env, status, header, began_at)
13
+
14
+ msg = @format % [
15
+ metadata["clientip"] || "-",
16
+ metadata["verb"],
17
+ metadata["request"],
18
+ metadata["response"].to_s[0..3],
19
+ metadata["bytes"],
20
+ metadata["request_time"] ]
21
+
22
+ @logger.info(msg, metadata)
23
+ end
24
+
25
+ def get_metadata(env, status, header, began_at)
26
+ length = extract_content_length(header)
27
+ qs = env["QUERY_STRING"].empty? ? "" : "?#{env["QUERY_STRING"]}"
28
+ {
29
+ 'clientip' => env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"],
30
+ 'verb' => env["REQUEST_METHOD"],
31
+ 'request' => "#{env["PATH_INFO"]}#{qs}",
32
+ 'http_version' => env["HTTP_VERSION"],
33
+ 'response' => status.to_s,
34
+ 'bytes' => (length || "").to_i,
35
+ 'referrer' => env["HTTP_REFERER"],
36
+ 'agent' => env['HTTP_USER_AGENT'],
37
+ 'request_time' => Time.now - began_at,
38
+ 'request_full_url' => env['REQUEST_URI']
39
+ }
40
+ end
41
+
42
+ end
43
+ end
@@ -1,2 +1,4 @@
1
+ require_relative 'plugins/base'
1
2
  require_relative 'plugins/console'
2
3
  require_relative 'plugins/airbrake'
4
+ require_relative 'plugins/logstash'
@@ -0,0 +1,59 @@
1
+ module LoggerFacade::Plugins
2
+
3
+ class Base
4
+
5
+ attr_reader :config, :level, :name
6
+
7
+ def initialize(name, config = nil)
8
+ @config = Hashie::Mash.new(config)
9
+ @level = (@config.level || :debug).to_sym
10
+ @name = name
11
+ end
12
+
13
+ def is_debug
14
+ is_level_active(:debug)
15
+ end
16
+
17
+ def trace(logger, message, metadata: {})
18
+ log(:trace, message, logger, metadata)
19
+ end
20
+
21
+ def debug(logger, message, metadata: {})
22
+ log(:debug, message, logger, metadata)
23
+ end
24
+
25
+ def info(logger, message, metadata: {})
26
+ log(:info, message, logger, metadata)
27
+ end
28
+
29
+ def warn(logger, message, metadata: {})
30
+ log(:warn, message, logger, metadata)
31
+ end
32
+
33
+ def error(logger, message, metadata: {})
34
+ log(:error, message, logger, metadata)
35
+ end
36
+
37
+ protected
38
+
39
+ def log(severity, message, logger, metadata)
40
+ # do nothing by default
41
+ end
42
+
43
+ def levels
44
+ {
45
+ trace: 0,
46
+ debug: 1,
47
+ info: 2,
48
+ warn: 3,
49
+ error: 4
50
+ }
51
+ end
52
+
53
+ def is_level_active(log_level)
54
+ levels[log_level] >= levels[level]
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -2,9 +2,7 @@ require 'hashie'
2
2
 
3
3
  module LoggerFacade::Plugins
4
4
 
5
- class Console
6
-
7
- attr_reader :config, :level, :name
5
+ class Console < Base
8
6
 
9
7
  def initialize(config = {})
10
8
  defaults = {
@@ -13,73 +11,39 @@ module LoggerFacade::Plugins
13
11
  message_format: '%time | %level | %logger - %msg'
14
12
  }
15
13
  config = defaults.merge(config)
16
-
17
- @config = Hashie::Mash.new(config)
18
- @level = @config.level.to_sym
19
- @name = "LoggerFacade::Plugins::Console"
20
- end
21
-
22
- def is_debug
23
- is_level_active(:debug)
24
- end
25
-
26
- def trace(logger, message)
27
- log(:trace, message, logger)
14
+ super("LoggerFacade::Plugins::Console", config)
28
15
  end
29
16
 
30
- def debug(logger, message)
31
- log(:debug, message, logger)
32
- end
17
+ protected
33
18
 
34
- def info(logger, message)
35
- log(:info, message, logger)
36
- end
37
-
38
- def warn(logger, message)
39
- log(:warn, message, logger)
40
- end
19
+ def log(log_level, message, logger, metadata)
20
+ return unless is_level_active(log_level)
41
21
 
42
- def error(logger, message)
43
- log(:error, message, logger)
22
+ write message(log_level, message, logger, metadata)
44
23
  end
45
24
 
46
25
  private
47
26
 
48
- def levels
49
- {
50
- trace: 0,
51
- debug: 1,
52
- info: 2,
53
- warn: 3,
54
- error: 4
55
- }
56
- end
57
-
58
- def is_level_active(log_level)
59
- levels[log_level] >= levels[level]
60
- end
27
+ def message(level, msg, logger, metadata)
28
+ msg = log_exception(msg) if msg.is_a? Exception
61
29
 
62
- def message(level, msg, logger)
63
- outputMsg = msg
30
+ timestamp = metadata[:timestamp] || Time.now.utc
31
+ pid = metadata[:pid] || Process.pid
64
32
 
65
- if msg.is_a? Exception
66
- outputMsg = "#{msg.message}\n#{(msg.backtrace || []).join("\n")}"
67
- end
68
-
69
- formatedMessage = config.message_format
33
+ config.message_format
70
34
  .gsub('%logger', logger.upcase)
71
- .gsub('%time', Time.now.utc.strftime(config.time_format))
35
+ .gsub('%time', timestamp.strftime(config.time_format))
72
36
  .gsub('%level', level.to_s.upcase)
73
- .gsub('%pid', Process.pid.to_s)
74
- .gsub('%msg', outputMsg)
75
-
76
- return formatedMessage;
37
+ .gsub('%pid', pid.to_s)
38
+ .gsub('%msg', msg)
77
39
  end
78
40
 
79
- def log(log_level, message, logger)
80
- return unless is_level_active(log_level)
41
+ def log_exception(msg)
42
+ "#{msg.message}\n#{(msg.backtrace || []).join("\n")}"
43
+ end
81
44
 
82
- Kernel.puts message(log_level, message, logger)
45
+ def write(message)
46
+ Kernel.puts message
83
47
  end
84
48
 
85
49
  end
@@ -0,0 +1,64 @@
1
+ require 'logger'
2
+ require 'yajl'
3
+
4
+ module LoggerFacade::Plugins
5
+ class Logstash < Base
6
+
7
+ def initialize(configuration = {})
8
+ super("LoggerFacade::Plugins::Logstash", configuration)
9
+
10
+ fail "Invalid configuration filename: #{config.filename}" unless config.filename
11
+
12
+ @logdevice = if config["device"]
13
+ shift_age = config.device["shift_age"]
14
+ shift_size = config.device["shift_size"]
15
+ LogDeviceWithRotation.new(config.filename, shift_age, shift_size)
16
+ else
17
+ LogDeviceWithoutRotation.new(config.filename)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :logdevice
24
+
25
+ def log(severity, message, logger, metadata)
26
+ return unless is_level_active(severity)
27
+ return unless logdevice
28
+
29
+ ts = metadata.delete :timestamp
30
+ ts ||= Time.now.utc
31
+ metadata[:severity] = severity
32
+ metadata[:logger] = logger
33
+ metadata[:message] = message
34
+ metadata[:backtrace] = message.backtrace if message.is_a? Exception
35
+
36
+ json = {
37
+ '@timestamp' => ts.iso8601,
38
+ '@fields' => metadata
39
+ }
40
+
41
+ @logdevice.write("#{Yajl::Encoder.encode(json)}\n")
42
+ end
43
+
44
+ class LogDeviceWithRotation < ::Logger::LogDevice
45
+
46
+ private
47
+
48
+ # overrides base.add_log_header to disable log header
49
+ def add_log_header(file)
50
+ end
51
+ end
52
+
53
+ class LogDeviceWithoutRotation < LogDeviceWithRotation
54
+
55
+ # overrides base.write to disable log shifting
56
+ def write(message)
57
+ @mutex.synchronize do
58
+ @dev.write(message)
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -1,3 +1,3 @@
1
1
  module LoggerFacade
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -29,9 +29,12 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency 'simplecov', '~> 0.9'
30
30
  spec.add_development_dependency 'codeclimate-test-reporter', '~> 0.3'
31
31
  spec.add_development_dependency 'bump', '~> 0.5'
32
+ spec.add_development_dependency 'rack', '~> 1.6'
32
33
 
33
34
  spec.add_dependency 'hashie', '~> 3.2'
34
35
  spec.add_dependency 'airbrake', '~> 4.0'
35
36
  # sucker punch is used to use airbrake async
36
37
  spec.add_dependency 'sucker_punch', '~> 1.1'
38
+ # used by logstash plugin, for better performance
39
+ spec.add_dependency 'yajl-ruby', '~> 1.2'
37
40
  end
data/spec/spec_helper.rb CHANGED
@@ -23,3 +23,31 @@ RSpec.configure do |c|
23
23
  }
24
24
  end
25
25
  end
26
+
27
+ module TimeTestHelper
28
+ def with_mock_time(t = 0)
29
+ mc = class <<Time; self; end
30
+ mc.send :alias_method, :old_now, :now
31
+ mc.send :define_method, :now do
32
+ at(t)
33
+ end
34
+ yield
35
+ ensure
36
+ mc.send :alias_method, :now, :old_now
37
+ end
38
+ end
39
+
40
+ module LevelTestHelper
41
+ def next_level(lev)
42
+ levels = {
43
+ trace: 0,
44
+ debug: 1,
45
+ info: 2,
46
+ warn: 3,
47
+ error: 4
48
+ }
49
+
50
+ val = levels[lev.to_sym]
51
+ levels.select { |k,v| v > val }.keys.first
52
+ end
53
+ end
@@ -41,13 +41,57 @@ describe LoggerFacade::Log do
41
41
  %w(trace debug info warn error).each do |level|
42
42
 
43
43
  describe("##{level}") do
44
+ let(:time) { Time.new(1983, 01, 25, 13, 10, 01, '+00:00') }
45
+ let(:pid) { 0 }
44
46
 
45
47
  it("calls the plugin in #{level} level") do
46
48
  message = "call with message"
47
- expect(plugin).to receive(level.to_sym).with(subject.name, message)
49
+ expect(plugin).to receive(level.to_sym).with(subject.name, message, anything)
48
50
  subject.send(level.to_sym, message)
49
51
  end
50
52
 
53
+ it("calls the plugin with metadata dictionary when not present") do
54
+ message = "call with message"
55
+ metadata = {}
56
+ expect(plugin).to receive(level.to_sym)
57
+ .with(subject.name, message,
58
+ hash_including(metadata: hash_including(:timestamp, :pid)))
59
+ subject.send(level.to_sym, message)
60
+ end
61
+
62
+ it("appends timestamp to metadata") do
63
+ allow(Time).to receive(:now) { time }
64
+ message = "call with message"
65
+ metadata = {}
66
+ expect(plugin).to receive(level.to_sym)
67
+ .with(subject.name, message,
68
+ hash_including(metadata: hash_including(timestamp: time))
69
+ )
70
+ subject.send(level.to_sym, message, metadata)
71
+ end
72
+
73
+ it("appends timestamp to metadata") do
74
+ allow(Time).to receive(:now) { time }
75
+ message = "call with message"
76
+ metadata = {}
77
+ expect(plugin).to receive(level.to_sym)
78
+ .with(subject.name, message,
79
+ hash_including(metadata: hash_including(timestamp: time))
80
+ )
81
+ subject.send(level.to_sym, message, metadata)
82
+ end
83
+
84
+ it("appends pid to metadata") do
85
+ allow(Process).to receive(:pid) { pid }
86
+ message = "call with message"
87
+ metadata = {}
88
+ expect(plugin).to receive(level.to_sym)
89
+ .with(subject.name, message,
90
+ hash_including(metadata: hash_including(pid: pid))
91
+ )
92
+ subject.send(level.to_sym, message, metadata)
93
+ end
94
+
51
95
  end
52
96
 
53
97
  end
@@ -0,0 +1,107 @@
1
+ require 'rack/commonlogger'
2
+ require 'rack/lint'
3
+ require 'rack/mock'
4
+ require 'logger_facade'
5
+ require 'logger_facade/middleware/rack'
6
+
7
+ describe LoggerFacade::Middleware::Rack do
8
+ include TimeTestHelper
9
+ let(:regex) do
10
+ /(-) "(\w+) \/(\w+)\?(q=\w+)" (\d{3}) (\d+) ([\d\.]+)/
11
+ end
12
+ let(:logger) { double('logger') }
13
+ let(:obj) { "foobar" }
14
+ let(:length) { obj.size }
15
+ let(:app) do
16
+ Rack::Lint.new lambda { |env|
17
+ [200,
18
+ {"Content-Type" => "text/html", "Content-Length" => length.to_s},
19
+ [obj]]}
20
+ end
21
+
22
+ before do
23
+ expect(LoggerFacade::Manager).to receive(:get_logger){ logger }
24
+ end
25
+
26
+ it "logs to logger facade instance" do
27
+ expect(logger).to receive(:info)#.with('- "GET /" 200 6 0.0007')
28
+ res = Rack::MockRequest.new(described_class.new(app)).get("/")
29
+ end
30
+
31
+ it "log in the specified log format" do
32
+ expect(logger).to receive(:info) do |message|
33
+ md = regex.match(message)
34
+ expect(md).not_to be_nil
35
+ ip, method, path, qs, status, size, duration = *md.captures
36
+ expect(ip).to eq("-")
37
+ expect(method).to eq("GET")
38
+ expect(path).to eq("path")
39
+ expect(qs).to eq("q=true")
40
+ expect(status).to eq("200")
41
+ expect(size).to eq(length.to_s)
42
+ expect(duration.to_f).to eq(0)
43
+ end
44
+ with_mock_time do
45
+ Rack::MockRequest.new(described_class.new(app)).get("/path?q=true")
46
+ end
47
+ end
48
+
49
+ context 'with proper metadata' do
50
+
51
+ after do
52
+ with_mock_time do
53
+ Rack::MockRequest.new(described_class.new(app)).get("/path?q=true")
54
+ end
55
+ end
56
+
57
+ it "log clientip as metadata" do
58
+ expect(logger).to receive(:info)
59
+ .with(anything, hash_including("clientip"))
60
+ end
61
+
62
+ it "log verb as metadata" do
63
+ expect(logger).to receive(:info)
64
+ .with(anything, hash_including("verb" => "GET"))
65
+ end
66
+
67
+ it "log request as metadata" do
68
+ expect(logger).to receive(:info)
69
+ .with(anything, hash_including("request" => "/path?q=true"))
70
+ end
71
+
72
+ it "log http_version as metadata" do
73
+ expect(logger).to receive(:info)
74
+ .with(anything, hash_including("http_version" => nil))
75
+ end
76
+
77
+ it "log response as metadata" do
78
+ expect(logger).to receive(:info)
79
+ .with(anything, hash_including("response" => "200"))
80
+ end
81
+
82
+ it "log bytes as metadata" do
83
+ expect(logger).to receive(:info)
84
+ .with(anything, hash_including("bytes" => length))
85
+ end
86
+
87
+ it "log referrer as metadata" do
88
+ expect(logger).to receive(:info)
89
+ .with(anything, hash_including("referrer" => nil))
90
+ end
91
+
92
+ it "log agent as metadata" do
93
+ expect(logger).to receive(:info)
94
+ .with(anything, hash_including("agent" => nil))
95
+ end
96
+
97
+ it "log request_time as metadata" do
98
+ expect(logger).to receive(:info)
99
+ .with(anything, hash_including("request_time" => 0.0))
100
+ end
101
+
102
+ it "log request_full_url as metadata" do
103
+ expect(logger).to receive(:info)
104
+ .with(anything, hash_including("request_full_url" => nil))
105
+ end
106
+ end
107
+ end
@@ -1,10 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe LoggerFacade::Plugins::Console do
4
+ include LevelTestHelper
4
5
 
5
6
  subject { described_class.new }
6
7
 
7
8
  let(:time) { Time.new(1983, 01, 25, 13, 10, 01, '+00:00') }
9
+ let(:metadata){ { timestamp: time, pid: 100 } }
8
10
 
9
11
  before :each do
10
12
  allow(Time).to receive(:now) { time }
@@ -23,19 +25,6 @@ describe LoggerFacade::Plugins::Console do
23
25
 
24
26
  end
25
27
 
26
- def next_level(lev)
27
- levels = {
28
- trace: 0,
29
- debug: 1,
30
- info: 2,
31
- warn: 3,
32
- error: 4
33
- }
34
-
35
- val = levels[lev.to_sym]
36
- levels.select { |k,v| v > val }.keys.first
37
- end
38
-
39
28
  %w(trace debug info warn error).each do |level|
40
29
 
41
30
  context("logging in #{level} level") do
@@ -0,0 +1,207 @@
1
+ require 'json'
2
+
3
+ describe LoggerFacade::Plugins::Logstash do
4
+ include LevelTestHelper
5
+
6
+ subject { described_class.new }
7
+
8
+ let(:filename) { './test.log' }
9
+ let(:config) { { filename: filename } }
10
+ let(:time) { Time.new(1983, 01, 25, 13, 10, 01, '+00:00') }
11
+ let(:metadata){ { timestamp: time, pid: 100 } }
12
+ let(:file) { double('file') }
13
+
14
+ before :each do
15
+ allow(Time).to receive(:now) { time }
16
+ allow_any_instance_of(Object).to receive(:open) { file }
17
+ allow_any_instance_of(Object).to receive(:sync=)
18
+ end
19
+
20
+ describe("initialize") do
21
+ it 'defaults to logdevice without rotation' do
22
+ expect(LoggerFacade::Plugins::Logstash::LogDeviceWithoutRotation)
23
+ .to receive(:new).with(filename)
24
+ described_class.new(config)
25
+ end
26
+
27
+ it 'uses ruby Logger config' do
28
+ age = 'age'
29
+ size = 1024
30
+ config[:device] = { shift_age: age, shift_size: size }
31
+ expect(LoggerFacade::Plugins::Logstash::LogDeviceWithRotation)
32
+ .to receive(:new).with(filename, age, size)
33
+ described_class.new(config)
34
+ end
35
+
36
+ it 'raises an error on invalid filename configuration' do
37
+ expect { described_class.new({}) }.to raise_exception(RuntimeError)
38
+ end
39
+ end
40
+
41
+ describe('#is_debug') do
42
+
43
+ it('returns true when log level lower than info') do
44
+ subject = described_class.new(config)
45
+ expect(subject.is_debug).to be true
46
+ end
47
+
48
+ it('returns false when log level higher than debug') do
49
+ config[:level] = :info
50
+ subject = described_class.new(config)
51
+ expect(subject.is_debug).to be false
52
+ end
53
+
54
+ end
55
+
56
+ %w(trace debug info warn error).each do |level|
57
+
58
+ context("logging in #{level} level") do
59
+ subject do
60
+ config[:level] = level.to_sym
61
+ described_class.new(config)
62
+ end
63
+
64
+ let(:message) { "call with message" }
65
+ let(:logger) { "name" }
66
+ let(:severity) { level.to_sym }
67
+
68
+ context("when plugin log level lower or equal than #{level}") do
69
+
70
+ it("writes to file") do
71
+ expect(file).to receive(:write)
72
+ subject.send(severity, logger, message)
73
+ end
74
+
75
+ context 'format' do
76
+ it("writes a valid json") do
77
+ expect(file).to receive(:write) do |msg|
78
+ data = nil
79
+ expect { data = JSON.parse(msg) }.not_to raise_exception
80
+ expect(data).to be
81
+ end
82
+ subject.send(severity, logger, message)
83
+ end
84
+
85
+ it("ends with line break") do
86
+ expect(file).to receive(:write) do |msg|
87
+ expect(msg[-1,1]).to eq("\n")
88
+ end
89
+ subject.send(severity, logger, message)
90
+ end
91
+
92
+ context 'timestamp' do
93
+ # time + 1.month
94
+ let(:ts) { time + 2592000 }
95
+ let(:metadata) { { timestamp: ts } }
96
+
97
+ it("writes logstash timestamp format") do
98
+ expect(file).to receive(:write) do |msg|
99
+ data = JSON.parse(msg)
100
+ expect(data["@timestamp"]).to eq(time.iso8601)
101
+ end
102
+ subject.send(severity, logger, message)
103
+ end
104
+
105
+ it("uses metadata timestamp") do
106
+ expect(file).to receive(:write) do |msg|
107
+ data = JSON.parse(msg)
108
+ expect(data["@timestamp"]).to eq(ts.iso8601)
109
+ end
110
+ subject.send(severity, logger, message, metadata: metadata)
111
+ end
112
+
113
+ it("doesn't write field timestamp") do
114
+ expect(file).to receive(:write) do |msg|
115
+ data = JSON.parse(msg)["@fields"]
116
+ expect(data["timestamp"]).to be_nil
117
+ end
118
+ subject.send(severity, logger, message, metadata: metadata)
119
+ end
120
+ end
121
+
122
+ it("writes fields") do
123
+ expect(file).to receive(:write) do |msg|
124
+ data = JSON.parse(msg)
125
+ expect(data["@fields"]).to be
126
+ end
127
+ subject.send(severity, logger, message)
128
+ end
129
+
130
+ it("writes severity") do
131
+ expect(file).to receive(:write) do |msg|
132
+ data = JSON.parse(msg)["@fields"]
133
+ expect(data["severity"]).to eq(severity.to_s)
134
+ end
135
+ subject.send(severity, logger, message)
136
+ end
137
+
138
+ it("writes logger") do
139
+ expect(file).to receive(:write) do |msg|
140
+ data = JSON.parse(msg)["@fields"]
141
+ expect(data["logger"]).to eq(logger)
142
+ end
143
+ subject.send(severity, logger, message)
144
+ end
145
+
146
+ it("writes message") do
147
+ expect(file).to receive(:write) do |msg|
148
+ data = JSON.parse(msg)["@fields"]
149
+ expect(data["message"]).to eq(message)
150
+ end
151
+ subject.send(severity, logger, message)
152
+ end
153
+
154
+ it("writes other metadata fields") do
155
+ metadata = { context: true }
156
+ expect(file).to receive(:write) do |msg|
157
+ data = JSON.parse(msg)["@fields"]
158
+ expect(data["context"]).to eq(true)
159
+ end
160
+ subject.send(severity, logger, message, metadata: metadata)
161
+ end
162
+
163
+ context "on excepetion parameter" do
164
+
165
+ it('writes the exception message') do
166
+ error = Exception.new('test log')
167
+ expect(file).to receive(:write) do |msg|
168
+ data = JSON.parse(msg)["@fields"]
169
+ expect(data["message"]).to eq("test log")
170
+ end
171
+ subject.send(severity, logger, error)
172
+ end
173
+
174
+ it('writes the exception backtrace') do
175
+ error = Exception.new('test log')
176
+ error.set_backtrace "stacktrace"
177
+ expect(file).to receive(:write) do |msg|
178
+ data = JSON.parse(msg)["@fields"]
179
+ expect(data["backtrace"]).to eq(error.backtrace)
180
+ end
181
+ subject.send(severity, logger, error)
182
+ end
183
+ end
184
+ end
185
+
186
+ end
187
+
188
+ unless level == 'error'
189
+ context("when plugin log level higher than #{level}") do
190
+ subject do
191
+ config[:level] = next_level(level)
192
+ described_class.new(config)
193
+ end
194
+
195
+ it("doesn't write to file") do
196
+ expect(file).not_to receive(:write)
197
+ subject.send(level.to_sym, "name", message)
198
+ end
199
+
200
+ end
201
+ end
202
+
203
+ end
204
+
205
+ end
206
+
207
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logger_facade
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pedro Januário
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-31 00:00:00.000000000 Z
11
+ date: 2015-01-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0.5'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rack
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.6'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.6'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: hashie
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +164,20 @@ dependencies:
150
164
  - - "~>"
151
165
  - !ruby/object:Gem::Version
152
166
  version: '1.1'
167
+ - !ruby/object:Gem::Dependency
168
+ name: yajl-ruby
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '1.2'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '1.2'
153
181
  description: |-
154
182
  Simple class library to work as logger facade.
155
183
  This simple logger facade allows you to hook plugins to execute logging.
@@ -172,17 +200,22 @@ files:
172
200
  - lib/logger_facade/log.rb
173
201
  - lib/logger_facade/loggable.rb
174
202
  - lib/logger_facade/manager.rb
203
+ - lib/logger_facade/middleware/rack.rb
175
204
  - lib/logger_facade/plugins.rb
176
205
  - lib/logger_facade/plugins/airbrake.rb
206
+ - lib/logger_facade/plugins/base.rb
177
207
  - lib/logger_facade/plugins/console.rb
208
+ - lib/logger_facade/plugins/logstash.rb
178
209
  - lib/logger_facade/version.rb
179
210
  - logger_facade.gemspec
180
211
  - spec/spec_helper.rb
181
212
  - spec/unit/log_spec.rb
182
213
  - spec/unit/loggable_spec.rb
183
214
  - spec/unit/manager_spec.rb
215
+ - spec/unit/middleware/rack_spec.rb
184
216
  - spec/unit/plugins/airbrake_spec.rb
185
217
  - spec/unit/plugins/console_spec.rb
218
+ - spec/unit/plugins/logstash_spec.rb
186
219
  homepage: https://github.com/pjanuario/logger-facade-ruby
187
220
  licenses:
188
221
  - MIT
@@ -214,5 +247,7 @@ test_files:
214
247
  - spec/unit/log_spec.rb
215
248
  - spec/unit/loggable_spec.rb
216
249
  - spec/unit/manager_spec.rb
250
+ - spec/unit/middleware/rack_spec.rb
217
251
  - spec/unit/plugins/airbrake_spec.rb
218
252
  - spec/unit/plugins/console_spec.rb
253
+ - spec/unit/plugins/logstash_spec.rb