key_value_logging 0.0.1
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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +60 -0
- data/Rakefile +1 -0
- data/key_value_logging.gemspec +28 -0
- data/lib/key_value_logging/logger_middleware.rb +20 -0
- data/lib/key_value_logging/railtie.rb +31 -0
- data/lib/key_value_logging/tagged_logging.rb +82 -0
- data/lib/key_value_logging/version.rb +3 -0
- data/lib/key_value_logging.rb +9 -0
- data/spec/logger_middleware_spec.rb +38 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/tagged_logging_spec.rb +18 -0
- metadata +146 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Maximilian Schulz
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# KeyValueLogging
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'key_value_logging'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install key_value_logging
|
18
|
+
|
19
|
+
## Environment specific configuration
|
20
|
+
|
21
|
+
Add tag to all requests:
|
22
|
+
|
23
|
+
# By referencing a request object method
|
24
|
+
config.key_value_logging.tags = { uuid: :uuid }
|
25
|
+
|
26
|
+
# By providing a proc
|
27
|
+
config.key_value_logging.tags = { params: ->(request){ request.filtered_params } }
|
28
|
+
|
29
|
+
# By providing flat data
|
30
|
+
config.key_value_logging.tags = { mydata: 'Fixed string' }
|
31
|
+
|
32
|
+
By default the key value tags are rendered like ```key=value```. But you may
|
33
|
+
need to preprocess them. In order to make your life easier, you can access all
|
34
|
+
data as a hash by setting the raw format option. The log message itself will be
|
35
|
+
available at the ```:message``` key:
|
36
|
+
|
37
|
+
config.key_value_logging.format = :raw
|
38
|
+
|
39
|
+
Especially in combination with the raw format option, it makes sense to set your
|
40
|
+
own formatter. This could post-process the raw tags and data. As we extend the
|
41
|
+
logger and formatter, we provide an easy way to set yout own formatter while
|
42
|
+
taking advantage of the custom key-value behaviour:
|
43
|
+
|
44
|
+
config.key_value_logging.formatter = YourCustomLogFormatter.new
|
45
|
+
|
46
|
+
## In-app usage
|
47
|
+
|
48
|
+
Like the default rails TaggedLogging logger, you can tag log messages with this
|
49
|
+
logger. The syntax is almost the same. But instead of providing flat strings, you
|
50
|
+
now provide a hash with key and value:
|
51
|
+
|
52
|
+
logger.tagged(city: 'Cologne', country: 'Cologne') { logger.info 'made with love' }
|
53
|
+
|
54
|
+
## Contributing
|
55
|
+
|
56
|
+
1. Fork it
|
57
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
58
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
59
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
60
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'key_value_logging/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "key_value_logging"
|
8
|
+
gem.version = KeyValueLogging::VERSION
|
9
|
+
gem.authors = ["Maximilian Schulz"]
|
10
|
+
gem.email = ["m.schulz@kulturfluss.de"]
|
11
|
+
gem.description = %q{KeyValue based replacement for rails TaggedLogging}
|
12
|
+
gem.summary = %q{Rails TaggedLogging only support simple tags.
|
13
|
+
When using logging solutions with automatic data detection,
|
14
|
+
key-value pairs are more useful. This gem allows you to
|
15
|
+
log tags as key-value paris and format them as you like.}
|
16
|
+
gem.homepage = "https://github.com/railslove/key_value_logging"
|
17
|
+
gem.files = `git ls-files`.split($/)
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
19
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
|
+
gem.require_paths = ["lib"]
|
21
|
+
|
22
|
+
gem.add_runtime_dependency 'activesupport', '>= 3'
|
23
|
+
gem.add_runtime_dependency 'rack', '>=1.0.0'
|
24
|
+
gem.add_runtime_dependency 'railties', '>= 3'
|
25
|
+
gem.add_runtime_dependency 'formatted_rails_logger'
|
26
|
+
|
27
|
+
gem.add_development_dependency 'rspec'
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rails'
|
2
|
+
|
3
|
+
module KeyValueLogging
|
4
|
+
class LoggerMiddleware < Rails::Rack::Logger
|
5
|
+
def compute_tags(request)
|
6
|
+
tag_value_pairs = @taggers.map do |tag, key_or_proc|
|
7
|
+
value = case key_or_proc
|
8
|
+
when Proc
|
9
|
+
key_or_proc.call(request)
|
10
|
+
when Symbol
|
11
|
+
request.send(key_or_proc)
|
12
|
+
else
|
13
|
+
key_or_proc
|
14
|
+
end
|
15
|
+
[tag, value]
|
16
|
+
end
|
17
|
+
Hash[*tag_value_pairs.flatten]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module KeyValueLogging
|
2
|
+
class KeyValueLoggingRailtie < Rails::Railtie
|
3
|
+
|
4
|
+
# Default options for the key value logger
|
5
|
+
config.key_value_logging = ActiveSupport::OrderedOptions.new
|
6
|
+
config.key_value_logging.tags = {}
|
7
|
+
config.key_value_logging.format = :default
|
8
|
+
config.key_value_logging.formatter = nil
|
9
|
+
|
10
|
+
# Add custom logging middleware, after the logger is initialized
|
11
|
+
initializer "key_value_logging_railtie.configure_rails_initialization", after: :initialize_logger do |app|
|
12
|
+
# Apply monkey patch to BufferedLogger to allow to set a formatter
|
13
|
+
require 'formatted_rails_logger'
|
14
|
+
FormattedRailsLogger.patch_rails
|
15
|
+
|
16
|
+
logger = Rails.logger
|
17
|
+
|
18
|
+
# If provided, set the custom formatter
|
19
|
+
if config.key_value_logging.formatter.present?
|
20
|
+
logger.formatter = config.key_value_logging.formatter
|
21
|
+
end
|
22
|
+
|
23
|
+
# Extend the logger with key value logging
|
24
|
+
Rails.logger = KeyValueLogging::TaggedLogging.new(logger, config.key_value_logging.format)
|
25
|
+
|
26
|
+
# Replace the default rails with thw key value logger middleware
|
27
|
+
app.middleware.delete(Rails::Rack::Logger)
|
28
|
+
app.middleware.use KeyValueLogging::LoggerMiddleware, config.key_value_logging.tags
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'active_support/all'
|
3
|
+
|
4
|
+
# Wraps any standard Logger object to provide tagging capabilities.
|
5
|
+
#
|
6
|
+
# logger = KeyValueTaggedLogging.new(Logger.new(STDOUT))
|
7
|
+
# logger.tagged('location' => 'Cologne') { logger.info 'Stuff' } # Logs "Stuff location=Cologne"
|
8
|
+
module KeyValueLogging
|
9
|
+
module TaggedLogging
|
10
|
+
module Formatter # :nodoc:
|
11
|
+
attr_accessor :key_value_format
|
12
|
+
|
13
|
+
# This method is invoked when a log event occurs.
|
14
|
+
def call(severity, timestamp, progname, msg)
|
15
|
+
formatted_msg = self.send("process_#{key_value_format}", msg)
|
16
|
+
super(severity, timestamp, progname, formatted_msg)
|
17
|
+
end
|
18
|
+
|
19
|
+
def tagged(tags)
|
20
|
+
# Add temporary tags to current tags
|
21
|
+
new_tags = push_tags(tags)
|
22
|
+
yield self
|
23
|
+
ensure
|
24
|
+
# Remove the temporary tags from current tags
|
25
|
+
pop_tags(tags.keys)
|
26
|
+
end
|
27
|
+
|
28
|
+
def push_tags(tags)
|
29
|
+
tags.tap do |new_tags|
|
30
|
+
current_tags.merge! new_tags
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def pop_tags(keys)
|
35
|
+
keys.each { |key| current_tags.delete(key) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def clear_tags!
|
39
|
+
current_tags.clear
|
40
|
+
end
|
41
|
+
|
42
|
+
def current_tags
|
43
|
+
Thread.current[:key_value_tagged_logging_tags] ||= {}
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def process_default(msg)
|
49
|
+
tags = current_tags.map { |key, value| "#{key}=#{value}" }
|
50
|
+
[msg, tags].flatten.compact.join(' ')
|
51
|
+
end
|
52
|
+
|
53
|
+
def process_raw(msg)
|
54
|
+
if msg.kind_of?(Hash)
|
55
|
+
current_tags.merge(msg)
|
56
|
+
else
|
57
|
+
current_tags.merge(message: msg)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.new(logger, key_value_format = 'default')
|
63
|
+
yield(logger) if block_given?
|
64
|
+
# Ensure we set a default formatter so we aren't extending nil!
|
65
|
+
logger.formatter ||= ActiveSupport::Logger::SimpleFormatter.new
|
66
|
+
logger.formatter.extend Formatter
|
67
|
+
logger.formatter.key_value_format = key_value_format
|
68
|
+
logger.extend(self)
|
69
|
+
end
|
70
|
+
|
71
|
+
delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter
|
72
|
+
|
73
|
+
def tagged(tags = {})
|
74
|
+
formatter.tagged(tags) { yield self }
|
75
|
+
end
|
76
|
+
|
77
|
+
def flush
|
78
|
+
clear_tags!
|
79
|
+
super if defined?(super)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rack/test'
|
3
|
+
|
4
|
+
describe KeyValueLogging::LoggerMiddleware do
|
5
|
+
include Rack::Test::Methods
|
6
|
+
|
7
|
+
let(:output) { StringIO.new }
|
8
|
+
let(:config) { nil }
|
9
|
+
|
10
|
+
let(:app) do
|
11
|
+
Rails.logger = KeyValueLogging::TaggedLogging.new(Logger.new(output))
|
12
|
+
middleware_config = config
|
13
|
+
Rack::Builder.new do
|
14
|
+
use KeyValueLogging::LoggerMiddleware, middleware_config
|
15
|
+
run lambda { |env|
|
16
|
+
[200, { 'Content-Type' => 'text/plain' }, ['Hello World!']]
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'config by request attribute' do
|
22
|
+
let(:config) { { params: :params } }
|
23
|
+
before { get '/test?hello=world' }
|
24
|
+
it { expect(output.string).to include('params={"hello"=>"world"}') }
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'config by proc' do
|
28
|
+
let(:config) { { params: ->(request) { request.params } } }
|
29
|
+
before { get '/test?hello=world' }
|
30
|
+
it { expect(output.string).to include('params={"hello"=>"world"}') }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'config with invalid configuration' do
|
34
|
+
let(:config) { { invalid: ['test'] } }
|
35
|
+
before { get '/test?hello=world' }
|
36
|
+
it { expect(output.string).to include('invalid=test') }
|
37
|
+
end
|
38
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
require 'key_value_logging'
|
9
|
+
ENV['RAILS_ENV'] = 'test'
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
13
|
+
config.run_all_when_everything_filtered = true
|
14
|
+
config.filter_run :focus
|
15
|
+
|
16
|
+
# Run specs in random order to surface order dependencies. If you find an
|
17
|
+
# order dependency and want to debug it, you can fix the order by providing
|
18
|
+
# the seed, which is printed after each run.
|
19
|
+
# --seed 1234
|
20
|
+
config.order = 'random'
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe KeyValueLogging::TaggedLogging do
|
4
|
+
let(:output) { StringIO.new }
|
5
|
+
before { logger.tagged(location: 'Cologne'){ logger.info('My Message') } }
|
6
|
+
|
7
|
+
context 'default message style' do
|
8
|
+
let(:logger) { KeyValueLogging::TaggedLogging.new(Logger.new(output)) }
|
9
|
+
it { expect(output.string).to include('My Message') }
|
10
|
+
it { expect(output.string).to include('location=Cologne') }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'raw message style' do
|
14
|
+
let(:logger) { KeyValueLogging::TaggedLogging.new(Logger.new(output), :raw) }
|
15
|
+
it { expect(output.string).to include(':message=>"My Message"') }
|
16
|
+
it { expect(output.string).to include(':location=>"Cologne"') }
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: key_value_logging
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Maximilian Schulz
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-08-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rack
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.0.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.0.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: railties
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: formatted_rails_logger
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: KeyValue based replacement for rails TaggedLogging
|
95
|
+
email:
|
96
|
+
- m.schulz@kulturfluss.de
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- .gitignore
|
102
|
+
- .rspec
|
103
|
+
- Gemfile
|
104
|
+
- LICENSE.txt
|
105
|
+
- README.md
|
106
|
+
- Rakefile
|
107
|
+
- key_value_logging.gemspec
|
108
|
+
- lib/key_value_logging.rb
|
109
|
+
- lib/key_value_logging/logger_middleware.rb
|
110
|
+
- lib/key_value_logging/railtie.rb
|
111
|
+
- lib/key_value_logging/tagged_logging.rb
|
112
|
+
- lib/key_value_logging/version.rb
|
113
|
+
- spec/logger_middleware_spec.rb
|
114
|
+
- spec/spec_helper.rb
|
115
|
+
- spec/tagged_logging_spec.rb
|
116
|
+
homepage: https://github.com/railslove/key_value_logging
|
117
|
+
licenses: []
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options: []
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ! '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 1.8.24
|
137
|
+
signing_key:
|
138
|
+
specification_version: 3
|
139
|
+
summary: Rails TaggedLogging only support simple tags. When using logging solutions
|
140
|
+
with automatic data detection, key-value pairs are more useful. This gem allows
|
141
|
+
you to log tags as key-value paris and format them as you like.
|
142
|
+
test_files:
|
143
|
+
- spec/logger_middleware_spec.rb
|
144
|
+
- spec/spec_helper.rb
|
145
|
+
- spec/tagged_logging_spec.rb
|
146
|
+
has_rdoc:
|