logrithm 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +60 -0
- data/.rubocop_todo.yml +47 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +36 -0
- data/Rakefile +24 -0
- data/config/logrithm.yml +41 -0
- data/lib/logrithm/formatters/default.rb +10 -0
- data/lib/logrithm/formatters/pretty.rb +27 -0
- data/lib/logrithm/formatters.rb +4 -0
- data/lib/logrithm/log.rb +148 -0
- data/lib/logrithm/middleware/rack.rb +50 -0
- data/lib/logrithm/spitters/exception.rb +54 -0
- data/lib/logrithm/spitters/string.rb +18 -0
- data/lib/logrithm/spitters.rb +2 -0
- data/lib/logrithm/utils/color.rb +303 -0
- data/lib/logrithm/utils/helpers.rb +25 -0
- data/lib/logrithm/utils/output.rb +70 -0
- data/lib/logrithm/utils/syringe.rb +32 -0
- data/lib/logrithm/version.rb +3 -0
- data/lib/logrithm.rb +57 -0
- data/logrithm.gemspec +34 -0
- metadata +161 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2641dcf0038e8cd1fc2df39d52136413be3c974c
|
4
|
+
data.tar.gz: e41c59225bd7dc8c3145e0fb9ccc784fa9489363
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c45a5f6f871cba9c060c80531fa2cd98f96f430d0239ba3c0abb7630bb91dc471beedbbb3ea40621680d760cf48100cf62c914b10895eefcc58e262c54192008
|
7
|
+
data.tar.gz: e7894f3ae6bbaf81639be0ee99879b534a48543b3e274f9ec2a8c090b59321d9b45f64f7c714920d65037e2a1080554b12eebd3f61f98517710f71615b0d2dba
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
3
|
+
# Offense count: 6
|
4
|
+
# Cop supports --auto-correct.
|
5
|
+
# Configuration parameters: , SupportedStyles.
|
6
|
+
# SupportedStyles: only_raise, only_fail, semantic
|
7
|
+
Style/SignalException:
|
8
|
+
EnforcedStyle: semantic
|
9
|
+
|
10
|
+
Metrics/ClassLength:
|
11
|
+
Max: 200
|
12
|
+
|
13
|
+
Metrics/CyclomaticComplexity:
|
14
|
+
Max: 20
|
15
|
+
|
16
|
+
Metrics/LineLength:
|
17
|
+
Max: 180
|
18
|
+
|
19
|
+
Metrics/MethodLength:
|
20
|
+
Max: 42
|
21
|
+
|
22
|
+
Metrics/PerceivedComplexity:
|
23
|
+
Max: 20
|
24
|
+
|
25
|
+
Metrics/AbcSize:
|
26
|
+
Max: 42
|
27
|
+
|
28
|
+
Style/EmptyCaseCondition:
|
29
|
+
Exclude:
|
30
|
+
- 'lib/**/*'
|
31
|
+
- 'spec/**/*'
|
32
|
+
|
33
|
+
Style/AsciiIdentifiers:
|
34
|
+
Exclude:
|
35
|
+
- 'lib/**/*'
|
36
|
+
- 'spec/**/*'
|
37
|
+
|
38
|
+
Style/AsciiComments:
|
39
|
+
Exclude:
|
40
|
+
- 'lib/**/*'
|
41
|
+
- 'spec/**/*'
|
42
|
+
|
43
|
+
Style/Documentation:
|
44
|
+
Exclude:
|
45
|
+
- 'lib/**/*'
|
46
|
+
- 'spec/**/*'
|
47
|
+
|
48
|
+
Style/RaiseArgs:
|
49
|
+
Exclude:
|
50
|
+
- 'lib/**/*'
|
51
|
+
|
52
|
+
Style/SpecialGlobalVars:
|
53
|
+
Exclude:
|
54
|
+
- 'lib/**/*'
|
55
|
+
- 'spec/**/*'
|
56
|
+
|
57
|
+
Style/MultilineBlockChain:
|
58
|
+
Exclude:
|
59
|
+
- 'lib/**/*'
|
60
|
+
- 'spec/**/*'
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2016-06-09 09:39:16 +0200 using RuboCop version 0.40.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 1
|
10
|
+
# Configuration parameters: MinBodyLength.
|
11
|
+
Style/GuardClause:
|
12
|
+
Exclude:
|
13
|
+
- 'logrithm.gemspec'
|
14
|
+
|
15
|
+
# Offense count: 1
|
16
|
+
# Cop supports --auto-correct.
|
17
|
+
Style/MutableConstant:
|
18
|
+
Exclude:
|
19
|
+
- 'lib/logrithm/version.rb'
|
20
|
+
|
21
|
+
# Offense count: 2
|
22
|
+
# Cop supports --auto-correct.
|
23
|
+
# Configuration parameters: PreferredDelimiters.
|
24
|
+
Style/PercentLiteralDelimiters:
|
25
|
+
Exclude:
|
26
|
+
- 'logrithm.gemspec'
|
27
|
+
|
28
|
+
# Offense count: 1
|
29
|
+
# Cop supports --auto-correct.
|
30
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
31
|
+
# SupportedStyles: only_raise, only_fail, semantic
|
32
|
+
Style/SignalException:
|
33
|
+
Exclude:
|
34
|
+
- 'logrithm.gemspec'
|
35
|
+
|
36
|
+
# Offense count: 17
|
37
|
+
# Cop supports --auto-correct.
|
38
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline.
|
39
|
+
# SupportedStyles: single_quotes, double_quotes
|
40
|
+
Style/StringLiterals:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
# Offense count: 2
|
44
|
+
# Cop supports --auto-correct.
|
45
|
+
Style/UnneededPercentQ:
|
46
|
+
Exclude:
|
47
|
+
- 'logrithm.gemspec'
|
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Aleksei Matiushkin
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Logrithm
|
2
|
+
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
|
6
|
+
Add this line to your application's Gemfile:
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
gem 'logrithm'
|
10
|
+
```
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install logrithm
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
|
23
|
+
## Development
|
24
|
+
|
25
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
26
|
+
|
27
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/logrithm. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
32
|
+
|
33
|
+
|
34
|
+
## License
|
35
|
+
|
36
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
require 'bundler'
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'bundler/gem_tasks'
|
7
|
+
|
8
|
+
require 'rspec/core/rake_task'
|
9
|
+
|
10
|
+
begin
|
11
|
+
Bundler.setup(:default, :development, :test)
|
12
|
+
rescue Bundler::BundlerError => e
|
13
|
+
$stderr.puts e.message
|
14
|
+
$stderr.puts 'Run `bundle install` to install missing gems'
|
15
|
+
exit e.status_code
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Tests'
|
19
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
20
|
+
spec.rspec_opts = '-Ispec'
|
21
|
+
# spec.rcov = true
|
22
|
+
end
|
23
|
+
|
24
|
+
task default: [:spec]
|
data/config/logrithm.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
base:
|
2
|
+
error: 'Kantox::Exceptions::StandardError'
|
3
|
+
stack: 20
|
4
|
+
|
5
|
+
format:
|
6
|
+
id: '__id__'
|
7
|
+
|
8
|
+
log:
|
9
|
+
formatters:
|
10
|
+
production: 'default'
|
11
|
+
development: 'pretty'
|
12
|
+
staging: 'default'
|
13
|
+
test: 'default'
|
14
|
+
standalone: 'pretty'
|
15
|
+
|
16
|
+
nested: 7
|
17
|
+
backtrace:
|
18
|
+
len: 8
|
19
|
+
skip: 3
|
20
|
+
|
21
|
+
colors:
|
22
|
+
info:
|
23
|
+
label: '01;38;05;21'
|
24
|
+
text: '01;38;05;110'
|
25
|
+
highlight: '01;38;05;51'
|
26
|
+
exception: '01;38;05;88'
|
27
|
+
application: '01;38;05;253'
|
28
|
+
method_call: '01;38;05;253'
|
29
|
+
|
30
|
+
symbols:
|
31
|
+
info: '✔'
|
32
|
+
|
33
|
+
stopwords:
|
34
|
+
- '^Message to be suppressed'
|
35
|
+
|
36
|
+
spitters:
|
37
|
+
airbrake:
|
38
|
+
active: true
|
39
|
+
signature: 'Airbrake.notify'
|
40
|
+
sender: 'error_class'
|
41
|
+
message: 'error_message'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Logrithm
|
2
|
+
module Formatters
|
3
|
+
module Pretty
|
4
|
+
def formatter
|
5
|
+
proc do |severity, datetime, _, message|
|
6
|
+
'' <<
|
7
|
+
Log::INSTANCE.send(:lead, severity, datetime) <<
|
8
|
+
parse(message).map do |formatted|
|
9
|
+
Log::INSTANCE.send(:color, severity).last.colorize(formatted)
|
10
|
+
end.join(Logrithm::Log::JOINER) << $/
|
11
|
+
end
|
12
|
+
end
|
13
|
+
module_function :formatter
|
14
|
+
|
15
|
+
def parse(message)
|
16
|
+
return enum_for(:parse, message) unless block_given?
|
17
|
+
[*message].each do |obj|
|
18
|
+
klazz = obj.class.ancestors.inject(nil) do |memo, k|
|
19
|
+
memo || Utils::Helpers.constantize(k, Logrithm::Spitters)
|
20
|
+
end || Utils::Helpers.constantize(:string, Logrithm::Spitters)
|
21
|
+
yield klazz.new(obj).formatted
|
22
|
+
end
|
23
|
+
end
|
24
|
+
module_function :parse
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/logrithm/log.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'io/console'
|
3
|
+
|
4
|
+
# rubocop:disable Style/GlobalVars
|
5
|
+
module Logrithm
|
6
|
+
class Log
|
7
|
+
include Kungfuig
|
8
|
+
|
9
|
+
JOINER = "#{$/} #{option(:log, :joiners, :objects) || '⮩ '}".freeze
|
10
|
+
|
11
|
+
def self.log(message)
|
12
|
+
if $♯
|
13
|
+
puts "Self: [#{$♯}], Message: [#{message}]."
|
14
|
+
else
|
15
|
+
# idx = caller.index { |s| s.include? __FILE__ } to work inside pry
|
16
|
+
Utils::Syringe.inject(*Utils::Helpers.dirty_lookup_class_method(*caller.first[/\A(.+):(\d+)(?=:in)/].split(/:(?=\d+\z)/)))
|
17
|
+
puts "Message: [#{message}]."
|
18
|
+
end
|
19
|
+
rescue
|
20
|
+
puts "Message: [#{message}]."
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(log = nil, **params)
|
24
|
+
[
|
25
|
+
File.join(__dir__, '..', '..', 'config', 'logrithm.yml'),
|
26
|
+
(File.join(Rails.root, 'config', 'logrithm.yml') if Logrithm.rails?)
|
27
|
+
].compact.each do |cfg|
|
28
|
+
kungfuig(cfg) if File.exist?(cfg)
|
29
|
+
end
|
30
|
+
kungfuig(params)
|
31
|
+
ensure_logger(log) if log
|
32
|
+
|
33
|
+
@leader = {}
|
34
|
+
@colors = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def logger
|
38
|
+
ensure_logger
|
39
|
+
end
|
40
|
+
|
41
|
+
def flush
|
42
|
+
logdev.flush
|
43
|
+
end
|
44
|
+
|
45
|
+
def level=(level = nil)
|
46
|
+
logger.level = level || case Logrithm.env
|
47
|
+
when :dev, :development, :test then Logger::DEBUG
|
48
|
+
else Logger::INFO
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
INSTANCE = Log.new
|
53
|
+
private_class_method :new
|
54
|
+
|
55
|
+
%i(debug info warn error fatal).each do |m|
|
56
|
+
define_method(m) do |*args, **extended|
|
57
|
+
logger.public_send m, (args.length == 1 && extended.empty? ? args.first : [*args, **extended])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class << self
|
62
|
+
def option(*name)
|
63
|
+
INSTANCE.option(*name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def app_root
|
67
|
+
File.realpath(INSTANCE.option(:log, :root) || Logrithm.rails? && Rails.root || File.join(__dir__, '..', '..'))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def formatter
|
74
|
+
begin
|
75
|
+
name = option :log, :formatters, Logrithm.env
|
76
|
+
Utils::Helpers.constantize name, Logrithm::Formatters
|
77
|
+
rescue NameError
|
78
|
+
Formatters::Default
|
79
|
+
end.formatter
|
80
|
+
end
|
81
|
+
|
82
|
+
def logdev
|
83
|
+
logger.instance_variable_get(:@logdev).instance_variable_get(:@dev)
|
84
|
+
end
|
85
|
+
|
86
|
+
def ensure_logger(log = nil)
|
87
|
+
return @log if @log
|
88
|
+
|
89
|
+
@log = case
|
90
|
+
when log then log
|
91
|
+
when Logrithm.rails?
|
92
|
+
Kernel.const_get('::Rails')
|
93
|
+
.logger
|
94
|
+
.instance_variable_get(:@logger)
|
95
|
+
.instance_variable_get(:@log)
|
96
|
+
else
|
97
|
+
Logger.new($stderr)
|
98
|
+
end
|
99
|
+
|
100
|
+
@tty = @log.respond_to?(:tty?) && @log.tty? ||
|
101
|
+
(l = logdev) && l.tty? ||
|
102
|
+
Logrithm.rails? && Logrithm.env == :development
|
103
|
+
|
104
|
+
# rubocop:disable Style/ParallelAssignment)
|
105
|
+
@formatter, @log.formatter = @log.formatter, formatter
|
106
|
+
# rubocop:enable Style/ParallelAssignment)
|
107
|
+
|
108
|
+
self.level = nil
|
109
|
+
|
110
|
+
@log
|
111
|
+
end
|
112
|
+
|
113
|
+
def leader(severity)
|
114
|
+
@leader[severity] ||= case Logrithm.severity(severity)
|
115
|
+
when 0 then option(:log, :symbols, :debug) || '✓'
|
116
|
+
when 1 then option(:log, :symbols, :info) || '✔'
|
117
|
+
when 2 then option(:log, :symbols, :warn) || '✗'
|
118
|
+
when 3 then option(:log, :symbols, :error) || '✘'
|
119
|
+
when 4 then option(:log, :symbols, :fatal) || '∅'
|
120
|
+
else option(:log, :symbols, :debug) || '•'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def color(severity)
|
125
|
+
@colors[severity] ||= case Logrithm.severity(severity)
|
126
|
+
when 0 then [option(:log, :colors, :debug, :label) || '#747474', option(:log, :colors, :debug, :text) || '#9C9C9C']
|
127
|
+
when 1 then [option(:log, :colors, :info, :label) || '#FF0000', option(:log, :colors, :info, :text) || '#6699CC']
|
128
|
+
when 2 then [option(:log, :colors, :warn, :label) || '#FFFF00', option(:log, :colors, :warn, :text) || '#FFCC66']
|
129
|
+
when 3 then [option(:log, :colors, :error, :label) || '#AA0000', option(:log, :colors, :error, :text) || '#CC6666']
|
130
|
+
when 4 then [option(:log, :colors, :fatal, :label) || '#FF0000', option(:log, :colors, :fatal, :text) || '#FF0000']
|
131
|
+
else [option(:log, :colors, :debug, :label) || '#747474', option(:log, :colors, :debug, :text) || '#9C9C9C']
|
132
|
+
end.map { |c| Utils::Color.new c }
|
133
|
+
end
|
134
|
+
|
135
|
+
def lead(severity, datetime = safe_now)
|
136
|
+
" " <<
|
137
|
+
color(severity).first.colorize(leader(severity)) <<
|
138
|
+
" #{Utils::Output::VB} " <<
|
139
|
+
color(:debug).first.colorize(datetime.strftime('%Y%m%d-%H%M%S.%3N')) <<
|
140
|
+
" #{Utils::Output::VB} "
|
141
|
+
end
|
142
|
+
|
143
|
+
def safe_now
|
144
|
+
Time.respond_to?(:zone) ? Time.zone.now : Time.now
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
# rubocop:enable Style/GlobalVars
|
@@ -0,0 +1,50 @@
|
|
1
|
+
begin
|
2
|
+
require 'rack/cache'
|
3
|
+
rescue LoadError
|
4
|
+
puts "Couldn't find rack-cache - make sure you have it in your Gemfile:"
|
5
|
+
puts " gem 'rack-cache', require: 'rack/cache'"
|
6
|
+
end
|
7
|
+
|
8
|
+
module Logrithm
|
9
|
+
module Middleware
|
10
|
+
class Rack
|
11
|
+
MAXREADLEN = 2048
|
12
|
+
|
13
|
+
def initialize(app, logger)
|
14
|
+
# rubocop:disable Style/ParallelAssignment
|
15
|
+
@app, @logger = app, logger
|
16
|
+
# rubocop:enable Style/ParallelAssignment
|
17
|
+
@rp, @wp = IO.pipe
|
18
|
+
|
19
|
+
Thread.new do
|
20
|
+
loop do
|
21
|
+
begin
|
22
|
+
✍ @rp.read_nonblock(MAXREADLEN)
|
23
|
+
rescue IO::WaitReadable
|
24
|
+
IO.select([@rp])
|
25
|
+
retry
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def call(env)
|
32
|
+
IO.select(nil, [env['rack.errors'] = @wp])
|
33
|
+
@app.call(env)
|
34
|
+
end
|
35
|
+
|
36
|
+
# rubocop:disable Style/MethodName
|
37
|
+
# rubocop:disable Style/VariableName
|
38
|
+
# rubocop:disable Style/OpMethod
|
39
|
+
def ✍(what)
|
40
|
+
(@✍ ||= Logrithm::INSTANCE).rack what
|
41
|
+
end
|
42
|
+
# rubocop:enable Style/OpMethod
|
43
|
+
# rubocop:enable Style/VariableName
|
44
|
+
# rubocop:enable Style/MethodName
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Rails.application.middleware
|
50
|
+
.insert_before Rack::Cache, Logrithm::Middleware::Rack, :logrithm if Logrithm.rails?
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Logrithm
|
2
|
+
module Spitters
|
3
|
+
class Wrapper < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class Exception
|
7
|
+
BACKTRACE_LENGTH = Logrithm::Log.option(:log, :backtrace, :len) || 8
|
8
|
+
|
9
|
+
def initialize(e)
|
10
|
+
@e = e.is_a?(::Exception) ? e : Wrapper.new(e.inspect)
|
11
|
+
end
|
12
|
+
|
13
|
+
def formatted
|
14
|
+
formatted = format(@e)
|
15
|
+
|
16
|
+
msg = [
|
17
|
+
"Error: #{formatted[:causes].map { |c| "⟨#{c.class}⟩ (“#{c.message}”)" }.join(' ⇐ ')}",
|
18
|
+
formatted[:backtrace],
|
19
|
+
"[#{formatted[:omitted]} more]".rjust(20, '.')
|
20
|
+
].join(joiner)
|
21
|
+
Logrithm::Utils::Output.clrz(msg, Logrithm.color(:error).last)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def joiner
|
27
|
+
"#{$/} #{Logrithm::Log.option(:log, :joiners, :exception) || '⮩ '}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def format(e)
|
31
|
+
bt = e.backtrace.is_a?(Array) ? e.backtrace : caller
|
32
|
+
fbt = format_backtrace(bt)
|
33
|
+
{
|
34
|
+
causes: loop.each_with_object(causes: [], current: e) do |_, memo|
|
35
|
+
memo[:causes] << memo[:current]
|
36
|
+
memo[:current] = memo[:current].cause
|
37
|
+
break memo unless memo[:current]
|
38
|
+
end[:causes],
|
39
|
+
backtrace: fbt,
|
40
|
+
omitted: bt.size - fbt.size
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def format_backtrace(backtrace)
|
45
|
+
backtrace.map.with_index do |bt, idx|
|
46
|
+
if idx < BACKTRACE_LENGTH || bt =~ /^#{Logrithm::Log.app_root}/
|
47
|
+
"[#{idx.to_s.rjust(3, ' ')}] " << \
|
48
|
+
bt.gsub(/^(#{Logrithm::Log.app_root}[^:]*):(\d+):/, "⟦\\1⟧:⟦\\2⟧: ").gsub(/`(.*?)'/, "⟬\\1⟭")
|
49
|
+
end
|
50
|
+
end.compact
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Logrithm
|
2
|
+
module Spitters
|
3
|
+
class String
|
4
|
+
def initialize(obj)
|
5
|
+
@obj = obj
|
6
|
+
end
|
7
|
+
|
8
|
+
def formatted
|
9
|
+
str = case @obj
|
10
|
+
when String then @obj
|
11
|
+
when Symbol, Regexp then @obj.to_s
|
12
|
+
else @obj.inspect
|
13
|
+
end
|
14
|
+
# Logrithm::Utils::Output.clrz(str, Logrithm.color(:error).last)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,303 @@
|
|
1
|
+
module Logrithm
|
2
|
+
module Utils
|
3
|
+
# Dealing with colors
|
4
|
+
class Color
|
5
|
+
# Copyright (c) 2007 McClain Looney
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
# THE SOFTWARE.
|
24
|
+
|
25
|
+
# Implements a color (r,g,b + a) with conversion to/from web format (eg #aabbcc), and
|
26
|
+
# with a number of utilities to lighten, darken and blend values.
|
27
|
+
|
28
|
+
attr_reader :r, :g, :b, :a
|
29
|
+
|
30
|
+
# Table for conversion to hex
|
31
|
+
HEXVAL = ('0'..'9').to_a.concat(('A'..'F').to_a).freeze
|
32
|
+
# Default value for #darken, #lighten etc.
|
33
|
+
BRIGHTNESS_DEFAULT = 0.2
|
34
|
+
CLEAR_TERM = "\e[0m".freeze
|
35
|
+
|
36
|
+
# Constructor. Inits to white (#FFFFFF) by default, or accepts any params
|
37
|
+
# supported by #parse.
|
38
|
+
def initialize(*args)
|
39
|
+
@r = 255
|
40
|
+
@g = 255
|
41
|
+
@b = 255
|
42
|
+
@a = 255
|
43
|
+
|
44
|
+
if args.size.between?(3, 4)
|
45
|
+
self.r = args[0]
|
46
|
+
self.g = args[1]
|
47
|
+
self.b = args[2]
|
48
|
+
self.a = args[3] if args[3]
|
49
|
+
else
|
50
|
+
set(*args)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# All-purpose setter - pass in another Color, '#000000', rgb vals... whatever
|
55
|
+
def set(*args)
|
56
|
+
val = Color.parse(*args)
|
57
|
+
unless val.nil?
|
58
|
+
self.r = val.r
|
59
|
+
self.g = val.g
|
60
|
+
self.b = val.b
|
61
|
+
self.a = val.a
|
62
|
+
end
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
# Test for equality, accepts string vals as well, eg Color.new('aaa') == '#AAAAAA' => true
|
67
|
+
def ==(other)
|
68
|
+
val = Color.parse(other)
|
69
|
+
return false if val.nil?
|
70
|
+
r == val.r && g == val.g && b == val.b && a == val.a
|
71
|
+
end
|
72
|
+
|
73
|
+
# Setters for individual channels - take 0-255 or '00'-'FF' values
|
74
|
+
%i(r g b a).each do |m|
|
75
|
+
define_method "#{m}=" do |val|
|
76
|
+
instance_variable_set("@#{m}", from_hex(val))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Attempt to read in a string and parse it into values
|
81
|
+
def self.parse(*args)
|
82
|
+
case args.size
|
83
|
+
when 0 then return nil
|
84
|
+
when 1
|
85
|
+
case val = args.first
|
86
|
+
when Color then val
|
87
|
+
when Fixnum then Color.new(val, val, val) # Single value, assume grayscale
|
88
|
+
when /\A(\d{,3};){1,3}\d{,3}\z/ # console
|
89
|
+
# FIXME: implememnt this
|
90
|
+
# 16 + 36 * r + 6 * g + b
|
91
|
+
case cl = val.split(';').last.to_i
|
92
|
+
when 0..15 then '#000000' # FIXME: make it working
|
93
|
+
when 232..255
|
94
|
+
Color.new(*([cl - 232] * 3).map { |c| c * 10 + 16 })
|
95
|
+
else # FIXME: the line below fails for 231, it becomes 255
|
96
|
+
r, gb = (cl - 16).divmod(36)
|
97
|
+
g, b = gb.divmod(6)
|
98
|
+
Color.new(*[r, g, b].map { |e| (51.0 * e).floor })
|
99
|
+
end
|
100
|
+
when String # assume rgb and try to guess
|
101
|
+
str = val.to_s.upcase[/[0-9A-F]{3,8}/] || ''
|
102
|
+
Color.new(*case str.length
|
103
|
+
when 3, 4 then str.scan(/[0-9A-F]/).map { |d| d * 2 }
|
104
|
+
when 6, 8 then str.scan(/[0-9A-F]{2}/)
|
105
|
+
else 'FF'
|
106
|
+
end.map { |c| Integer("0x#{c}") })
|
107
|
+
end
|
108
|
+
when 2 # assume gray + alpha
|
109
|
+
val, alpha = args
|
110
|
+
Color.new(val, val, val, alpha)
|
111
|
+
when 3, 4 then Color.new(*args)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
# rubocop:enable Metrics/MethodLength
|
115
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
116
|
+
|
117
|
+
def inspect
|
118
|
+
id = format('%x', object_id << 1)
|
119
|
+
"#<#{self.class.name}:0x#{id.rjust(14, '0')} 💻=“#{to_esc(true)}███#{CLEAR_TERM}” ✎=“\\e[#{to_esc(false)}m” 🗔=“#{to_html}”>"
|
120
|
+
end
|
121
|
+
|
122
|
+
# FIXME: what we really want here?
|
123
|
+
def to_s(as_esc = true)
|
124
|
+
to_esc(as_esc)
|
125
|
+
end
|
126
|
+
|
127
|
+
def colorize(*str, concatenator: '')
|
128
|
+
"#{to_esc(true)}#{str.map(&:to_s).join(concatenator)}#{CLEAR_TERM}"
|
129
|
+
end
|
130
|
+
|
131
|
+
# rubocop:disable Metrics/ParameterLists
|
132
|
+
# Color as used in 256-color terminal escape sequences
|
133
|
+
def to_esc(surround = true, bold: true, italic: false, underline: false, reverse: false, foreground: true)
|
134
|
+
result = if grayscale?
|
135
|
+
r < 16 ? 0 : ((r - 16) / 10).floor + 232
|
136
|
+
else
|
137
|
+
16 + 36 * (r / 51).floor + 6 * (g / 51).floor + (b / 51).floor
|
138
|
+
end
|
139
|
+
|
140
|
+
esc = [
|
141
|
+
bold ? '01' : nil,
|
142
|
+
italic ? '03' : nil,
|
143
|
+
underline ? '04' : nil,
|
144
|
+
reverse ? '07' : nil,
|
145
|
+
foreground ? '38' : '48',
|
146
|
+
'05',
|
147
|
+
result
|
148
|
+
].compact.join(';')
|
149
|
+
|
150
|
+
surround ? "\e[#{esc}m" : esc
|
151
|
+
end
|
152
|
+
# rubocop:enable Metrics/ParameterLists
|
153
|
+
|
154
|
+
def to_rgb(add_hash = true)
|
155
|
+
(add_hash ? '#' : '') + to_hex(r) + to_hex(g) + to_hex(b)
|
156
|
+
end
|
157
|
+
|
158
|
+
def to_rgba(add_hash = true)
|
159
|
+
to_rgb(add_hash) + to_hex(a)
|
160
|
+
end
|
161
|
+
|
162
|
+
def opaque?
|
163
|
+
@a == 255
|
164
|
+
end
|
165
|
+
|
166
|
+
def trans?
|
167
|
+
!opaque?
|
168
|
+
end
|
169
|
+
|
170
|
+
def grayscale?
|
171
|
+
@r == @g && @g == @b
|
172
|
+
end
|
173
|
+
|
174
|
+
# Lighten color towards white. 0.0 is a no-op, 1.0 will return #FFFFFF
|
175
|
+
def lighten(amt = BRIGHTNESS_DEFAULT)
|
176
|
+
return self if amt <= 0
|
177
|
+
return WHITE if amt >= 1.0
|
178
|
+
Color.new(self).tap do |val|
|
179
|
+
val.r += ((255 - val.r) * amt).to_i
|
180
|
+
val.g += ((255 - val.g) * amt).to_i
|
181
|
+
val.b += ((255 - val.b) * amt).to_i
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# In place version of #lighten
|
186
|
+
def lighten!(amt = BRIGHTNESS_DEFAULT)
|
187
|
+
set(lighten(amt))
|
188
|
+
end
|
189
|
+
|
190
|
+
# Darken a color towards full black. 0.0 is a no-op, 1.0 will return #000000
|
191
|
+
def darken(amt = BRIGHTNESS_DEFAULT)
|
192
|
+
return self if amt <= 0
|
193
|
+
return BLACK if amt >= 1.0
|
194
|
+
Color.new(self).tap do |val|
|
195
|
+
val.r -= (val.r * amt).to_i
|
196
|
+
val.g -= (val.g * amt).to_i
|
197
|
+
val.b -= (val.b * amt).to_i
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# In place version of #darken
|
202
|
+
def darken!(amt = BRIGHTNESS_DEFAULT)
|
203
|
+
set(darken(amt))
|
204
|
+
end
|
205
|
+
|
206
|
+
# Convert to grayscale, using perception-based weighting
|
207
|
+
def grayscale
|
208
|
+
Color.new(self).tap do |val|
|
209
|
+
val.r = val.g = val.b = (0.2126 * val.r + 0.7152 * val.g + 0.0722 * val.b)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# In place version of #grayscale
|
214
|
+
def grayscale!
|
215
|
+
set(grayscale)
|
216
|
+
end
|
217
|
+
|
218
|
+
# Blend to a color amt % towards another color value, eg
|
219
|
+
# red.blend(blue, 0.5) will be purple, white.blend(black, 0.5) will be gray, etc.
|
220
|
+
def blend(other, amt)
|
221
|
+
other = Color.parse(other)
|
222
|
+
return Color.new(self) if amt <= 0 || other.nil?
|
223
|
+
return Color.new(other) if amt >= 1.0
|
224
|
+
Color.new(self).tap do |val|
|
225
|
+
val.r += ((other.r - val.r) * amt).to_i
|
226
|
+
val.g += ((other.g - val.g) * amt).to_i
|
227
|
+
val.b += ((other.b - val.b) * amt).to_i
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# In place version of #blend
|
232
|
+
def blend!(other, amt)
|
233
|
+
set(blend(other, amt))
|
234
|
+
self
|
235
|
+
end
|
236
|
+
|
237
|
+
# Class-level version for explicit blends of two values, useful with constants
|
238
|
+
def self.blend(col1, col2, amt)
|
239
|
+
col1, col2 = [col1, col2].map { |c| Color.parse c }
|
240
|
+
col1.blend(col2, amt)
|
241
|
+
end
|
242
|
+
|
243
|
+
# rubocop:disable Metrics/ParameterLists
|
244
|
+
def self.to_xterm256(text, color, bold: true, italic: false, underline: false, reverse: false, foreground: true)
|
245
|
+
color = Color.preset(color) unless color.is_a?(Color)
|
246
|
+
[
|
247
|
+
color.to_esc(true, bold: bold, italic: italic, underline: underline, reverse: reverse, foreground: foreground),
|
248
|
+
text,
|
249
|
+
CLEAR_TERM
|
250
|
+
].join
|
251
|
+
end
|
252
|
+
# rubocop:enable Metrics/ParameterLists
|
253
|
+
|
254
|
+
def self.preset(type)
|
255
|
+
Color.parse case type
|
256
|
+
when :label then '#999999'
|
257
|
+
when :success then '#468847'
|
258
|
+
when :warning then '#F89406'
|
259
|
+
when :important then '#B94A48'
|
260
|
+
when :fatal then '#B94A48'
|
261
|
+
when :error then '#FF0000'
|
262
|
+
when :info then '#3A87AD'
|
263
|
+
when :inverse then '#333333'
|
264
|
+
else type
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
protected
|
269
|
+
|
270
|
+
def to_html(add_hash = true)
|
271
|
+
trans? ? to_rgba(add_hash) : to_rgb(add_hash)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Convert int to string hex, eg 255 => 'FF'
|
275
|
+
def to_hex(val)
|
276
|
+
HEXVAL[val / 16] + HEXVAL[val % 16]
|
277
|
+
end
|
278
|
+
|
279
|
+
# Convert int or string to int, eg 80 => 80, 'FF' => 255, '7' => 119
|
280
|
+
def from_hex(val)
|
281
|
+
if val.is_a?(String)
|
282
|
+
# Double up if single char form
|
283
|
+
val *= 2 if val.size == 1
|
284
|
+
# Convert to integer
|
285
|
+
val = val.hex
|
286
|
+
end
|
287
|
+
# Clamp
|
288
|
+
val = 0 if val < 0
|
289
|
+
val = 255 if val > 255
|
290
|
+
val
|
291
|
+
end
|
292
|
+
|
293
|
+
# Some constants for general use
|
294
|
+
WHITE = Color.new(255, 255, 255).freeze
|
295
|
+
BLACK = Color.new(0, 0, 0).freeze
|
296
|
+
RED = Color.new(255, 0, 0).freeze
|
297
|
+
GREEN = Color.new(0, 255, 0).freeze
|
298
|
+
BLUE = Color.new(0, 0, 255).freeze
|
299
|
+
NONE = Color.new(0, 0, 0, 255).freeze
|
300
|
+
end
|
301
|
+
# rubocop:enable Metrics/ClassLength
|
302
|
+
end
|
303
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Logrithm
|
2
|
+
module Utils
|
3
|
+
module Helpers
|
4
|
+
class << self
|
5
|
+
def constantize(string, namespace = Kernel)
|
6
|
+
namespace = Kernel.const_get(namespace) unless namespace.is_a?(Module)
|
7
|
+
result = namespace.const_get string.to_s.gsub(/(?:\A|_)(\w)/) { |m| m[-1].upcase }
|
8
|
+
result if namespace == Kernel || result.to_s =~ /\A#{namespace}/
|
9
|
+
rescue NameError
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def dirty_lookup_class_method(file, lineno)
|
14
|
+
content = File.readlines(file)[0..lineno.to_i].reverse
|
15
|
+
result = content.each_with_object(method: nil, ns: []) do |line, memo|
|
16
|
+
(memo[:method] ||= line[/\A\s*def\s*(?<method>\w+)/, :method]) &&
|
17
|
+
(memo[:ns] << line[/\A\s*(?:class|module)\s*(?<ns>\w+)/, :ns])
|
18
|
+
end
|
19
|
+
|
20
|
+
[const_get(result[:ns].compact.reverse.join('::')), result[:method]]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Logrithm
|
2
|
+
module Utils
|
3
|
+
module Output
|
4
|
+
VB = '│'.freeze
|
5
|
+
HB = '─'.freeze
|
6
|
+
TL = '┌'.freeze
|
7
|
+
TR = '┐'.freeze
|
8
|
+
BL = '└'.freeze
|
9
|
+
BR = '┘'.freeze
|
10
|
+
|
11
|
+
TERM_MARGIN = 2
|
12
|
+
BOX_MARGIN = 2
|
13
|
+
|
14
|
+
TERM_MARGIN_STR = ' ' * TERM_MARGIN
|
15
|
+
BOX_MARGIN_STR = ' ' * BOX_MARGIN
|
16
|
+
|
17
|
+
HIGHLIGHT_COLOR = Color.new(Logrithm::Log.option(:log, :colors, :highlight) || '01;38;05;51')
|
18
|
+
EXCEPTION_COLOR = Color.new(Logrithm::Log.option(:log, :colors, :exception) || '01;38;05;88')
|
19
|
+
APPDIR_COLOR = Color.new(Logrithm::Log.option(:log, :colors, :root) || '01;38;05;253')
|
20
|
+
METHOD_COLOR = Color.new(Logrithm::Log.option(:log, :colors, :method_call) || '01;38;05;253')
|
21
|
+
DATETIME_COLOR = Color.new(Logrithm::Log.option(:log, :colors, :datetime) || '01;38;05;240')
|
22
|
+
EXTENDED_COLOR = Color.new(Logrithm::Log.option(:log, :colors, :extended) || '01;38;05;246')
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def line(filler = HB, width: $stdin.winsize.last, margin: TERM_MARGIN, color: Color::RED)
|
26
|
+
color.colorize(' ' * margin, filler * (width - 2 * margin))
|
27
|
+
end
|
28
|
+
|
29
|
+
# rubocop:disable Metrics/AbcSize
|
30
|
+
def rectangle(text, width: $stdin.winsize.last - 2 * TERM_MARGIN, color: Color::RED, frame_color: :same)
|
31
|
+
text_width = width - 2 - 2 * BOX_MARGIN
|
32
|
+
splitted = text.split(' ').each_with_object(['']) do |word, memo|
|
33
|
+
next if word.strip.empty?
|
34
|
+
memo.last.length + word.length + 1 <= text_width ? memo.last << ' ' << word : memo << word
|
35
|
+
end.map(&:strip)
|
36
|
+
frame_color = case frame_color
|
37
|
+
when :same then color
|
38
|
+
when :none then Color::NONE
|
39
|
+
else frame_color
|
40
|
+
end
|
41
|
+
spaces = ' ' * (BOX_MARGIN + (text_width - splitted.max_by(&:length).length) / 2)
|
42
|
+
[
|
43
|
+
frame_color.colorize(TERM_MARGIN_STR + TL << HB * (width - 2) << TR),
|
44
|
+
frame_color.colorize("#{TERM_MARGIN_STR}#{VB}#{' ' * (width - 2)}#{VB}"),
|
45
|
+
*splitted.map do |line|
|
46
|
+
trailing_spaces = ' ' * (width - (2 + spaces.length + line.length))
|
47
|
+
frame_color.colorize("#{TERM_MARGIN_STR}#{VB}") <<
|
48
|
+
color.colorize("#{spaces}#{line}#{trailing_spaces}") <<
|
49
|
+
frame_color.colorize(VB)
|
50
|
+
end,
|
51
|
+
frame_color.colorize("#{TERM_MARGIN_STR}#{VB}#{' ' * (width - 2)}#{VB}"),
|
52
|
+
frame_color.colorize(TERM_MARGIN_STR + BL << HB * (width - 2) << BR)
|
53
|
+
].join($/)
|
54
|
+
end
|
55
|
+
# rubocop:enable Metrics/AbcSize
|
56
|
+
|
57
|
+
def clrz(txt, clr)
|
58
|
+
txt = txt.to_s
|
59
|
+
.gsub(/«(.*?)»/m, "#{HIGHLIGHT_COLOR.to_esc}\\1#{clr.to_esc}")
|
60
|
+
.gsub(/⟨(.*?)⟩/m, "#{EXCEPTION_COLOR.to_esc}\\1#{clr.to_esc}")
|
61
|
+
.gsub(/⟦(.*?)⟧/m, "#{APPDIR_COLOR.to_esc}\\1#{clr.to_esc}")
|
62
|
+
.gsub(/⟬(.*?)⟭/m, "#{METHOD_COLOR.to_esc}\\1#{clr.to_esc}")
|
63
|
+
.gsub(/⟪(.*?)⟫/m, "#{EXTENDED_COLOR.to_esc}\\1#{clr.to_esc}")
|
64
|
+
|
65
|
+
clr.colorize txt
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# rubocop:disable Style/GlobalVars
|
2
|
+
module Logrithm
|
3
|
+
module Utils
|
4
|
+
module Syringe
|
5
|
+
class << self
|
6
|
+
def inject(*km)
|
7
|
+
return unless km.size == 2 && km.first.is_a?(Class) && km.last.is_a?(String)
|
8
|
+
klazz, method = km
|
9
|
+
|
10
|
+
name = scramble "Logrithm@#{klazz}##{method}"
|
11
|
+
return if klazz.ancestors.map(&:name).include? "Kernel::#{name}"
|
12
|
+
|
13
|
+
Kernel.const_set(
|
14
|
+
name,
|
15
|
+
Module.new do
|
16
|
+
define_method method do |*args|
|
17
|
+
$♯ = self
|
18
|
+
super(*args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
)
|
22
|
+
klazz.prepend Kernel.const_get name
|
23
|
+
end
|
24
|
+
|
25
|
+
def scramble(str)
|
26
|
+
str.gsub(/[:#@?!]/, ':' => ':', '#' => '#', '@' => '@', '?' => '?', '!' => '!', '=' => '=')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
# rubocop:enable Style/GlobalVars
|
data/lib/logrithm.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'kungfuig'
|
2
|
+
|
3
|
+
require 'logrithm/version'
|
4
|
+
require 'logrithm/utils/color'
|
5
|
+
require 'logrithm/utils/helpers'
|
6
|
+
|
7
|
+
require 'logrithm/formatters'
|
8
|
+
|
9
|
+
require 'logrithm/utils/syringe'
|
10
|
+
|
11
|
+
module Logrithm
|
12
|
+
def rails?
|
13
|
+
Kernel.const_defined?('::Rails')
|
14
|
+
end
|
15
|
+
module_function :rails?
|
16
|
+
|
17
|
+
def env
|
18
|
+
case
|
19
|
+
when ENV['LOGRITHM_ENV'] then ENV['LOGRITHM_ENV'].to_sym
|
20
|
+
when rails? then Rails.env
|
21
|
+
else :development
|
22
|
+
end
|
23
|
+
end
|
24
|
+
module_function :env
|
25
|
+
|
26
|
+
def severity(severity)
|
27
|
+
case severity.to_s.upcase
|
28
|
+
when 'DEBUG', '0' then 0
|
29
|
+
when 'INFO', '1' then 1
|
30
|
+
when 'WARN', '2' then 2
|
31
|
+
when 'ERROR', '3' then 3
|
32
|
+
when 'FATAL', '4' then 4
|
33
|
+
else 2
|
34
|
+
end
|
35
|
+
end
|
36
|
+
module_function :severity
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'logrithm/log'
|
40
|
+
require 'logrithm/utils/output'
|
41
|
+
require 'logrithm/spitters'
|
42
|
+
|
43
|
+
require 'logrithm/middleware/rack'
|
44
|
+
|
45
|
+
module Logrithm
|
46
|
+
class << self
|
47
|
+
%i(debug info warn error fatal).each do |m|
|
48
|
+
define_method(m) do |*args, **extended|
|
49
|
+
Logrithm::Log::INSTANCE.public_send m, *args, **extended
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def color(severity)
|
54
|
+
Log::INSTANCE.send(:color, severity)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/logrithm.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'logrithm/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'logrithm'
|
8
|
+
spec.version = Logrithm::VERSION
|
9
|
+
spec.authors = ['Aleksei Matiushkin']
|
10
|
+
spec.email = ['aleksei.matiushkin@kantox.com']
|
11
|
+
|
12
|
+
spec.summary = %q{Standard logger improvements}
|
13
|
+
spec.description = %q{Some improvements to standard logger, such as detailed colored logger messages etc.}
|
14
|
+
spec.homepage = 'https://kantox.com'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' unless spec.respond_to?(:metadata)
|
18
|
+
|
19
|
+
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
20
|
+
|
21
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
|
+
spec.bindir = 'bin'
|
23
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ['lib']
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.10'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
spec.add_development_dependency 'rspec'
|
29
|
+
|
30
|
+
spec.add_development_dependency 'pry'
|
31
|
+
spec.add_development_dependency 'awesome_print'
|
32
|
+
|
33
|
+
spec.add_dependency 'kungfuig', '~> 0.7', '>= 0.7.5'
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: logrithm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aleksei Matiushkin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: awesome_print
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: kungfuig
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.7'
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 0.7.5
|
93
|
+
type: :runtime
|
94
|
+
prerelease: false
|
95
|
+
version_requirements: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0.7'
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 0.7.5
|
103
|
+
description: Some improvements to standard logger, such as detailed colored logger
|
104
|
+
messages etc.
|
105
|
+
email:
|
106
|
+
- aleksei.matiushkin@kantox.com
|
107
|
+
executables: []
|
108
|
+
extensions: []
|
109
|
+
extra_rdoc_files: []
|
110
|
+
files:
|
111
|
+
- ".gitignore"
|
112
|
+
- ".rspec"
|
113
|
+
- ".rubocop.yml"
|
114
|
+
- ".rubocop_todo.yml"
|
115
|
+
- ".travis.yml"
|
116
|
+
- CODE_OF_CONDUCT.md
|
117
|
+
- Gemfile
|
118
|
+
- LICENSE.txt
|
119
|
+
- README.md
|
120
|
+
- Rakefile
|
121
|
+
- config/logrithm.yml
|
122
|
+
- lib/logrithm.rb
|
123
|
+
- lib/logrithm/formatters.rb
|
124
|
+
- lib/logrithm/formatters/default.rb
|
125
|
+
- lib/logrithm/formatters/pretty.rb
|
126
|
+
- lib/logrithm/log.rb
|
127
|
+
- lib/logrithm/middleware/rack.rb
|
128
|
+
- lib/logrithm/spitters.rb
|
129
|
+
- lib/logrithm/spitters/exception.rb
|
130
|
+
- lib/logrithm/spitters/string.rb
|
131
|
+
- lib/logrithm/utils/color.rb
|
132
|
+
- lib/logrithm/utils/helpers.rb
|
133
|
+
- lib/logrithm/utils/output.rb
|
134
|
+
- lib/logrithm/utils/syringe.rb
|
135
|
+
- lib/logrithm/version.rb
|
136
|
+
- logrithm.gemspec
|
137
|
+
homepage: https://kantox.com
|
138
|
+
licenses:
|
139
|
+
- MIT
|
140
|
+
metadata: {}
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options: []
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
requirements: []
|
156
|
+
rubyforge_project:
|
157
|
+
rubygems_version: 2.4.8
|
158
|
+
signing_key:
|
159
|
+
specification_version: 4
|
160
|
+
summary: Standard logger improvements
|
161
|
+
test_files: []
|