tracee 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +3 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +36 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/tracee/benchmarkable.rb +113 -0
- data/lib/tracee/engine.rb +18 -0
- data/lib/tracee/ext/active_support.rb +51 -0
- data/lib/tracee/ext/better_errors.rb +29 -0
- data/lib/tracee/ext/exception.rb +68 -0
- data/lib/tracee/logger.rb +165 -0
- data/lib/tracee/preprocessors/base.rb +15 -0
- data/lib/tracee/preprocessors/formatter.rb +135 -0
- data/lib/tracee/preprocessors/quiet_assets.rb +25 -0
- data/lib/tracee/stack/base_decorator.rb +42 -0
- data/lib/tracee/stack.rb +44 -0
- data/lib/tracee/stream.rb +100 -0
- data/lib/tracee/version.rb +3 -0
- data/lib/tracee.rb +95 -0
- data/tracee.gemspec +26 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 35e0c1ca049152c29c82fc41fb10eb9c5b5e4ae3
|
4
|
+
data.tar.gz: 6a5655c070b8a7d3a1702ce2ac60f38db4eb1912
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 89ebe61689eb24b7d43b45e7c3ad00617940f117dffda032a2a1417ce8d9cdb4dae08c4569a17a8f2a08239fa86f78df7db27b0616d50ddfbf110c5825a9da13
|
7
|
+
data.tar.gz: 7797753852f37ef9141a7bd1edde46d7ac126241063634cf806c911e92a1132ba784360e5e3f85ccfef0a46efc697b70f62c8cc5708b69584810b8ae6bd8f32b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Sergey Baev, https://github.com/tinbka
|
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,36 @@
|
|
1
|
+
# Tracee
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/tracee`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'tracee'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install tracee
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake false` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
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).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/tracee.
|
36
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tracee"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
module Tracee
|
2
|
+
# Suppose, we have a code that works too slow, still it runs for 50ms.
|
3
|
+
# You measure time in 5 points between calls, and it gives you the first time:
|
4
|
+
# 0.025997, 0.012339, 0.037864, 0.0415
|
5
|
+
# the next time:
|
6
|
+
# 0.01658, 0.011366, 0.046607, 0.052117
|
7
|
+
# the third time:
|
8
|
+
# 0.016733, 0.011295, 0.032805, 0.036667
|
9
|
+
# How long has each block of code been executed _actualy_?
|
10
|
+
# The code runtime may fluctuate slightly because a PC does some work beside your benchmarking.
|
11
|
+
# The less the code runtime, the more relative fluctuations are. Thats why we do enough passes to minify them.
|
12
|
+
#
|
13
|
+
# This module allows to not only measure time between arbitrary calls (ticks),
|
14
|
+
# and not only get an average from multiple repeats of a block,
|
15
|
+
# but to get a list of averages between each arbitrary call (tick) in a block.
|
16
|
+
#
|
17
|
+
# Here's a sample:
|
18
|
+
#
|
19
|
+
#> $log.benchmark(times:20) {Ability.new(u)}
|
20
|
+
# 23:29:59.021 INFO [ability.rb:76 :assistant_permissions]: [tick +0.576797]
|
21
|
+
# 23:29:59.034 INFO [ability.rb:84 :assistant_permissions]: [tick +0.245685]
|
22
|
+
# 23:29:59.075 INFO [ability.rb:93 :assistant_permissions]: [tick +0.728214]
|
23
|
+
# 23:29:59.120 INFO [ability.rb:111 :assistant_permissions]: [tick +0.866646]
|
24
|
+
# 23:29:59.120 INFO [(irb):8 :irb_binding]: [tick +0.000559] [120.978946ms each; 2419.578914ms total] #<Ability:0x000000088c89c8>
|
25
|
+
module Benchmarkable
|
26
|
+
|
27
|
+
def benchmark(times: 1, &block)
|
28
|
+
@benchmark = Benchmark.new
|
29
|
+
before_proc = Time.now
|
30
|
+
|
31
|
+
(times - 1).times {yield}
|
32
|
+
@benchmark.last_pass!
|
33
|
+
result = yield
|
34
|
+
|
35
|
+
now = Time.now
|
36
|
+
@benchmark = nil
|
37
|
+
|
38
|
+
diff_ms = (now - before_proc)*1000
|
39
|
+
milliseconds_each = highlight_time_diff(diff_ms/times)
|
40
|
+
milliseconds_total = highlight_time_diff(diff_ms)
|
41
|
+
info "[#{milliseconds_each}ms each; #{milliseconds_total}ms total] #{result}", caller_at: 1
|
42
|
+
end
|
43
|
+
|
44
|
+
def tick(msg='', caller_offset: 0)
|
45
|
+
now = Time.now
|
46
|
+
|
47
|
+
if @benchmark
|
48
|
+
if prev = Thread.current[:tracee_checkpoint]
|
49
|
+
tick_diff = @benchmark.add_time(now - prev)
|
50
|
+
if @benchmark.last_pass
|
51
|
+
info "[tick +#{highlight_time_diff(tick_diff)}] #{msg}", caller_at: caller_offset+1
|
52
|
+
end
|
53
|
+
@benchmark.next
|
54
|
+
# else we just write `now' to a thread var
|
55
|
+
end
|
56
|
+
else
|
57
|
+
if prev = Thread.current[:tracee_checkpoint]
|
58
|
+
info "[tick +#{highlight_time_diff(now - prev)}] #{msg}", caller_at: caller_offset+1
|
59
|
+
else
|
60
|
+
info "[tick] #{msg}", caller_at: caller_offset+1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Thread.current[:tracee_checkpoint] = now
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def tick!(msg='', caller_offset: 0)
|
69
|
+
@benchmark.first! if @benchmark
|
70
|
+
Thread.current[:tracee_checkpoint] = nil
|
71
|
+
|
72
|
+
tick msg, caller_offset: caller_offset+1
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def highlight_time_diff(diff)
|
79
|
+
diff.round(6).to_s.sub(/(\d+)\.(\d{0,3})(\d*)$/) {|m| "#$1.".light_white + $2.white + $3.light_black}
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
class Benchmark
|
86
|
+
attr_reader :ticks_diffs, :last_pass, :tick_number
|
87
|
+
|
88
|
+
def initialize
|
89
|
+
@ticks_diffs = Hash.new {|h, k| h[k] = 0}
|
90
|
+
end
|
91
|
+
|
92
|
+
def last_pass!
|
93
|
+
@last_pass = true
|
94
|
+
end
|
95
|
+
|
96
|
+
def first!
|
97
|
+
@tick_number = 0
|
98
|
+
end
|
99
|
+
|
100
|
+
def next
|
101
|
+
@tick_number += 1
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_time(amount)
|
105
|
+
@ticks_diffs[@tick_number] += amount
|
106
|
+
end
|
107
|
+
|
108
|
+
def tick_diff
|
109
|
+
@ticks_diffs[@tick_number]
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
begin
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
module Tracee
|
5
|
+
class Engine < Rails::Engine
|
6
|
+
|
7
|
+
initializer "tracee.decorate_stack" do |app|
|
8
|
+
if defined? ::BetterErrors
|
9
|
+
Tracee.decorate_better_errors_stack
|
10
|
+
else
|
11
|
+
Tracee.decorate_active_support_stack
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
rescue LoadError
|
18
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Tracee
|
2
|
+
module Extensions
|
3
|
+
module ActiveSupport
|
4
|
+
|
5
|
+
module BacktraceCleaner
|
6
|
+
extend ::ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
if defined? Rails
|
10
|
+
Rails.backtrace_cleaner.add_silencer {|line| line =~ IGNORE_RE}
|
11
|
+
end
|
12
|
+
alias_method_chain :clean, :decorate
|
13
|
+
end
|
14
|
+
|
15
|
+
def clean_with_decorate(backtrace, kind=:silent)
|
16
|
+
Stack::BaseDecorator.(clean_without_decorate(backtrace, kind))
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
module TaggedLogging
|
22
|
+
module Formatter
|
23
|
+
extend ::ActiveSupport::Concern
|
24
|
+
|
25
|
+
# Well, metaprogamming is a game you can play together!
|
26
|
+
# We do it so that Tracee::Logger would not have to care about an arity of corresponding #call or whatever.
|
27
|
+
included do
|
28
|
+
def self.extended(a_formatter_to_apply_tagging_on)
|
29
|
+
if a_formatter_to_apply_tagging_on.is_a? Tracee::Preprocessors::Base
|
30
|
+
a_formatter_to_apply_tagging_on.instance_variable_set\
|
31
|
+
:@original_call,
|
32
|
+
a_formatter_to_apply_tagging_on.class
|
33
|
+
.instance_method(:call)
|
34
|
+
.bind(a_formatter_to_apply_tagging_on)
|
35
|
+
|
36
|
+
a_formatter_to_apply_tagging_on.class_eval do
|
37
|
+
def call(severity, timestamp, progname, msg, caller_slice=[])
|
38
|
+
@original_call.(severity, timestamp, progname, "#{tags_text}#{msg}", caller_slice)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Tracee
|
2
|
+
module Extensions
|
3
|
+
module BetterErrors
|
4
|
+
module Middleware
|
5
|
+
extend ::ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
alias_method_chain :log_exception, :decorate
|
9
|
+
end
|
10
|
+
|
11
|
+
def log_exception_with_decorate
|
12
|
+
return unless ::BetterErrors.logger
|
13
|
+
|
14
|
+
message = "\n#{@error_page.exception.class} - #{@error_page.exception.message}:\n"
|
15
|
+
|
16
|
+
frames = @error_page.backtrace_frames # original definition
|
17
|
+
frames = frames.map(&:to_s).reject {|line| line =~ IGNORE_RE}
|
18
|
+
frames = Stack::BaseDecorator.(frames)
|
19
|
+
frames.each do |frame|
|
20
|
+
message << " #{frame}\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
::BetterErrors.logger.fatal message
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Tracee
|
2
|
+
module Extensions
|
3
|
+
module Exception
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
alias_method_chain :set_backtrace, :decorate
|
8
|
+
class_attribute :trace_decorator
|
9
|
+
end
|
10
|
+
|
11
|
+
## Gotcha:
|
12
|
+
# If you also set (e.g. in irbrc file)
|
13
|
+
#
|
14
|
+
# module Readline
|
15
|
+
# alias :orig_readline :readline
|
16
|
+
# def readline(*args)
|
17
|
+
# ln = orig_readline(*args)
|
18
|
+
# SCRIPT_LINES__['(irb)'] << "#{ln}\n"
|
19
|
+
# ln
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# it will be possible to fetch lines entered in IRB
|
24
|
+
# else format_trace would only read ordinary require'd files
|
25
|
+
##
|
26
|
+
if RUBY_VERSION > '2.1'
|
27
|
+
|
28
|
+
def set_backtrace_with_decorate(trace)
|
29
|
+
if decorator = self.class.trace_decorator
|
30
|
+
if trace.is_a? Thread::Backtrace
|
31
|
+
return trace
|
32
|
+
else
|
33
|
+
trace = decorator.(trace)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
set_backtrace_without_decorate(trace)
|
37
|
+
end
|
38
|
+
|
39
|
+
else
|
40
|
+
|
41
|
+
def set_backtrace(trace)
|
42
|
+
if decorator = self.class.trace_decorator
|
43
|
+
trace = decorator.(trace)
|
44
|
+
end
|
45
|
+
set_backtrace_without_decorate(trace)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
## Use case:
|
51
|
+
# We have some method that we don't want to crash application in production but want to have this crash potential been logged
|
52
|
+
#
|
53
|
+
# def unsured_method
|
54
|
+
# ... some crashable calls ...
|
55
|
+
# rescue PotentialException
|
56
|
+
# $!.log
|
57
|
+
# end
|
58
|
+
##
|
59
|
+
def log
|
60
|
+
Rails.logger.error [
|
61
|
+
"The exception has been handled: #{self.class} — #{message.force_encoding('UTF-8')}:",
|
62
|
+
*Rails.backtrace_cleaner.clean(backtrace)
|
63
|
+
]*"\n"
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module Tracee
|
2
|
+
|
3
|
+
class Logger
|
4
|
+
LEVELS = [
|
5
|
+
'DEBUG', # Low-level information for developers
|
6
|
+
'INFO', # Generic (useful) information about system operation
|
7
|
+
'WARN', ## A warning
|
8
|
+
'ERROR', # A handleable error condition
|
9
|
+
'FATAL', # An unhandleable error that results in a program crash
|
10
|
+
'UNKNOWN' # An unknown message that should always be logged
|
11
|
+
].freeze
|
12
|
+
LEVEL_NAMES = LEVELS.map(&:downcase).freeze
|
13
|
+
|
14
|
+
include Tracee::Benchmarkable
|
15
|
+
|
16
|
+
|
17
|
+
attr_reader :level, :preprocessors, :formatter, :streams
|
18
|
+
|
19
|
+
|
20
|
+
def initialize(stream: $stdout, streams: nil, formatter: {:formatter => :tracee}, preprocessors: [], level: :info)
|
21
|
+
@streams = []
|
22
|
+
streams ||= [stream]
|
23
|
+
streams.each {|item| add_stream item}
|
24
|
+
|
25
|
+
if formatter.is_a? Hash
|
26
|
+
# `formatter=' can't accept *array
|
27
|
+
set_formatter *formatter.to_a.flatten
|
28
|
+
else
|
29
|
+
self.formatter = formatter
|
30
|
+
end
|
31
|
+
|
32
|
+
@preprocessors = []
|
33
|
+
preprocessors.each {|item|
|
34
|
+
if item.is_a? Hash
|
35
|
+
add_preprocessor *item.to_a.flatten
|
36
|
+
else
|
37
|
+
add_preprocessor item
|
38
|
+
end
|
39
|
+
}
|
40
|
+
|
41
|
+
self.level = level
|
42
|
+
read_log_level_from_env
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def add_preprocessor(callable_or_symbol=nil, *preprocessor_params)
|
47
|
+
if callable_or_symbol.is_a? Symbol
|
48
|
+
@preprocessors << Tracee::Preprocessors.const_get(callable_or_symbol.to_s.camelize).new(*preprocessor_params)
|
49
|
+
elsif callable_or_symbol.respond_to? :call
|
50
|
+
@preprocessors << callable_or_symbol
|
51
|
+
else
|
52
|
+
raise TypeError, 'A preprocessor must respond to #call'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def set_formatter(callable_or_symbol=nil, *formatter_params)
|
57
|
+
if callable_or_symbol.is_a? Symbol
|
58
|
+
@formatter = Tracee::Preprocessors.const_get(callable_or_symbol.to_s.camelize).new(*formatter_params)
|
59
|
+
elsif callable_or_symbol.respond_to? :call
|
60
|
+
@formatters = callable_or_symbol
|
61
|
+
else
|
62
|
+
raise TypeError, 'A formatter must respond to #call'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
alias formatter= set_formatter
|
66
|
+
|
67
|
+
def should_process_caller?
|
68
|
+
formatter.respond_to? :should_process_caller? and formatter.should_process_caller?
|
69
|
+
end
|
70
|
+
|
71
|
+
def add_stream(target_or_stream)
|
72
|
+
if target_or_stream.is_a? Tracee::Stream
|
73
|
+
@streams << target_or_stream
|
74
|
+
else
|
75
|
+
@streams << Stream.new(target_or_stream)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def level=(level)
|
80
|
+
@level = level.is_a?(Integer) ? level : LEVELS.index(level.to_s.upcase)
|
81
|
+
end
|
82
|
+
|
83
|
+
alias log_level= level=
|
84
|
+
alias log_level level
|
85
|
+
|
86
|
+
|
87
|
+
def write(msg, progname, level, level_int, caller_slice=[])
|
88
|
+
now = DateTime.now
|
89
|
+
|
90
|
+
catch :halt do
|
91
|
+
@preprocessors.each do |preprocessor|
|
92
|
+
msg = preprocessor.(level, now, progname, msg, caller_slice)
|
93
|
+
end
|
94
|
+
|
95
|
+
msg = @formatter.(level, now, progname, msg, caller_slice)
|
96
|
+
|
97
|
+
@streams.each do |stream|
|
98
|
+
stream.write msg, level_int, log_level
|
99
|
+
end
|
100
|
+
end
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
|
104
|
+
LEVELS.each_with_index do |level, level_int|
|
105
|
+
const_set level, level_int
|
106
|
+
|
107
|
+
level_name = level.downcase
|
108
|
+
|
109
|
+
class_eval <<-EOS, __FILE__, __LINE__+1
|
110
|
+
def #{level_name}(*args, &block)
|
111
|
+
return if @level > #{level_int}
|
112
|
+
|
113
|
+
if block
|
114
|
+
msg = block.()
|
115
|
+
if args[0].is_a? Hash
|
116
|
+
caller_at = args[0][:caller_at] || 0
|
117
|
+
else
|
118
|
+
progname = args[0].to_s
|
119
|
+
end
|
120
|
+
else
|
121
|
+
msg = args[0]
|
122
|
+
end
|
123
|
+
|
124
|
+
if should_process_caller?
|
125
|
+
caller = caller(1)
|
126
|
+
|
127
|
+
caller_at ||= (args[1] || {})[:caller_at] || 0
|
128
|
+
if caller_at.is_a? Array
|
129
|
+
caller_slice = caller_at.map! {|i| caller[i]}
|
130
|
+
else
|
131
|
+
caller_slice = [*caller[caller_at]]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
write msg, progname, '#{level_name}', #{level_int}, caller_slice
|
136
|
+
end
|
137
|
+
|
138
|
+
def #{level_name}?
|
139
|
+
@level <= #{level_int}
|
140
|
+
end
|
141
|
+
EOS
|
142
|
+
end
|
143
|
+
|
144
|
+
alias <= debug
|
145
|
+
alias << info
|
146
|
+
alias < warn
|
147
|
+
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def read_log_level_from_env
|
152
|
+
if ENV['LOG_LEVEL'] and LEVELS.include? ENV['LOG_LEVEL']
|
153
|
+
self.log_level = ENV['LOG_LEVEL']
|
154
|
+
elsif ENV['DEBUG'] || ENV['VERBOSE']
|
155
|
+
self.log_level = 'DEBUG'
|
156
|
+
elsif ENV['WARN'] || ENV['QUIET']
|
157
|
+
self.log_level = 'WARN'
|
158
|
+
elsif ENV['SILENT']
|
159
|
+
self.log_level = 'ERROR'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Tracee
|
2
|
+
module Preprocessors
|
3
|
+
class Formatter < Base
|
4
|
+
COLORED_LEVELS = {
|
5
|
+
'debug' => 'DEBUG'.white,
|
6
|
+
'info' => 'INFO'.light_cyan,
|
7
|
+
'warn' => 'WARN'.light_magenta,
|
8
|
+
'error' => 'ERROR'.light_yellow,
|
9
|
+
'fatal' => 'FATAL'.light_red,
|
10
|
+
'unknown' => 'UNKNOWN'.light_black
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
UPCASE_LEVELS = Tracee::Logger::LEVEL_NAMES.map {|name| [name, name.upcase]}.to_h.freeze
|
14
|
+
|
15
|
+
TEMPLATES = {
|
16
|
+
tracee: {
|
17
|
+
summary: "%{datetime} %{level} [%{caller}]: %{message}",
|
18
|
+
datetime: "%T.%3N",
|
19
|
+
level: COLORED_LEVELS,
|
20
|
+
caller: "#{'%{file}:%{line}'.white} #{':%{method}'.light_red}"
|
21
|
+
},
|
22
|
+
|
23
|
+
logger_formatter: {
|
24
|
+
summary: "%{level_letter}, [%{datetime} #%{pid}] %{level} -- %{progname}: %{message}",
|
25
|
+
datetime: "%FT%T.%6N",
|
26
|
+
level: UPCASE_LEVELS
|
27
|
+
},
|
28
|
+
|
29
|
+
plain: "%{message}",
|
30
|
+
|
31
|
+
empty: ""
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
TEMPLATE_KEYS = %w{datetime level level_letter pid progname caller message}.freeze
|
35
|
+
CALLER_KEYS = %W{path file line method}.freeze
|
36
|
+
|
37
|
+
attr_reader :summary, :caller, :datetime, :level
|
38
|
+
|
39
|
+
|
40
|
+
# available template keys : datetime, level, level_letter, pid, thread_id, progname, caller, message
|
41
|
+
# available caller keys : path, file, line, method
|
42
|
+
# params : {
|
43
|
+
# summary: <string containing available template keys as interpolation marks>,
|
44
|
+
# datetime: <format available to DateTime#strftime>, # optional
|
45
|
+
# level: {<severity level name> => <label string>, ... }, # optional
|
46
|
+
# caller: <string containing available caller keys as interpolation marks> # required if summary refers caller
|
47
|
+
# }
|
48
|
+
def initialize(params_or_key)
|
49
|
+
if params_or_key.is_a? Symbol
|
50
|
+
params = TEMPLATES[params_or_key]
|
51
|
+
end
|
52
|
+
if params.is_a? String
|
53
|
+
params = {summary: params}
|
54
|
+
end
|
55
|
+
|
56
|
+
unless params.is_a? Hash
|
57
|
+
raise TypeError, 'params must be a Hash or a reference to one of Tracee::Formatters::Template::TEMPLATES'
|
58
|
+
end
|
59
|
+
|
60
|
+
@summary, @caller, @datetime, @level = params.values_at(:summary, :caller, :datetime, :level).map &:freeze
|
61
|
+
@references = TEMPLATE_KEYS.select {|key| @summary["%{#{key}}"]}.to_set
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def call(msg_level, datetime, progname, msg, caller_slice=[])
|
66
|
+
result = @summary.dup
|
67
|
+
|
68
|
+
if @references.include? 'datetime'
|
69
|
+
result.sub! '%{datetime}', datetime.strftime(@datetime || '%FT%T%Z')
|
70
|
+
end
|
71
|
+
|
72
|
+
if @references.include? 'level' or @references.include? 'level_letter'
|
73
|
+
level = @level[msg_level] || msg_level
|
74
|
+
result.sub! '%{level}', level
|
75
|
+
result.sub! '%{level_letter}', level[0]
|
76
|
+
end
|
77
|
+
|
78
|
+
if @references.include? 'pid'
|
79
|
+
result.sub! '%{pid}', Process.pid.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
if @references.include? 'thread_id'
|
83
|
+
result.sub! '%{thread_id}', Thread.current.object_id
|
84
|
+
end
|
85
|
+
|
86
|
+
if @references.include? 'progname'
|
87
|
+
result.sub! '%{progname}', progname
|
88
|
+
end
|
89
|
+
|
90
|
+
if @references.include? 'caller'
|
91
|
+
caller_slice = caller_slice.reverse.map {|line|
|
92
|
+
path, file, line, is_block, block_level, method = line.match(CALLER_RE)[1..-1]
|
93
|
+
block_level ||= is_block && '1'
|
94
|
+
method = "#{method} {#{block_level}}" if block_level
|
95
|
+
@caller % {path: path, file: file, line: line, method: method}
|
96
|
+
} * ' -> '
|
97
|
+
result.sub! '%{caller}', caller_slice
|
98
|
+
end
|
99
|
+
|
100
|
+
if @references.include? 'message'
|
101
|
+
if msg.nil?
|
102
|
+
msg = "\b\b"
|
103
|
+
elsif !msg.is_a?(String)
|
104
|
+
msg = msg.inspect
|
105
|
+
end
|
106
|
+
result.sub! '%{message}', msg
|
107
|
+
end
|
108
|
+
|
109
|
+
return result + "\n"
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def should_process_caller?
|
114
|
+
@references.include? 'caller'
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def inspect
|
119
|
+
summary = @summary.dup
|
120
|
+
if @datetime
|
121
|
+
summary.sub!('%{datetime}', DateTime.parse('2000-10-20 11:22:33.123456789').strftime(@datetime))
|
122
|
+
end
|
123
|
+
if @level
|
124
|
+
summary.sub!('%{level}', "{#{@level.values*', '}}")
|
125
|
+
summary.sub!('%{level_letter}', "{#{@level.values.map {|w| w[0]}*', '}}")
|
126
|
+
end
|
127
|
+
if @caller
|
128
|
+
summary.sub!('%{caller}', @caller.to_s)
|
129
|
+
end
|
130
|
+
%{#{to_s.chop} "#{summary}">}
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# quiet_assets gem works only on thread-safe environment or on multithreaded one with only few assets.
|
2
|
+
# Known issue: https://github.com/evrone/quiet_assets/issues/40
|
3
|
+
# I'd fix it so that it would always reset log_level to that of Rails.application.config.log_level
|
4
|
+
# But such an approach will not allow to change log_level within runtime.
|
5
|
+
#
|
6
|
+
# Tracee deals with it in pretty straightforward manner. It just ignores whatever we do not care about.
|
7
|
+
module Tracee
|
8
|
+
module Preprocessors
|
9
|
+
class QuietAssets < Base
|
10
|
+
|
11
|
+
def initialize(assets_paths=['assets'])
|
12
|
+
@assets_paths_pattern = assets_paths * '|'
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(msg_level, datetime, progname, msg, caller_slice=[])
|
16
|
+
if msg =~ %r{^Started GET "/(#@assets_paths_pattern)/}
|
17
|
+
halt!
|
18
|
+
else
|
19
|
+
msg
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Tracee
|
2
|
+
module Stack
|
3
|
+
module BaseDecorator
|
4
|
+
|
5
|
+
# Make sure that Dir.pwd is the application root directory
|
6
|
+
def self.call(source)
|
7
|
+
return source if source.empty? or source[0]["\n"] # already decorated
|
8
|
+
|
9
|
+
result, current_line_steps = [], []
|
10
|
+
# path, file, line, is_block, block_level, method
|
11
|
+
step_details = source[0].match(CALLER_RE)
|
12
|
+
|
13
|
+
source.each_with_index do |step, i|
|
14
|
+
next_step_details = source[i+1] && source[i+1].match(CALLER_RE)
|
15
|
+
|
16
|
+
if step_details and step_details[:path] !~ Tracee::IGNORE_RE
|
17
|
+
#if level = step_details[:block_level]
|
18
|
+
# step = step.sub(/block (\(\d+ levels\) )?in/, "{#{level}}")
|
19
|
+
#end
|
20
|
+
if method = step_details[:method] and next_step_details and [step_details[:path], step_details[:line]] == [next_step_details[:path], next_step_details[:line]]
|
21
|
+
current_line_steps.unshift "`#{method}#{" {#{step_details[:block_level]}}" if step_details[:block_level]}'"
|
22
|
+
elsif step_details[:line].to_i > 0 and code_line = Tracee::Stack.readline(step_details[:path], step_details[:line].to_i)
|
23
|
+
current_line_steps.unshift step
|
24
|
+
result << "#{current_line_steps * ' -> '}\n >> #{code_line}"
|
25
|
+
current_line_steps = []
|
26
|
+
else
|
27
|
+
result << step
|
28
|
+
end
|
29
|
+
end
|
30
|
+
step_details = next_step_details
|
31
|
+
end
|
32
|
+
|
33
|
+
if defined? IRB and IRB.conf[:BACK_TRACE_LIMIT] and result.size > IRB.conf[:BACK_TRACE_LIMIT] and result.last[-1] != "\n"
|
34
|
+
result.last << "\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/tracee/stack.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'tracee/stack/base_decorator'
|
2
|
+
|
3
|
+
module Tracee
|
4
|
+
module Stack
|
5
|
+
SCRIPT_LINES_MTIMES = {}
|
6
|
+
|
7
|
+
# Rails' autoreload of code doesn't rewrite SCRIPT_LINES__,
|
8
|
+
# to perform that automatically, Tracee::Stack.reload_script_lines should be turned on.
|
9
|
+
# This mattr is left writable mostly for debug purposes.
|
10
|
+
mattr_accessor :reload_script_lines
|
11
|
+
self.reload_script_lines = true
|
12
|
+
|
13
|
+
def self.readlines(file)
|
14
|
+
if reload_script_lines
|
15
|
+
if File.exists?(file)
|
16
|
+
mtime = File.mtime(file)
|
17
|
+
unless SCRIPT_LINES_MTIMES[file] and SCRIPT_LINES_MTIMES[file] >= mtime
|
18
|
+
SCRIPT_LINES_MTIMES[file] = mtime
|
19
|
+
SCRIPT_LINES__[file] = IO.readlines(file)
|
20
|
+
end
|
21
|
+
SCRIPT_LINES__[file]
|
22
|
+
elsif lines = SCRIPT_LINES__[file]
|
23
|
+
lines
|
24
|
+
end
|
25
|
+
else
|
26
|
+
if lines = SCRIPT_LINES__[file]
|
27
|
+
lines
|
28
|
+
else
|
29
|
+
if File.exists?(file)
|
30
|
+
SCRIPT_LINES_MTIMES[file] = File.mtime(file)
|
31
|
+
SCRIPT_LINES__[file] = IO.readlines(file)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.readline(file, line)
|
38
|
+
if lines = readlines(file)
|
39
|
+
(lines[line.to_i - 1] || "<line #{line} is not found> ").chomp.green
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Tracee
|
2
|
+
|
3
|
+
class Stream
|
4
|
+
attr_reader :target
|
5
|
+
|
6
|
+
class TargetError < TypeError
|
7
|
+
def initialize(message='A target must be IO | String | Hash{<level name> => <level log file path | IO>, ... } | Hash{:cascade => <level log file path pattern with "level" key>}', *) super end
|
8
|
+
end
|
9
|
+
|
10
|
+
# @ target : IO | String | {<level name> => < level log file path | IO >, ... } | {:cascade => < level log file path pattern >}
|
11
|
+
# pattern example : "log/development.%{level}.log"
|
12
|
+
def initialize(target)
|
13
|
+
if target.is_a? Hash
|
14
|
+
raise TargetError if target.values.any? {|val| !( val.is_a? String or val.is_a? IO or val.is_a? StringIO )}
|
15
|
+
|
16
|
+
if pattern = target[:cascade]
|
17
|
+
target = Tracee::Logger::LEVEL_NAMES.map {|name|
|
18
|
+
[name, pattern % {level: name}]
|
19
|
+
}.to_h
|
20
|
+
else
|
21
|
+
target = target.with_indifferent_access
|
22
|
+
end
|
23
|
+
|
24
|
+
else
|
25
|
+
raise TargetError unless target.is_a? String or target.is_a? IO or target.is_a? StringIO
|
26
|
+
end
|
27
|
+
|
28
|
+
@target = target
|
29
|
+
end
|
30
|
+
|
31
|
+
# cascade principle:
|
32
|
+
#
|
33
|
+
# logger.log_level = :debug
|
34
|
+
# logger.warn msg
|
35
|
+
# development.debug.log << msg
|
36
|
+
# development.info.log << msg
|
37
|
+
# development.warn.log << msg
|
38
|
+
#
|
39
|
+
# logger.log_level = :warn
|
40
|
+
# logger.error msg
|
41
|
+
# development.warn.log << msg
|
42
|
+
# development.error.log << msg
|
43
|
+
def write(msg, msg_level=nil, log_level=nil)
|
44
|
+
return if msg.nil?
|
45
|
+
|
46
|
+
case @target
|
47
|
+
when Hash # cascade
|
48
|
+
Tracee::Logger::LEVEL_NAMES[log_level..msg_level].each do |name|
|
49
|
+
if target = @target[name]
|
50
|
+
io_write target, msg
|
51
|
+
end
|
52
|
+
end
|
53
|
+
else
|
54
|
+
io_write @target, msg
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
alias << write
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def io_write(target, msg)
|
63
|
+
case target
|
64
|
+
when IO, StringIO then target << msg
|
65
|
+
when String then File.open(target, 'a') {|f| f << msg}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
class IndifferentStream < Stream
|
73
|
+
|
74
|
+
class TargetError < TypeError
|
75
|
+
def initialize(message='A target must be an object implementing #<< method | Hash{<level name> => <such an object>, ... }', *) super end
|
76
|
+
end
|
77
|
+
|
78
|
+
# @ target : IO | String | {<level name> => < level log file path | IO >, ... } | {:cascade => < level log file path pattern >}
|
79
|
+
# pattern example : "log/development.%{level}.log"
|
80
|
+
def initialize(target)
|
81
|
+
if target.is_a? Hash
|
82
|
+
raise TargetError if target.values.any? {|val| !val.respond_to? :<<}
|
83
|
+
|
84
|
+
target = target.with_indifferent_access
|
85
|
+
else
|
86
|
+
raise TargetError unless target.respond_to? :<<
|
87
|
+
end
|
88
|
+
|
89
|
+
@target = target
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def io_write(target, msg)
|
95
|
+
target << msg
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
data/lib/tracee.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# When you working with IRB/Pry/Ripl it should be defined in according rc-file for all loaded ruby files to be cached into.
|
2
|
+
unless defined? SCRIPT_LINES__
|
3
|
+
SCRIPT_LINES__ = {}
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'active_support'
|
7
|
+
require 'active_support/inflector'
|
8
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
9
|
+
require 'active_support/core_ext/module/aliasing'
|
10
|
+
require 'active_support/core_ext/class/attribute'
|
11
|
+
require 'active_support/tagged_logging'
|
12
|
+
|
13
|
+
require 'colorize'
|
14
|
+
|
15
|
+
require 'tracee/version'
|
16
|
+
require 'tracee/benchmarkable'
|
17
|
+
require 'tracee/logger'
|
18
|
+
require 'tracee/preprocessors/base'
|
19
|
+
require 'tracee/preprocessors/formatter'
|
20
|
+
require 'tracee/preprocessors/quiet_assets'
|
21
|
+
require 'tracee/stream'
|
22
|
+
require 'tracee/stack'
|
23
|
+
require 'tracee/ext/exception'
|
24
|
+
require 'tracee/ext/active_support'
|
25
|
+
require 'tracee/ext/better_errors'
|
26
|
+
require 'tracee/engine'
|
27
|
+
|
28
|
+
module Tracee
|
29
|
+
CALLER_RE = \
|
30
|
+
%r{^(?<path>.*?(?<file>[^/\\]+?))#{ # ( path ( file ) )
|
31
|
+
}:(?<line>\d+)(?::in #{ # :( line )[ :in
|
32
|
+
}`(?<is_block>block (?:\((?<block_level>\d+) levels\) )?in )?(?<method>.+?)'#{ # `( [ block in ] closure )' ]
|
33
|
+
})?$}
|
34
|
+
|
35
|
+
IGNORE_RE = \
|
36
|
+
%r{/irb(/|\.rb$)#{ # irb internals
|
37
|
+
}|lib/active_support/dependencies.rb$#{ # everywhere-proxy
|
38
|
+
}|^-e:#{ # ruby -e oneliner
|
39
|
+
}|^(script|bin)/#{ # other common entry points
|
40
|
+
}|/gems/bundler-\d|ruby-\d.\d.\d(@[^/]+)?/bin/#{ # bundle console
|
41
|
+
}|lib/rails/commands(/|\.rb$)#{ # rails console
|
42
|
+
}}
|
43
|
+
|
44
|
+
|
45
|
+
class ::Exception
|
46
|
+
include Tracee::Extensions::Exception
|
47
|
+
end
|
48
|
+
|
49
|
+
module ::ActiveSupport::TaggedLogging::Formatter
|
50
|
+
include Tracee::Extensions::ActiveSupport::TaggedLogging::Formatter
|
51
|
+
end
|
52
|
+
|
53
|
+
# Use `Tracee.decorate_stack_everywhere` only within a console, because it significantly slowdown rails middleware.
|
54
|
+
# So better put it into .irbrc or similar.
|
55
|
+
class << self
|
56
|
+
|
57
|
+
def decorate_exceptions_stack
|
58
|
+
Exception.trace_decorator = Stack::BaseDecorator
|
59
|
+
|
60
|
+
# These would extremely slowdown or stop runtime
|
61
|
+
[SystemStackError, NoMemoryError, NameError, defined?(IRB::Abort) && IRB::Abort].compact.each do |klass|
|
62
|
+
klass.trace_decorator = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
# But this NameError's subclass would not
|
66
|
+
NoMethodError.trace_decorator = Exception.trace_decorator
|
67
|
+
end
|
68
|
+
|
69
|
+
def decorate_better_errors_stack(from_decorate_everywhere=false)
|
70
|
+
if defined? BetterErrors
|
71
|
+
BetterErrors::Middleware.class_eval do
|
72
|
+
include Tracee::Extensions::BetterErrors::Middleware
|
73
|
+
end
|
74
|
+
elsif !from_decorate_everywhere
|
75
|
+
warn "Tracee.decorate_better_errors_stack was ignored, because BetterErrors hadn't been defined."
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def decorate_active_support_stack
|
80
|
+
ActiveSupport::BacktraceCleaner.class_eval do
|
81
|
+
include Tracee::Extensions::ActiveSupport::BacktraceCleaner
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def decorate_stack_everywhere
|
86
|
+
decorate_exceptions_stack
|
87
|
+
decorate_better_errors_stack(true)
|
88
|
+
decorate_active_support_stack
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
$log ||= Logger.new
|
95
|
+
end
|
data/tracee.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tracee/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "tracee"
|
8
|
+
spec.version = Tracee::VERSION
|
9
|
+
spec.authors = ["Sergey Baev"]
|
10
|
+
spec.email = ["tinbka@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{An extensible logger with stack tracing, benchmarking, preprocessing, and severity-based output splitting}
|
13
|
+
spec.description = %q{Tracee is a simple extensible logger with stack tracing, benchmarking, preprocessing, and severity-based output splitting. Tracee is meant for development and debugging of any type of application or library, and compatible with Rails. The main reason of its existence is to help you see through a stack.}
|
14
|
+
spec.homepage = "https://github.com/tinbka/tracee"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_development_dependency "rspec", "~> 3.3"
|
23
|
+
|
24
|
+
spec.add_dependency "colorize"
|
25
|
+
spec.add_dependency "activesupport"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tracee
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sergey Baev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-16 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: '3.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: colorize
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
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: activesupport
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Tracee is a simple extensible logger with stack tracing, benchmarking,
|
84
|
+
preprocessing, and severity-based output splitting. Tracee is meant for development
|
85
|
+
and debugging of any type of application or library, and compatible with Rails.
|
86
|
+
The main reason of its existence is to help you see through a stack.
|
87
|
+
email:
|
88
|
+
- tinbka@gmail.com
|
89
|
+
executables: []
|
90
|
+
extensions: []
|
91
|
+
extra_rdoc_files: []
|
92
|
+
files:
|
93
|
+
- ".gitignore"
|
94
|
+
- ".rspec"
|
95
|
+
- ".travis.yml"
|
96
|
+
- Gemfile
|
97
|
+
- LICENSE
|
98
|
+
- README.md
|
99
|
+
- Rakefile
|
100
|
+
- bin/console
|
101
|
+
- bin/setup
|
102
|
+
- lib/tracee.rb
|
103
|
+
- lib/tracee/benchmarkable.rb
|
104
|
+
- lib/tracee/engine.rb
|
105
|
+
- lib/tracee/ext/active_support.rb
|
106
|
+
- lib/tracee/ext/better_errors.rb
|
107
|
+
- lib/tracee/ext/exception.rb
|
108
|
+
- lib/tracee/logger.rb
|
109
|
+
- lib/tracee/preprocessors/base.rb
|
110
|
+
- lib/tracee/preprocessors/formatter.rb
|
111
|
+
- lib/tracee/preprocessors/quiet_assets.rb
|
112
|
+
- lib/tracee/stack.rb
|
113
|
+
- lib/tracee/stack/base_decorator.rb
|
114
|
+
- lib/tracee/stream.rb
|
115
|
+
- lib/tracee/version.rb
|
116
|
+
- tracee.gemspec
|
117
|
+
homepage: https://github.com/tinbka/tracee
|
118
|
+
licenses: []
|
119
|
+
metadata: {}
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 2.4.8
|
137
|
+
signing_key:
|
138
|
+
specification_version: 4
|
139
|
+
summary: An extensible logger with stack tracing, benchmarking, preprocessing, and
|
140
|
+
severity-based output splitting
|
141
|
+
test_files: []
|