lograge-with-time 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ac5cfd309efdd6412c63cc4295d0ba9117eb9c65
4
+ data.tar.gz: a80420cf987a1d75c1e8fcb4a070bfc6873bd9eb
5
+ SHA512:
6
+ metadata.gz: 4936ec0831533e52689c7d0ede3e586da8da4da5cb9d3dd4fc38fd5e952aae8685cf379e0423a091084e05ed1cf0157a1b74dbd9a493a773f107798a67156721
7
+ data.tar.gz: 2b52005718728e6d3f6ac9c5fbe9bd304342f033f80ea359d725eb401bcc978c97358da74e8b044efddba5939d4df6978f9eed6da58ea4b6cab4e7ef2fdbe4bb
@@ -0,0 +1,149 @@
1
+ require 'lograge/version'
2
+ require 'lograge/formatters/cee'
3
+ require 'lograge/formatters/json'
4
+ require 'lograge/formatters/graylog2'
5
+ require 'lograge/formatters/key_value'
6
+ require 'lograge/formatters/l2met'
7
+ require 'lograge/formatters/lines'
8
+ require 'lograge/formatters/logstash'
9
+ require 'lograge/formatters/raw'
10
+ require 'lograge/log_subscriber'
11
+ require 'active_support/core_ext/module/attribute_accessors'
12
+ require 'active_support/core_ext/string/inflections'
13
+ require 'active_support/ordered_options'
14
+
15
+ module Lograge
16
+ module_function
17
+
18
+ mattr_accessor :logger, :application, :ignore_tests
19
+
20
+ # Custom options that will be appended to log line
21
+ #
22
+ # Currently supported formats are:
23
+ # - Hash
24
+ # - Any object that responds to call and returns a hash
25
+ #
26
+ mattr_writer :custom_options
27
+ self.custom_options = nil
28
+
29
+ def self.custom_options(event)
30
+ if @@custom_options.respond_to?(:call)
31
+ @@custom_options.call(event)
32
+ else
33
+ @@custom_options
34
+ end
35
+ end
36
+
37
+ # Before format allows you to change the structure of the output.
38
+ # You've to pass in something callable
39
+ #
40
+ mattr_writer :before_format
41
+ self.before_format = nil
42
+
43
+ def self.before_format(data, payload)
44
+ result = nil
45
+ result = @@before_format.call(data, payload) if @@before_format
46
+ result || data
47
+ end
48
+
49
+ # Set conditions for events that should be ignored
50
+ #
51
+ # Currently supported formats are:
52
+ # - A single string representing a controller action, e.g. 'users#sign_in'
53
+ # - An array of strings representing controller actions
54
+ # - An object that responds to call with an event argument and returns
55
+ # true iff the event should be ignored.
56
+ #
57
+ # The action ignores are given to 'ignore_actions'. The callable ignores
58
+ # are given to 'ignore'. Both methods can be called multiple times, which
59
+ # just adds more ignore conditions to a list that is checked before logging.
60
+
61
+ def self.ignore_actions(actions)
62
+ ignore(lambda do |event|
63
+ params = event.payload[:params]
64
+ Array(actions).include?("#{params['controller']}##{params['action']}")
65
+ end)
66
+ end
67
+
68
+ def ignore_tests
69
+ @ignore_tests ||= []
70
+ end
71
+
72
+ def self.ignore(test)
73
+ ignore_tests.push(test) if test
74
+ end
75
+
76
+ def ignore_nothing
77
+ @ignore_tests = []
78
+ end
79
+
80
+ def self.ignore?(event)
81
+ ignore_tests.any? { |ignore_test| ignore_test.call(event) }
82
+ end
83
+
84
+ # Loglines are emitted with this log level
85
+ mattr_accessor :log_level
86
+ self.log_level = :info
87
+
88
+ # The emitted log format
89
+ #
90
+ # Currently supported formats are>
91
+ # - :lograge - The custom tense lograge format
92
+ # - :logstash - JSON formatted as a Logstash Event.
93
+ mattr_accessor :formatter
94
+
95
+ def self.remove_existing_log_subscriptions
96
+ ActiveSupport::LogSubscriber.log_subscribers.each do |subscriber|
97
+ case subscriber
98
+ when ActionView::LogSubscriber
99
+ unsubscribe(:action_view, subscriber)
100
+ when ActionController::LogSubscriber
101
+ unsubscribe(:action_controller, subscriber)
102
+ end
103
+ end
104
+ end
105
+
106
+ def self.unsubscribe(component, subscriber)
107
+ events = subscriber.public_methods(false).reject { |method| method.to_s == 'call' }
108
+ events.each do |event|
109
+ ActiveSupport::Notifications.notifier.listeners_for("#{event}.#{component}").each do |listener|
110
+ if listener.instance_variable_get('@delegate') == subscriber
111
+ ActiveSupport::Notifications.unsubscribe listener
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ def self.setup(app)
118
+ self.application = app
119
+ app.config.action_dispatch.rack_cache[:verbose] = false if app.config.action_dispatch.rack_cache
120
+ require 'lograge/rails_ext/rack/logger'
121
+ Lograge.remove_existing_log_subscriptions
122
+ Lograge::RequestLogSubscriber.attach_to :action_controller
123
+ Lograge.custom_options = lograge_config.custom_options
124
+ Lograge.before_format = lograge_config.before_format
125
+ Lograge.log_level = lograge_config.log_level || :info
126
+ support_deprecated_config # TODO: Remove with version 1.0
127
+ Lograge.formatter = lograge_config.formatter || Lograge::Formatters::KeyValue.new
128
+ Lograge.ignore_actions(lograge_config.ignore_actions)
129
+ Lograge.ignore(lograge_config.ignore_custom)
130
+ end
131
+
132
+ # TODO: Remove with version 1.0
133
+
134
+ def support_deprecated_config
135
+ return unless lograge_config.log_format
136
+
137
+ legacy_log_format = lograge_config.log_format
138
+ warning = 'config.lograge.log_format is deprecated. Use config.lograge.formatter instead.'
139
+ ActiveSupport::Deprecation.warn(warning, caller)
140
+ legacy_log_format = :key_value if legacy_log_format == :lograge
141
+ lograge_config.formatter = "Lograge::Formatters::#{legacy_log_format.to_s.classify}".constantize.new
142
+ end
143
+
144
+ def lograge_config
145
+ application.config.lograge
146
+ end
147
+ end
148
+
149
+ require 'lograge/railtie' if defined?(Rails)
@@ -0,0 +1,9 @@
1
+ module Lograge
2
+ module Formatters
3
+ class Cee
4
+ def call(data)
5
+ "@cee: #{JSON.dump(data)}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,30 @@
1
+ module Lograge
2
+ module Formatters
3
+ class Graylog2
4
+ def call(data)
5
+ # Cloning because we don't want to mess with the original when mutating keys.
6
+ data_clone = data.clone
7
+
8
+ base = {
9
+ short_message: short_message(data_clone)
10
+ }
11
+
12
+ # Add underscore to every key to follow GELF additional field syntax.
13
+ data_clone.keys.each do |key|
14
+ data_clone[underscore_prefix(key)] = data_clone[key]
15
+ data_clone.delete(key)
16
+ end
17
+
18
+ data_clone.merge(base)
19
+ end
20
+
21
+ def underscore_prefix(key)
22
+ "_#{key}".to_sym
23
+ end
24
+
25
+ def short_message(data)
26
+ "[#{data[:status]}] #{data[:method]} #{data[:path]} (#{data[:controller]}##{data[:action]})"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ require 'json'
2
+ module Lograge
3
+ module Formatters
4
+ class Json
5
+ def call(data)
6
+ ::JSON.dump(data)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,38 @@
1
+ module Lograge
2
+ module Formatters
3
+ class KeyValue
4
+ LOGRAGE_FIELDS = [
5
+ :time, :method, :path, :format, :controller, :action, :status, :error,
6
+ :duration, :view, :db, :location
7
+ ]
8
+
9
+ def call(data)
10
+ fields = fields_to_display(data)
11
+
12
+ event = fields.reduce([]) do |message, key|
13
+ next message unless data.key?(key)
14
+
15
+ message << format(key, data[key])
16
+ message
17
+ end
18
+ event.join(' ')
19
+ end
20
+
21
+ def fields_to_display(data)
22
+ LOGRAGE_FIELDS + (data.keys - LOGRAGE_FIELDS)
23
+ end
24
+
25
+ def format(key, value)
26
+ # Exactly preserve the previous output
27
+ # Parsing this can be ambigious if the error messages contains
28
+ # a single quote
29
+ value = "'#{value}'" if key == :error
30
+
31
+ # Ensure that we always have exactly two decimals
32
+ value = Kernel.format('%.2f', value) if value.is_a? Float
33
+
34
+ "#{key}=#{value}"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,36 @@
1
+ require 'lograge/formatters/key_value'
2
+
3
+ module Lograge
4
+ module Formatters
5
+ class L2met < KeyValue
6
+ L2MET_FIELDS = [
7
+ :method, :path, :format, :source, :status, :error,
8
+ :duration, :view, :db, :location
9
+ ]
10
+
11
+ def call(data)
12
+ super(modify_payload(data))
13
+ end
14
+
15
+ def format(key, value)
16
+ key = "measure#page.#{key}" if value.is_a?(Float)
17
+
18
+ super(key, value)
19
+ end
20
+
21
+ def fields_to_display(data)
22
+ L2MET_FIELDS + (data.keys - L2MET_FIELDS) - [:controller, :action]
23
+ end
24
+
25
+ def modify_payload(data)
26
+ data[:source] = source_field(data) if data[:controller] && data[:action]
27
+
28
+ data
29
+ end
30
+
31
+ def source_field(data)
32
+ "#{data[:controller].to_s.gsub('/', '-')}:#{data[:action]}"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,18 @@
1
+ module Lograge
2
+ module Formatters
3
+ class Lines
4
+ def call(data)
5
+ load_dependencies
6
+
7
+ ::Lines.dump(data)
8
+ end
9
+
10
+ def load_dependencies
11
+ require 'lines'
12
+ rescue LoadError
13
+ puts 'You need to install the lines gem to use this output.'
14
+ raise
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module Lograge
2
+ module Formatters
3
+ class Logstash
4
+ def call(data)
5
+ load_dependencies
6
+ event = LogStash::Event.new(data)
7
+
8
+ event.message = "[#{data[:status]}] #{data[:method]} #{data[:path]} (#{data[:controller]}##{data[:action]})"
9
+ event.to_json
10
+ end
11
+
12
+ def load_dependencies
13
+ require 'logstash-event'
14
+ rescue LoadError
15
+ puts 'You need to install the logstash-event gem to use the logstash output.'
16
+ raise
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ module Lograge
2
+ module Formatters
3
+ class Raw
4
+ def call(data)
5
+ data
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,104 @@
1
+ require 'json'
2
+
3
+ require 'active_support/core_ext/class/attribute'
4
+ require 'active_support/log_subscriber'
5
+
6
+ module Lograge
7
+ class RequestLogSubscriber < ActiveSupport::LogSubscriber
8
+ def process_action(event)
9
+ return if Lograge.ignore?(event)
10
+
11
+ payload = event.payload
12
+
13
+ data = extract_time(event)
14
+ data.merge! extract_request(payload)
15
+ data.merge! extract_status(payload)
16
+ data.merge! runtimes(event)
17
+ data.merge! location(event)
18
+ data.merge! custom_options(event)
19
+
20
+ data = before_format(data, payload)
21
+ formatted_message = Lograge.formatter.call(data)
22
+ logger.send(Lograge.log_level, formatted_message)
23
+ end
24
+
25
+ def redirect_to(event)
26
+ Thread.current[:lograge_location] = event.payload[:location]
27
+ end
28
+
29
+ def logger
30
+ Lograge.logger.presence || super
31
+ end
32
+
33
+ private
34
+
35
+ def extract_time(event)
36
+ {
37
+ time: event.time
38
+ }
39
+ end
40
+
41
+ def extract_request(payload)
42
+ {
43
+ method: payload[:method],
44
+ path: extract_path(payload),
45
+ format: extract_format(payload),
46
+ controller: payload[:params]['controller'],
47
+ action: payload[:params]['action']
48
+ }
49
+ end
50
+
51
+ def extract_path(payload)
52
+ payload[:path].split('?').first
53
+ end
54
+
55
+ def extract_format(payload)
56
+ if ::ActionPack::VERSION::MAJOR == 3 && ::ActionPack::VERSION::MINOR == 0
57
+ payload[:formats].first
58
+ else
59
+ payload[:format]
60
+ end
61
+ end
62
+
63
+ def extract_status(payload)
64
+ if payload[:status]
65
+ { status: payload[:status].to_i }
66
+ elsif payload[:exception]
67
+ exception, message = payload[:exception]
68
+ { status: 500, error: "#{exception}:#{message}" }
69
+ else
70
+ { status: 0 }
71
+ end
72
+ end
73
+
74
+ def custom_options(event)
75
+ Lograge.custom_options(event) || {}
76
+ end
77
+
78
+ def before_format(data, payload)
79
+ Lograge.before_format(data, payload)
80
+ end
81
+
82
+ def runtimes(event)
83
+ {
84
+ duration: event.duration,
85
+ view: event.payload[:view_runtime],
86
+ db: event.payload[:db_runtime]
87
+ }.reduce({}) do |runtimes, (name, runtime)|
88
+ runtimes[name] = runtime.to_f.round(2) if runtime
89
+ runtimes
90
+ end
91
+ end
92
+
93
+ def location(_event)
94
+ location = Thread.current[:lograge_location]
95
+
96
+ if location
97
+ Thread.current[:lograge_location] = nil
98
+ { location: location }
99
+ else
100
+ {}
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,25 @@
1
+ require 'active_support/concern'
2
+ require 'rails/rack/logger'
3
+
4
+ module Rails
5
+ module Rack
6
+ # Overwrites defaults of Rails::Rack::Logger that cause
7
+ # unnecessary logging.
8
+ # This effectively removes the log lines from the log
9
+ # that say:
10
+ # Started GET / for 192.168.2.1...
11
+ class Logger
12
+ # Overwrites Rails 3.2 code that logs new requests
13
+ def call_app(*args)
14
+ env = args.last
15
+ @app.call(env)
16
+ ensure
17
+ ActiveSupport::LogSubscriber.flush_all!
18
+ end
19
+
20
+ # Overwrites Rails 3.0/3.1 code that logs new requests
21
+ def before_dispatch(_env)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/railtie'
2
+ require 'action_view/log_subscriber'
3
+ require 'action_controller/log_subscriber'
4
+
5
+ module Lograge
6
+ class Railtie < Rails::Railtie
7
+ config.lograge = ActiveSupport::OrderedOptions.new
8
+ config.lograge.enabled = false
9
+
10
+ initializer :lograge do |app|
11
+ Lograge.setup(app) if app.config.lograge.enabled
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Lograge
2
+ VERSION = '0.4.0'
3
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lograge-with-time
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Mathias Meyer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: guard-rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: actionpack
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '3'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: railties
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '3'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '3'
83
+ description: Tame Rails' multi-line logging into a single line per request
84
+ email:
85
+ - meyer@paperplanes.de
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - lib/lograge.rb
91
+ - lib/lograge/formatters/cee.rb
92
+ - lib/lograge/formatters/graylog2.rb
93
+ - lib/lograge/formatters/json.rb
94
+ - lib/lograge/formatters/key_value.rb
95
+ - lib/lograge/formatters/l2met.rb
96
+ - lib/lograge/formatters/lines.rb
97
+ - lib/lograge/formatters/logstash.rb
98
+ - lib/lograge/formatters/raw.rb
99
+ - lib/lograge/log_subscriber.rb
100
+ - lib/lograge/rails_ext/rack/logger.rb
101
+ - lib/lograge/railtie.rb
102
+ - lib/lograge/version.rb
103
+ homepage: https://github.com/roidrage/lograge
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.2.0
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Tame Rails' multi-line logging into a single line per request
127
+ test_files: []