trace-util-adv 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +7 -0
- data/VERSION +1 -1
- data/lib/README +87 -0
- data/lib/TODO.txt +12 -0
- data/lib/action_handler.rb +99 -0
- data/lib/appenders/appender.rb +97 -0
- data/lib/appenders/appender_registration.rb +16 -0
- data/lib/appenders/base_appender.rb +82 -0
- data/lib/appenders/file_appender.rb +72 -0
- data/lib/appenders/html_appender.rb +94 -0
- data/lib/appenders/logger_appender.rb +84 -0
- data/lib/appenders/stream_appender.rb +37 -0
- data/lib/appenders/template_log_appender.rb +28 -0
- data/lib/appenders/xml_appender.rb +75 -0
- data/lib/core_extensions.rb +101 -0
- data/lib/filters/base_filters.rb +178 -0
- data/lib/filters/composite_filters.rb +71 -0
- data/lib/filters/filter_factory.rb +17 -0
- data/lib/filters/message_filters.rb +35 -0
- data/lib/filters/tracing_filter.rb +88 -0
- data/lib/output_templates.rb +5 -0
- data/lib/rule_match.rb +38 -0
- data/lib/sample_filters.rb +95 -0
- data/lib/templates/base_template.rb +21 -0
- data/lib/templates/html_template.rb +48 -0
- data/lib/templates/string_template.rb +30 -0
- data/lib/templates/trace_output_handler.rb +47 -0
- data/lib/templates/xml_template.rb +36 -0
- data/lib/test_action_handler.rb +34 -0
- data/lib/test_appender.rb +29 -0
- data/lib/test_file_appender.rb +32 -0
- data/lib/test_filters.rb +112 -0
- data/lib/test_filters_chain.rb +100 -0
- data/lib/test_filters_create.rb +28 -0
- data/lib/test_html_appender.rb +66 -0
- data/lib/test_special_filters.rb +78 -0
- data/lib/test_xml_appender.rb +66 -0
- data/lib/test_xml_gen.rb +46 -0
- data/lib/trace_appenders.rb +9 -0
- data/lib/trace_calls.rb +86 -0
- data/lib/trace_ext.rb +57 -0
- data/lib/trace_filters.rb +4 -0
- data/trace-util-adv.gemspec +98 -0
- metadata +44 -2
data/CHANGELOG
ADDED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/README
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
Advanced TraceUtils
|
2
|
+
===================
|
3
|
+
last update: 26/10/2009
|
4
|
+
By kristian mandrup, kmandrup@gmail.com
|
5
|
+
---
|
6
|
+
|
7
|
+
This project enables advanced TRACING capabilities on any Ruby application, class or module.
|
8
|
+
|
9
|
+
The project was inspired by TraceUtils, a little meta-programming example (~50 lines of code) by Dave Thomas
|
10
|
+
(See Pragmatic Bookstore - http://pragprog.com/)
|
11
|
+
|
12
|
+
The original TraceUtils has been completely refactored and expanded to include:
|
13
|
+
- Indentation according to nesting level of methods calls
|
14
|
+
- Define sets of context filters for when to apply tracing on a method, when to apply appenders etc
|
15
|
+
- module, class, method, instance variable and composite filters
|
16
|
+
- Action handlers to handle each trace/log statement
|
17
|
+
- Appenders, similar to log4j appenders, to allow channeling of trace messages to multiple trace consumers
|
18
|
+
- Action handlers and Appenders can also have context filters registered
|
19
|
+
- Custom Filters
|
20
|
+
|
21
|
+
Design
|
22
|
+
------
|
23
|
+
- Log message
|
24
|
+
each log message consists of the message itself and its current context. The context includes:
|
25
|
+
- Modules/Classes hierarchy of the method where the log statement is called
|
26
|
+
- Method name
|
27
|
+
- Instance variables
|
28
|
+
|
29
|
+
TODO:
|
30
|
+
log messages can be inserted in your application code and will be handled by the tracing framework.
|
31
|
+
Currently the framework only supports tracing of method entry and exits.
|
32
|
+
|
33
|
+
- Tracing::TraceExt
|
34
|
+
is configured with a set of action handlers (see below)
|
35
|
+
can be configured with a set of filters
|
36
|
+
|
37
|
+
- Tracing::TraceCalls
|
38
|
+
each Module/Class which should have tracing applied must include the TraceCalls module
|
39
|
+
when TraceCalls is included, the 'included' callback method iterates all instance methods of the module it has been included
|
40
|
+
for each such instance method, it will apply a 'before' and 'after' aspect to the instance method only if all filters registered
|
41
|
+
directly on TraceExt are passed.
|
42
|
+
|
43
|
+
When (the application runs and) the instance method is called, each action handler registered with TraceExt is called to handle the "runtime" tracing
|
44
|
+
|
45
|
+
- Filter
|
46
|
+
decides whether the trace message and current context should "pass through". Multiple filters act as a filter chain, where all filters
|
47
|
+
in the chain must be passed the filtered item is rejected
|
48
|
+
- ModuleFilter, ClassFilter, MethodFilter, CompositeModuleFilter, CompositeClassFilter, InstanceVarFilter, CompositeInstanceVarFilter
|
49
|
+
|
50
|
+
You can also specify custom filters (see class RangeFilter in filters/message_filters.rb)
|
51
|
+
Filters can also include custom include/exclude filters using Procs or Classes (see test_special_filters.rb)
|
52
|
+
|
53
|
+
TODO
|
54
|
+
soon filters will be a lot easier to set up for the simple use cases!
|
55
|
+
|
56
|
+
- ActionHandler
|
57
|
+
handles a runtime logging action
|
58
|
+
can be configured with a set of filters and appenders
|
59
|
+
if all filters are passed for the given log message and context, each appender is called in turn with the same message and context
|
60
|
+
The appenders can also be set using registered appenders with symbols. Default symbol mappings are:
|
61
|
+
:logger, :xml, :html, :template
|
62
|
+
|
63
|
+
For more on Appenders, see section below
|
64
|
+
|
65
|
+
Usage:
|
66
|
+
@ah1 = Tracing::ActionHandler.new(:filters => [Module_filter_A, Method_filter_A], :appenders => [tla1])
|
67
|
+
|
68
|
+
Alternative appender configuration using symbols:
|
69
|
+
@ah1 = Tracing::ActionHandler.new(:filters => [Module_filter_A, Method_filter_A], :appenders => [:template, :xml])
|
70
|
+
|
71
|
+
|
72
|
+
- Appender
|
73
|
+
is configured with some appender options specific to the type of Appender
|
74
|
+
must be configured with a Tracer that renders the output string which the appender the appends to some consumer, such as a stream
|
75
|
+
- LoggerAppender, HtmlAppender, XmlAppender, FileAppender
|
76
|
+
- TemplatateLogAppender
|
77
|
+
|
78
|
+
Usage:
|
79
|
+
tla1 = Tracing::TemplateLogAppender.new(:options => {:overwrite => false, :time_limit => 2.minutes}, :tracer => :string)
|
80
|
+
|
81
|
+
For any Appender, the tracer configuration can be set using a symbol :string, :xml, :html.
|
82
|
+
Otherwise the default tracer for the given appender is used, fx HtmlTracer for the HtmlAppender.
|
83
|
+
|
84
|
+
Examples of use
|
85
|
+
--------------
|
86
|
+
- test_filters.rb
|
87
|
+
- test_special_filters.rb
|
data/lib/TODO.txt
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'filters/tracing_filter'
|
2
|
+
require 'trace_appenders'
|
3
|
+
|
4
|
+
module Tracing
|
5
|
+
class ActionHandler
|
6
|
+
include Tracing::Filter::Registration
|
7
|
+
include Tracing::Filter::Exec
|
8
|
+
include Tracing::Appender::Registration
|
9
|
+
|
10
|
+
attr_accessor :filters
|
11
|
+
|
12
|
+
# enable registration of action handlers
|
13
|
+
module Registration
|
14
|
+
attr_accessor :action_handlers
|
15
|
+
|
16
|
+
def action_handler_from_type(type, options)
|
17
|
+
if type.kind_of?(Symbol)
|
18
|
+
type_sym = type.to_sym
|
19
|
+
# use default mappings if no match found
|
20
|
+
appender_class = appender_mappings[type_sym] || action_handler_mappings[:default]
|
21
|
+
if appender_class
|
22
|
+
appender_class.new(options)
|
23
|
+
else
|
24
|
+
# nil
|
25
|
+
raise Exception, "No appender registered for this type: #{type}"
|
26
|
+
end
|
27
|
+
elsif appender.kind_of? Tracing::OutputTemplate::Trace
|
28
|
+
appender
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_action_handlers(reg_action_handlers)
|
35
|
+
if reg_action_handlers.kind_of? Array
|
36
|
+
reg_action_handlers.flatten!
|
37
|
+
new_action_handlers = reg_action_handlers.collect do |f|
|
38
|
+
create_action_handler(f)
|
39
|
+
end
|
40
|
+
new_action_handlers
|
41
|
+
elsif reg_action_handlers.kind_of?(Symbol) || reg_action_handlers.kind_of?(Hash)
|
42
|
+
create_action_handler(reg_action_handlers)
|
43
|
+
else
|
44
|
+
reg_action_handlers
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_action_handler(options)
|
49
|
+
if options.kind_of? Tracing::ActionHandler
|
50
|
+
options
|
51
|
+
elsif options.kind_of? Hash
|
52
|
+
Tracing::ActionHandler.new(options)
|
53
|
+
elsif options.kind_of?(Symbol) || options.kind_of?(String)
|
54
|
+
type = options.to_sym
|
55
|
+
Tracing::Appender.appender_from_type(type, options)
|
56
|
+
else
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO: add convenience way to register action handlers using symbols!
|
62
|
+
def register_action_handlers(reg_action_handlers)
|
63
|
+
@action_handlers ||= []
|
64
|
+
new_action_handlers = create_action_handlers(reg_action_handlers)
|
65
|
+
raise Exception, "No action handlers" if !new_action_handlers
|
66
|
+
@action_handlers << new_action_handlers
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# register filters and appenders
|
71
|
+
def initialize(init_options)
|
72
|
+
register_filters(init_options[:filters])
|
73
|
+
register_appenders(init_options[:appenders] || init_options)
|
74
|
+
end
|
75
|
+
|
76
|
+
# action for tracelogs that pass the filters
|
77
|
+
# by default send to all appenders for further processing!
|
78
|
+
def handle_allow(txt, context)
|
79
|
+
appenders.each do |appender|
|
80
|
+
appender.handle(txt, context)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# action for tracelogs that DO NOT pass the filters
|
85
|
+
def handle_not_allow(txt, context)
|
86
|
+
end
|
87
|
+
|
88
|
+
# handle tracelogs by applying filters and action handler methods
|
89
|
+
def handle(txt, context)
|
90
|
+
if filters_allow?(txt, context)
|
91
|
+
# puts "handle_allow: " + context[:method_name]
|
92
|
+
handle_allow(txt, context)
|
93
|
+
else
|
94
|
+
# puts "handle_not_allow: " + context[:method_name]
|
95
|
+
handle_not_allow(txt, context)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end # class
|
99
|
+
end # module
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Tracing
|
2
|
+
module Appender
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# array of Appender instances
|
7
|
+
attr_accessor :appender_mappings
|
8
|
+
|
9
|
+
# used to help construct/register Appender instances using convenience symbols
|
10
|
+
def default_appender_mappings
|
11
|
+
{
|
12
|
+
:logger => Tracing::LoggerAppender,
|
13
|
+
:stream => Tracing::StreamAppender,
|
14
|
+
:xml => Tracing::XmlAppender,
|
15
|
+
:html => Tracing::HtmlAppender,
|
16
|
+
:template => Tracing::TemplateLogAppender,
|
17
|
+
:default => Tracing::StreamAppender
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def register_default_mappings
|
22
|
+
@appender_mappings ||= {}
|
23
|
+
@appender_mappings.merge!(default_appender_mappings)
|
24
|
+
end
|
25
|
+
|
26
|
+
def register_appender_mappings(_appender_mappings)
|
27
|
+
appenders_mappings ||= {}
|
28
|
+
appenders_mappings.merge!(_appender_mappings)
|
29
|
+
end
|
30
|
+
|
31
|
+
def unregister_appender_mappings(hash)
|
32
|
+
default_appender_mappings.reject!{|key, value| hash.include? key}
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def create_appenders(appenders)
|
37
|
+
register_default_mappings if !appender_mappings
|
38
|
+
if appenders.kind_of? Array
|
39
|
+
appenders.flatten!
|
40
|
+
new_appenders = appenders.collect do |f|
|
41
|
+
Tracing::Appender.create_appender(f)
|
42
|
+
end
|
43
|
+
new_appenders
|
44
|
+
elsif appenders.kind_of?(Symbol) || appenders.kind_of?(Hash)
|
45
|
+
Tracing::Appender.create_appender(appenders)
|
46
|
+
else
|
47
|
+
appenders
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def appender_options_type(options)
|
52
|
+
options[:type]
|
53
|
+
end
|
54
|
+
|
55
|
+
def appender_type(options)
|
56
|
+
if options.kind_of? Hash
|
57
|
+
appender_options_type(options)
|
58
|
+
elsif options.kind_of?(Symbol) || options.kind_of?(String)
|
59
|
+
options.to_sym
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def appender_from_type(type, options)
|
64
|
+
register_default_mappings if !appender_mappings
|
65
|
+
if type.kind_of?(Symbol)
|
66
|
+
type_sym = type.to_sym
|
67
|
+
# use default mappings if no match found
|
68
|
+
appender_class = appender_mappings[type_sym] || appender_mappings[:default]
|
69
|
+
if appender_class
|
70
|
+
appender_class.new(options)
|
71
|
+
else
|
72
|
+
# nil
|
73
|
+
raise Exception, "No appender registered for this type: #{type}"
|
74
|
+
end
|
75
|
+
elsif appender.kind_of? Tracing::OutputTemplate::Trace
|
76
|
+
appender
|
77
|
+
else
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
def create_appender(options)
|
84
|
+
appender_mappings ||= {}
|
85
|
+
type = appender_type(options)
|
86
|
+
if type
|
87
|
+
appender_from_type(type, options)
|
88
|
+
elsif options.kind_of? Tracing::BaseAppender
|
89
|
+
options
|
90
|
+
else
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end # class
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Tracing
|
2
|
+
module Appender
|
3
|
+
|
4
|
+
# enable registration of appenders
|
5
|
+
module Registration
|
6
|
+
attr_accessor :appenders
|
7
|
+
|
8
|
+
def register_appenders(appenders)
|
9
|
+
@appenders ||= []
|
10
|
+
new_appenders = Tracing::Appender.create_appenders(appenders)
|
11
|
+
@appenders.add(new_appenders) if new_appenders
|
12
|
+
end
|
13
|
+
|
14
|
+
end # registration
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Tracing
|
2
|
+
# base appender (abstract)
|
3
|
+
class BaseAppender
|
4
|
+
include Tracing::Filter::Registration
|
5
|
+
include Tracing::Filter::Exec
|
6
|
+
|
7
|
+
attr_accessor :options
|
8
|
+
attr_reader :tracer
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :tracers
|
12
|
+
|
13
|
+
def default_tracers
|
14
|
+
{
|
15
|
+
:string => Tracing::OutputTemplate::StringTrace,
|
16
|
+
:xml => Tracing::OutputTemplate::XmlTrace,
|
17
|
+
:html => Tracing::OutputTemplate::HtmlTrace,
|
18
|
+
:default => Tracing::OutputTemplate::StringTrace
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def register_tracers(tracers = nil)
|
23
|
+
@tracers ||= default_tracers
|
24
|
+
@tracers = @tracers.merge!(tracers || {})
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_tracer(tracer)
|
28
|
+
tracers ||= register_tracers
|
29
|
+
if tracer.kind_of?(Symbol) || tracer.kind_of?(String)
|
30
|
+
tracer_class = tracers[tracer.to_sym] || tracers[:default]
|
31
|
+
tracer_class.new
|
32
|
+
elsif tracer.kind_of? Tracing::OutputTemplate::Trace
|
33
|
+
tracer
|
34
|
+
else
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(init_options = nil)
|
41
|
+
return if !init_options
|
42
|
+
if init_options.kind_of? Hash
|
43
|
+
@options = init_options[:options] || init_options
|
44
|
+
register_filters(init_options[:filters])
|
45
|
+
tracer = @options[:tracer] if @options
|
46
|
+
return if !tracer
|
47
|
+
|
48
|
+
tracer = self.class.create_tracer(tracer)
|
49
|
+
@tracer = tracer
|
50
|
+
elsif init_options.kind_of? Symbol
|
51
|
+
self.class.register_tracers
|
52
|
+
tracer = self.class.tracers[init_options]
|
53
|
+
return if !tracer
|
54
|
+
|
55
|
+
tracer = self.class.create_tracer(tracer)
|
56
|
+
@tracer = tracer
|
57
|
+
else
|
58
|
+
raise Exception, "Appender must be initialized with Hash"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# default action handler depending on filters result
|
63
|
+
def handle(txt, context)
|
64
|
+
if filters_allow?(txt, context)
|
65
|
+
allow_append(txt, context)
|
66
|
+
else
|
67
|
+
not_allow_append(txt, context)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def allow_append(txt, context)
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
def not_allow_append(txt, context)
|
76
|
+
end
|
77
|
+
|
78
|
+
def time_limit
|
79
|
+
options[:time_limit] || 1.minute
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Tracing
|
2
|
+
# File Appender
|
3
|
+
class FileAppender < BaseAppender
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def default_path
|
7
|
+
return if !@default_path
|
8
|
+
if !File.exists?(@default_path)
|
9
|
+
FileUtils.mkdir_p @default_path
|
10
|
+
end
|
11
|
+
@default_path
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_path=(path)
|
15
|
+
@default_path = path
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# file
|
20
|
+
def write_file(file, txt)
|
21
|
+
file = file_path(file)
|
22
|
+
|
23
|
+
write_mode = mode(file)
|
24
|
+
File.open(file, write_mode) do |f|
|
25
|
+
f.puts txt
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_initial_file(file)
|
30
|
+
end
|
31
|
+
|
32
|
+
def insert_into_file(file, txt, marker_txt)
|
33
|
+
line = marker_txt
|
34
|
+
|
35
|
+
file = file_path(file)
|
36
|
+
|
37
|
+
if !File.exist?(file)
|
38
|
+
create_initial_file(file)
|
39
|
+
end
|
40
|
+
|
41
|
+
gsub_file file, /(#{Regexp.escape(line)})/mi do |match|
|
42
|
+
"#{txt}\n#{match}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# helper for deciding file "write mode"
|
47
|
+
def is_old_file?(file)
|
48
|
+
File.new(file).mtime < (Time.now - time_limit)
|
49
|
+
end
|
50
|
+
|
51
|
+
def file_path(file)
|
52
|
+
file = File.join(self.class.default_path, file) if self.class.default_path
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
def gsub_file(path, regexp, *args, &block)
|
57
|
+
content = File.read(path).gsub(regexp, *args, &block)
|
58
|
+
File.open(path, 'wb') { |file| file.write(content) }
|
59
|
+
end
|
60
|
+
|
61
|
+
# default file "write mode"
|
62
|
+
def mode(file)
|
63
|
+
if !options[:overwrite] && File.exist?(file) && !is_old_file?(file)
|
64
|
+
"a+"
|
65
|
+
else
|
66
|
+
"w+"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end # class
|
71
|
+
|
72
|
+
end # tracing
|