paul_bunyan 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/.travis.yml +9 -0
- data/Dockerfile +13 -0
- data/Gemfile +6 -0
- data/Guardfile +16 -0
- data/LICENSE.txt +22 -0
- data/README.md +51 -0
- data/README.rdoc +3 -0
- data/Rakefile +19 -0
- data/bin/logging_demo +17 -0
- data/build.sh +7 -0
- data/docker-compose.yml +4 -0
- data/lib/paul_bunyan.rb +70 -0
- data/lib/paul_bunyan/json_formatter.rb +122 -0
- data/lib/paul_bunyan/level.rb +28 -0
- data/lib/paul_bunyan/log_relayer.rb +148 -0
- data/lib/paul_bunyan/rails_ext.rb +7 -0
- data/lib/paul_bunyan/rails_ext/instrumentation.rb +41 -0
- data/lib/paul_bunyan/rails_ext/rack_logger.rb +24 -0
- data/lib/paul_bunyan/railtie.rb +75 -0
- data/lib/paul_bunyan/railtie/log_subscriber.rb +182 -0
- data/lib/paul_bunyan/text_formatter.rb +11 -0
- data/lib/paul_bunyan/version.rb +3 -0
- data/lib/tasks/paul_bunyan_tasks.rake +4 -0
- data/paul_bunyan.gemspec +30 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +28 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/secret_token.rb +1 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +56 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/gemfiles/40.gemfile +5 -0
- data/spec/gemfiles/40.gemfile.lock +137 -0
- data/spec/gemfiles/41.gemfile +5 -0
- data/spec/gemfiles/41.gemfile.lock +142 -0
- data/spec/gemfiles/42.gemfile +5 -0
- data/spec/gemfiles/42.gemfile.lock +167 -0
- data/spec/lib/paul_bunyan/json_formatter_spec.rb +237 -0
- data/spec/lib/paul_bunyan/level_spec.rb +78 -0
- data/spec/lib/paul_bunyan/log_relayer_spec.rb +333 -0
- data/spec/lib/paul_bunyan/rails_ext/instrumentation_spec.rb +81 -0
- data/spec/lib/paul_bunyan/railtie/log_subscriber_spec.rb +304 -0
- data/spec/lib/paul_bunyan/railtie_spec.rb +37 -0
- data/spec/lib/paul_bunyan_spec.rb +137 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/notification_helpers.rb +22 -0
- metadata +303 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0c8be4508c3bf748cc21adad0846a24d822dbea2
|
4
|
+
data.tar.gz: bee128e2be543d84c4ae31571949385c53c74a6f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 49db1d3690ffdf9756b57200227e1ddb797dd867064fb3c4f63e6264e1babd8737473986abd01497a02a22b962ad17bcbfe4a4ad91ec7431c499307304c8363d
|
7
|
+
data.tar.gz: a41bb6c373d12cc1a4d68e0c38524aa5bea49a6e8adc175f439fdd17c7dcf9f53e46c39f03a8a7b279702b8c9c08efa7f72a103e79975cbcb7b9fb997c6936be
|
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
log/*.log
|
19
|
+
pkg/
|
20
|
+
spec/dummy/db/*.sqlite3
|
21
|
+
spec/dummy/db/*.sqlite3-journal
|
22
|
+
spec/dummy/log/*.log
|
23
|
+
spec/dummy/tmp/
|
24
|
+
spec/dummy/.sass-cache
|
data/.travis.yml
ADDED
data/Dockerfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
FROM docker.insops.net/instructure/instructure-ruby:2.1
|
2
|
+
MAINTAINER Tyler Pickett <tpickett@instructure.com>
|
3
|
+
|
4
|
+
WORKDIR /usr/src/app
|
5
|
+
COPY Gemfile* *.gemspec /usr/src/app/
|
6
|
+
COPY lib/paul_bunyan/version.rb /usr/src/app/lib/paul_bunyan/
|
7
|
+
RUN bundle install
|
8
|
+
|
9
|
+
COPY . /usr/src/app
|
10
|
+
USER root
|
11
|
+
RUN chown -R docker:docker /usr/src/app/*
|
12
|
+
USER docker
|
13
|
+
CMD bundle exec wwtd --parallel
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
5
|
+
# rspec may be run, below are examples of the most common uses.
|
6
|
+
# * bundler: 'bundle exec rspec'
|
7
|
+
# * bundler binstubs: 'bin/rspec'
|
8
|
+
# * spring: 'bin/rsspec' (This will use spring if running and you have
|
9
|
+
# installed the spring binstubs per the docs)
|
10
|
+
# * zeus: 'zeus rspec' (requires the server to be started separetly)
|
11
|
+
# * 'just' rspec: 'rspec'
|
12
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
13
|
+
watch(%r{^spec/.+_spec\.rb$})
|
14
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
15
|
+
watch('spec/spec_helper.rb') { "spec" }
|
16
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Instructure Inc.
|
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,51 @@
|
|
1
|
+
# Paul Bunyan (formerly: Logging)
|
2
|
+
|
3
|
+
PaulBunyan is a re-usable component with a globally accessible Logger with extra
|
4
|
+
support for handling logging in Rails.
|
5
|
+
|
6
|
+
```
|
7
|
+
class Foo
|
8
|
+
include PaulBunyan
|
9
|
+
|
10
|
+
def bar
|
11
|
+
logger.warn "blah"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
```
|
15
|
+
|
16
|
+
Also included is a Railtie that overrides the default rails logger to always
|
17
|
+
print to STDOUT as well as format the messages to JSON for machine readable
|
18
|
+
goodness. This has been tested with Rails 4.2 but should compatible with
|
19
|
+
Rails 3 or newer since that's when Railties were introduced.
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Add this line to your application's Gemfile:
|
24
|
+
|
25
|
+
gem 'paul_bunyan'
|
26
|
+
|
27
|
+
And then execute:
|
28
|
+
|
29
|
+
$ bundle
|
30
|
+
|
31
|
+
Or install it yourself as:
|
32
|
+
|
33
|
+
$ gem install paul_bunyan
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
### Non-Rails projects:
|
38
|
+
|
39
|
+
```
|
40
|
+
require 'paul_bunyan'
|
41
|
+
|
42
|
+
include PaulBunyan::Logger
|
43
|
+
|
44
|
+
PaulBunyan.set_logger(STDOUT)
|
45
|
+
|
46
|
+
logger.warn "blah"
|
47
|
+
```
|
48
|
+
|
49
|
+
### Rails projects:
|
50
|
+
|
51
|
+
Nothing after it's added to your Gemfile, the Railtie takes care of the rest.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
require "wwtd/tasks"
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'PaulBunyan'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
Bundler::GemHelper.install_tasks
|
18
|
+
|
19
|
+
task default: "wwtd"
|
data/bin/logging_demo
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require_relative '../lib/logging'
|
5
|
+
|
6
|
+
logger = PaulBunyan.set_logger(STDOUT)
|
7
|
+
puts "JSONFormatter"
|
8
|
+
logger.debug("PaulBunyanDemo") { "This is my debug message" }
|
9
|
+
logger.info("PaulBunyanDemo") { "This is my info message" }
|
10
|
+
logger.info("PaulBunyanDemo") { {foo: "bar", baz: "qux"} }
|
11
|
+
logger.warn("PaulBunyanDemo") { "This is my warn message" }
|
12
|
+
begin
|
13
|
+
raise "heck"
|
14
|
+
rescue => ex
|
15
|
+
logger.error("PaulBunyanDemo") { ex }
|
16
|
+
end
|
17
|
+
logger.fatal("PaulBunyanDemo") { "This is my fatal message" }
|
data/build.sh
ADDED
data/docker-compose.yml
ADDED
data/lib/paul_bunyan.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
require_relative 'paul_bunyan/json_formatter'
|
4
|
+
require_relative 'paul_bunyan/level'
|
5
|
+
require_relative 'paul_bunyan/log_relayer'
|
6
|
+
require_relative 'paul_bunyan/text_formatter'
|
7
|
+
require_relative 'paul_bunyan/version'
|
8
|
+
|
9
|
+
require_relative 'paul_bunyan/railtie' if defined? ::Rails::Railtie
|
10
|
+
|
11
|
+
# Example Usage:
|
12
|
+
#
|
13
|
+
# class MyClass
|
14
|
+
# include PaulBunyan
|
15
|
+
#
|
16
|
+
# def initialize
|
17
|
+
# logger.info{ "something is working!" }
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
module PaulBunyan
|
22
|
+
class Error < StandardError; end
|
23
|
+
class UnknownFormatterError < Error; end
|
24
|
+
class UnknownLevelError < Error; end
|
25
|
+
|
26
|
+
# The ONE method we care about.
|
27
|
+
def logger
|
28
|
+
PaulBunyan.logger
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
attr_accessor :default_formatter_type
|
33
|
+
PaulBunyan.default_formatter_type = :json
|
34
|
+
|
35
|
+
def logger
|
36
|
+
create_logger(STDOUT) unless @logger
|
37
|
+
@logger
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_logger(logdev, shift_age = 0, shift_size = 1048576, formatter_type: PaulBunyan.default_formatter_type)
|
41
|
+
logger = Logger.new(logdev, shift_age, shift_size)
|
42
|
+
logger.formatter = default_formatter(formatter_type) unless formatter_type.nil?
|
43
|
+
add_logger(logger)
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_logger(logger)
|
47
|
+
@logger ||= LogRelayer.new
|
48
|
+
@logger.add_logger(logger)
|
49
|
+
logger
|
50
|
+
end
|
51
|
+
|
52
|
+
def remove_logger(logger)
|
53
|
+
@logger.remove_logger(logger)
|
54
|
+
end
|
55
|
+
|
56
|
+
private def default_formatter(formatter_type)
|
57
|
+
case formatter_type
|
58
|
+
when :json
|
59
|
+
JSONFormatter.new
|
60
|
+
when :text
|
61
|
+
TextFormatter.new
|
62
|
+
else
|
63
|
+
fail UnknownFormatterError, "Unknown formatter type #{formatter_type}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# For backwards compatibility with applications that used this prior to the rename
|
70
|
+
Logging = PaulBunyan
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module PaulBunyan
|
5
|
+
class JSONFormatter
|
6
|
+
DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%3N'
|
7
|
+
|
8
|
+
def call(severity, time, progname, msg)
|
9
|
+
metadata = {
|
10
|
+
"ts" => time.utc.strftime(DATETIME_FORMAT),
|
11
|
+
"unix_ts" => time.to_f,
|
12
|
+
"severity" => severity,
|
13
|
+
"pid" => $$,
|
14
|
+
}
|
15
|
+
metadata['program'] = progname if progname
|
16
|
+
metadata['tags'] = current_tags unless current_tags.empty?
|
17
|
+
|
18
|
+
message_data = format_message(msg)
|
19
|
+
|
20
|
+
JSON::generate(merge_metadata_and_message(metadata, message_data)) + "\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
def clear_tags!
|
24
|
+
current_tags.clear
|
25
|
+
end
|
26
|
+
|
27
|
+
def current_tags
|
28
|
+
thread_key = @thread_key ||= "logging_tagged_logging_tags:#{ Thread.current.object_id }"
|
29
|
+
Thread.current[thread_key] ||= []
|
30
|
+
end
|
31
|
+
|
32
|
+
def datetime_format=(value)
|
33
|
+
# intentional nop because the whole point of this formatter is
|
34
|
+
# to have a consistent machine parsable format :-P
|
35
|
+
end
|
36
|
+
|
37
|
+
def datetime_format
|
38
|
+
DATETIME_FORMAT
|
39
|
+
end
|
40
|
+
|
41
|
+
def pop_tags(count = 1)
|
42
|
+
current_tags.pop(count)
|
43
|
+
end
|
44
|
+
|
45
|
+
def push_tags(*tags)
|
46
|
+
tags.flatten.reject{|t| t.nil? || t.to_s.strip == '' }.tap do |clean_tags|
|
47
|
+
current_tags.concat(clean_tags)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def tagged(*tags)
|
52
|
+
clean_tags = push_tags(tags)
|
53
|
+
yield
|
54
|
+
ensure
|
55
|
+
pop_tags(clean_tags.size)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# TODO: extract all of this formatting/merging out into another class if it grows
|
61
|
+
def format_message(message)
|
62
|
+
case message
|
63
|
+
when Exception
|
64
|
+
format_exception(message)
|
65
|
+
when String
|
66
|
+
format_string(message)
|
67
|
+
else
|
68
|
+
format_generic_object(message)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def format_exception(exception)
|
73
|
+
# TODO: capture the exception cause if it is present and handle the case where
|
74
|
+
# cause isn't actually an exception (such as Parslet::ParseFailed#cause)
|
75
|
+
{
|
76
|
+
"exception.class" => exception.class.to_s,
|
77
|
+
"exception.backtrace" => exception.backtrace,
|
78
|
+
"exception.message" => exception.message,
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def format_string(message)
|
83
|
+
{ "message" => strip_ansi(message.strip) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def format_generic_object(object)
|
87
|
+
if object.respond_to?(:to_h)
|
88
|
+
object.to_h
|
89
|
+
elsif object.respond_to?(:to_hash)
|
90
|
+
object.to_hash
|
91
|
+
else
|
92
|
+
format_string(object.inspect)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def merge_metadata_and_message(metadata, message)
|
97
|
+
clean_message = sanitize_message_keys(message, metadata.keys)
|
98
|
+
metadata.merge(clean_message)
|
99
|
+
end
|
100
|
+
|
101
|
+
def sanitize_message_keys(message, metadata_keys)
|
102
|
+
message.inject({}) { |clean, (key, value)|
|
103
|
+
key = key.to_s
|
104
|
+
if metadata_keys.include?(key)
|
105
|
+
clean["user.#{ key }"] = value
|
106
|
+
else
|
107
|
+
clean[key] = value
|
108
|
+
end
|
109
|
+
clean
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
ANSI_REGEX = /(?:\e\[|\u009b)(?:\d{1,3}(?:;\d{0,3})*)?[0-9A-MRcf-npqrsuy]/
|
114
|
+
def strip_ansi(value)
|
115
|
+
if value.respond_to?(:to_str)
|
116
|
+
value.to_str.gsub(ANSI_REGEX, '')
|
117
|
+
elsif value
|
118
|
+
value.gsub(ANSI_REGEX, '')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module PaulBunyan
|
2
|
+
module Level
|
3
|
+
LEVEL_MAP = {
|
4
|
+
DEBUG: Logger::DEBUG,
|
5
|
+
INFO: Logger::INFO,
|
6
|
+
WARN: Logger::WARN,
|
7
|
+
ERROR: Logger::ERROR,
|
8
|
+
FATAL: Logger::FATAL,
|
9
|
+
UNKNOWN: Logger::UNKNOWN
|
10
|
+
}.freeze
|
11
|
+
LOGGING_LEVEL_KEYS = LEVEL_MAP.keys.freeze
|
12
|
+
LOGGING_LEVELS = (Logger::DEBUG..Logger::UNKNOWN).freeze
|
13
|
+
|
14
|
+
def self.coerce_level(level)
|
15
|
+
coerced_level = level || Logger::DEBUG
|
16
|
+
if level =~ /\A\s*\d+\s*\z/
|
17
|
+
coerced_level = level.to_i
|
18
|
+
elsif level.is_a?(String) || level.is_a?(Symbol)
|
19
|
+
coerced_level = LEVEL_MAP[level.upcase.to_sym]
|
20
|
+
end
|
21
|
+
|
22
|
+
unless LOGGING_LEVELS.cover?(coerced_level)
|
23
|
+
fail UnknownLevelError, "Unknown logging level #{level}. Please try one of: #{LOGGING_LEVEL_KEYS.join(', ')}."
|
24
|
+
end
|
25
|
+
coerced_level
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|