logger_facade 0.2.0 → 0.3.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: 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