needle 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/LICENSE-BSD +27 -0
- data/doc/LICENSE-GPL +280 -0
- data/doc/LICENSE-RUBY +56 -0
- data/doc/README +70 -0
- data/doc/manual/chapter.erb +18 -0
- data/doc/manual/index.erb +29 -0
- data/doc/manual/manual.css +192 -0
- data/doc/manual/manual.rb +240 -0
- data/doc/manual/manual.yml +48 -0
- data/doc/manual/page.erb +86 -0
- data/doc/manual/parts/01_license.txt +5 -0
- data/doc/manual/parts/01_support.txt +1 -0
- data/doc/manual/parts/01_use_cases.txt +141 -0
- data/doc/manual/parts/01_what_is_needle.txt +1 -0
- data/doc/manual/parts/02_creating.txt +9 -0
- data/doc/manual/parts/02_namespaces.txt +47 -0
- data/doc/manual/parts/02_overview.txt +3 -0
- data/doc/manual/parts/02_services.txt +44 -0
- data/doc/manual/tutorial.erb +30 -0
- data/doc/manual-html/chapter-1.html +354 -0
- data/doc/manual-html/chapter-2.html +310 -0
- data/doc/manual-html/chapter-3.html +154 -0
- data/doc/manual-html/chapter-4.html +154 -0
- data/doc/manual-html/chapter-5.html +154 -0
- data/doc/manual-html/chapter-6.html +154 -0
- data/doc/manual-html/chapter-7.html +154 -0
- data/doc/manual-html/index.html +177 -0
- data/doc/manual-html/manual.css +192 -0
- data/lib/needle/container.rb +318 -0
- data/lib/needle/errors.rb +32 -0
- data/lib/needle/include-exclude.rb +116 -0
- data/lib/needle/interceptor-chain.rb +162 -0
- data/lib/needle/interceptor.rb +189 -0
- data/lib/needle/log-factory.rb +207 -0
- data/lib/needle/logger.rb +161 -0
- data/lib/needle/logging-interceptor.rb +62 -0
- data/lib/needle/models/prototype-deferred.rb +41 -0
- data/lib/needle/models/prototype.rb +39 -0
- data/lib/needle/models/proxy.rb +84 -0
- data/lib/needle/models/singleton-deferred.rb +57 -0
- data/lib/needle/models/singleton.rb +56 -0
- data/lib/needle/models.rb +44 -0
- data/lib/needle/registry.rb +110 -0
- data/lib/needle/service-point.rb +109 -0
- data/lib/needle/version.rb +28 -0
- data/lib/needle.rb +54 -0
- data/test/ALL-TESTS.rb +21 -0
- data/test/models/tc_prototype.rb +53 -0
- data/test/models/tc_prototype_deferred.rb +54 -0
- data/test/models/tc_proxy.rb +51 -0
- data/test/models/tc_singleton.rb +53 -0
- data/test/models/tc_singleton_deferred.rb +54 -0
- data/test/tc_container.rb +246 -0
- data/test/tc_interceptor.rb +92 -0
- data/test/tc_interceptor_chain.rb +181 -0
- data/test/tc_logger.rb +181 -0
- data/test/tc_models.rb +44 -0
- data/test/tc_registry.rb +34 -0
- data/test/tc_service_point.rb +100 -0
- metadata +107 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
#--
|
2
|
+
# =============================================================================
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# This source file is distributed as part of the Needle dependency injection
|
7
|
+
# library for Ruby. This file (and the library as a whole) may be used only as
|
8
|
+
# allowed by either the BSD license, or the Ruby license (or, by association
|
9
|
+
# with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
|
10
|
+
# distribution for the texts of these licenses.
|
11
|
+
# -----------------------------------------------------------------------------
|
12
|
+
# needle website : http://needle.rubyforge.org
|
13
|
+
# project website: http://rubyforge.org/projects/needle
|
14
|
+
# =============================================================================
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'needle/errors'
|
18
|
+
|
19
|
+
module Needle
|
20
|
+
|
21
|
+
# This represents the definition of an interceptor as it is attached to a
|
22
|
+
# service point. Instances of Interceptor are also used for configuring
|
23
|
+
# themselves programmatically.
|
24
|
+
#
|
25
|
+
# You will almost never instantiate an Interceptor object directly. Instead,
|
26
|
+
# use the Container#intercept method. You can then configure the new
|
27
|
+
# interceptor by chaining methods of the new object together, quite
|
28
|
+
# readably:
|
29
|
+
#
|
30
|
+
# container.intercept( :foo ).with! { some_interceptor }.
|
31
|
+
# with_options( :arg => :value )
|
32
|
+
#
|
33
|
+
# You can also create new interceptors on the fly via the Interceptor#doing
|
34
|
+
# method.
|
35
|
+
class Interceptor
|
36
|
+
|
37
|
+
# This is the wrapper for on-the-fly interceptors that have been created
|
38
|
+
# via Interceptor#doing. The callback registered with Interceptor#doing
|
39
|
+
# gets wrapped by an instance of this class, to comply with the interface
|
40
|
+
# required by InterceptorChainBuilder.
|
41
|
+
#
|
42
|
+
# This class should rarely (if ever) be instantiated directly. Instead,
|
43
|
+
# using the Interceptor#doing method to create dynamic interceptors.
|
44
|
+
class DynamicInterceptor
|
45
|
+
|
46
|
+
# Create a new DynamicInterceptor instance that wraps the given
|
47
|
+
# callback.
|
48
|
+
def initialize( callback )
|
49
|
+
@callback = callback
|
50
|
+
end
|
51
|
+
|
52
|
+
# This method is a concession to the required interceptor factory
|
53
|
+
# interface. It should return the new interceptor, configured to be
|
54
|
+
# attached to the given service point, and with the given options.
|
55
|
+
# It will always return +self+.
|
56
|
+
def new( point, opts )
|
57
|
+
@point = point
|
58
|
+
@options = opts
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Process this link in the interceptor chain. This will invoke the
|
63
|
+
# wrapped callback, passing in the chain and context parameters.
|
64
|
+
# Before invoking the callback, the options and service point
|
65
|
+
# references that were given in #new are assigned to context
|
66
|
+
# data members (so they can be referenced inside the callback).
|
67
|
+
def process( chain, context )
|
68
|
+
context.data[:options] = @options
|
69
|
+
context.data[:point] = @point
|
70
|
+
@callback.call( chain, context )
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
# The set of options that were given to this interceptor via the
|
76
|
+
# #with_options method.
|
77
|
+
attr_reader :options
|
78
|
+
|
79
|
+
# Create a new Interceptor definition. By default, it has no
|
80
|
+
# implementation and a priority of 0.
|
81
|
+
def initialize
|
82
|
+
@options = { :priority => 0 }
|
83
|
+
@doing = @with = nil
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns the action that was specified for this interceptor as a proc
|
87
|
+
# instance. This will either be the block passed to #with, or a proc
|
88
|
+
# that wraps the instantiation of a DynamicInterceptor (when #doing
|
89
|
+
# was used).
|
90
|
+
#
|
91
|
+
# If neither #with nor #doing were specified, an
|
92
|
+
# InterceptorConfigurationError is raised.
|
93
|
+
def action
|
94
|
+
return @with if @with
|
95
|
+
raise InterceptorConfigurationError,
|
96
|
+
"You must specify either 'with' or 'doing'" unless @doing
|
97
|
+
|
98
|
+
return proc { |c| DynamicInterceptor.new( @doing ) }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Sets the action for this interceptor to be that defined by the
|
102
|
+
# interceptor returned when the block is executed. You can only
|
103
|
+
# invoke #with once, and never after previously invoking #doing on the
|
104
|
+
# same interceptor instance.
|
105
|
+
#
|
106
|
+
# Usage:
|
107
|
+
#
|
108
|
+
# container.intercept( :foo ).
|
109
|
+
# with { |c| c.logging_interceptor }
|
110
|
+
def with( &block )
|
111
|
+
if @with
|
112
|
+
raise InterceptorConfigurationError,
|
113
|
+
"you cannot redefine 'with' behavior"
|
114
|
+
end
|
115
|
+
|
116
|
+
if @doing
|
117
|
+
raise InterceptorConfigurationError,
|
118
|
+
"cannot specify 'with' after specifying 'doing'"
|
119
|
+
end
|
120
|
+
|
121
|
+
if block.nil?
|
122
|
+
raise InterceptorConfigurationError,
|
123
|
+
"you must specify a block to 'with'"
|
124
|
+
end
|
125
|
+
|
126
|
+
@with = block
|
127
|
+
self
|
128
|
+
end
|
129
|
+
|
130
|
+
# This is identical to #with, but it wraps the block in another proc that
|
131
|
+
# calls +instance_eval+ on the container, with the block.
|
132
|
+
#
|
133
|
+
# Usage:
|
134
|
+
#
|
135
|
+
# container.intercept( :foo ).
|
136
|
+
# with! { logging_interceptor }
|
137
|
+
def with!( &block )
|
138
|
+
with { |c| c.instance_eval( &block ) }
|
139
|
+
end
|
140
|
+
|
141
|
+
# This allows new interceptors to be defined "on-the-fly". The associated
|
142
|
+
# block must accept two parameters--an object representing the chain of
|
143
|
+
# interceptors, and the context of the current method invocation. The block
|
144
|
+
# should then invoke #process_next on the chain (passing the context as
|
145
|
+
# the lone parameter) when the next element of the chain should be invoked.
|
146
|
+
#
|
147
|
+
# You should only call #doing once per interceptor, and never after
|
148
|
+
# invoking #with on the same interceptor.
|
149
|
+
def doing( &block )
|
150
|
+
if @doing
|
151
|
+
raise InterceptorConfigurationError,
|
152
|
+
"you cannot redefine 'doing' behavior"
|
153
|
+
end
|
154
|
+
|
155
|
+
if @with
|
156
|
+
raise InterceptorConfigurationError,
|
157
|
+
"cannot specify 'doing' after specifying 'with'"
|
158
|
+
end
|
159
|
+
|
160
|
+
if block.nil?
|
161
|
+
raise InterceptorConfigurationError,
|
162
|
+
"you must specify a block to 'doing'"
|
163
|
+
end
|
164
|
+
|
165
|
+
@doing = block
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
# Merge the given +opts+ hash into the interceptors options hash.
|
170
|
+
def with_options( opts={} )
|
171
|
+
@options.update opts
|
172
|
+
self
|
173
|
+
end
|
174
|
+
|
175
|
+
# A convenience method for querying the options on an interceptor
|
176
|
+
# definition.
|
177
|
+
def []( name )
|
178
|
+
@options[ name ]
|
179
|
+
end
|
180
|
+
|
181
|
+
# A convenience method for setting the options on an interceptor
|
182
|
+
# definition.
|
183
|
+
def []=( name, value )
|
184
|
+
@options[ name ] = value
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
#--
|
2
|
+
# =============================================================================
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# This source file is distributed as part of the Needle dependency injection
|
7
|
+
# library for Ruby. This file (and the library as a whole) may be used only as
|
8
|
+
# allowed by either the BSD license, or the Ruby license (or, by association
|
9
|
+
# with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
|
10
|
+
# distribution for the texts of these licenses.
|
11
|
+
# -----------------------------------------------------------------------------
|
12
|
+
# needle website : http://needle.rubyforge.org
|
13
|
+
# project website: http://rubyforge.org/projects/needle
|
14
|
+
# =============================================================================
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'needle/logger'
|
18
|
+
require 'thread'
|
19
|
+
|
20
|
+
module Needle
|
21
|
+
|
22
|
+
# A factory class that returns Logger instances. Since each registry
|
23
|
+
# has its own logger factory, the logger factory must be separately
|
24
|
+
# instantiable.
|
25
|
+
class LogFactory
|
26
|
+
|
27
|
+
# The default name of the log file to write to.
|
28
|
+
DEFAULT_LOG_FILENAME = "./needle.log"
|
29
|
+
|
30
|
+
# The default format of the log messages (see Logger for more info)
|
31
|
+
DEFAULT_MESSAGE_FORMAT = "[%-5p] %d -- %C: %m"
|
32
|
+
|
33
|
+
# Translate names of levels to their actual values.
|
34
|
+
LEVEL_TRANSLATOR = {
|
35
|
+
"DEBUG" => Logger::DEBUG,
|
36
|
+
"INFO" => Logger::INFO,
|
37
|
+
"WARN" => Logger::WARN,
|
38
|
+
"ERROR" => Logger::ERROR,
|
39
|
+
"FATAL" => Logger::FATAL,
|
40
|
+
"UNKNOWN" => Logger::UNKNOWN
|
41
|
+
}
|
42
|
+
|
43
|
+
# The default date format string to use when logging.
|
44
|
+
attr_reader :default_date_format
|
45
|
+
|
46
|
+
# The default message format string to use when logging.
|
47
|
+
attr_reader :default_message_format
|
48
|
+
|
49
|
+
# The default log level to use for logs that are created.
|
50
|
+
attr_reader :default_level
|
51
|
+
|
52
|
+
# The device that logs will write to.
|
53
|
+
attr_reader :device
|
54
|
+
|
55
|
+
# Create a new LogFactory using the given initialization parameters.
|
56
|
+
# The valid options are:
|
57
|
+
#
|
58
|
+
# * <tt>:device</tt>: the device (pseudo-IO object) to write log
|
59
|
+
# messages to. Either this, or <tt>:filename</tt> must be specified.
|
60
|
+
# * <tt>:filename</tt>: the filename of the log to write to.
|
61
|
+
# * <tt>:roll_age</tt>: the number of days before the log should be
|
62
|
+
# rolled.
|
63
|
+
# * <tt>:roll_frequency</tt>: either 'daily', 'weekly', or 'monthly'.
|
64
|
+
# * <tt>:roll_size</tt>: the maximum size of a log file.
|
65
|
+
# * <tt>:default_date_format</tt>: the default date format string for
|
66
|
+
# the log.
|
67
|
+
# * <tt>:default_message_format</tt>: the default message format string
|
68
|
+
# for the log.
|
69
|
+
# * <tt>:default_level</tt>: the default log level for the log.
|
70
|
+
# * <tt>:levels</tt>: a hash of patterns that map to a hash of 'level'
|
71
|
+
# 'date_format', and 'message_format' keys, specifying the log level,
|
72
|
+
# date format, and message format for any log whose name matches the key.
|
73
|
+
def initialize( opts={} )
|
74
|
+
if opts[:device]
|
75
|
+
@device = opts[:device]
|
76
|
+
else
|
77
|
+
filename = opts[:filename] || DEFAULT_LOG_FILENAME
|
78
|
+
roll_age = opts[:roll_age ] || opts[:roll_frequency] || 0
|
79
|
+
roll_size = opts[:roll_size] || 0
|
80
|
+
@device = Logger::LogDevice.new( filename,
|
81
|
+
:shift_age=>roll_age, :shift_size=>roll_size )
|
82
|
+
end
|
83
|
+
|
84
|
+
@default_date_format = opts[:default_date_format]
|
85
|
+
@default_message_format = opts[:default_message_format] ||
|
86
|
+
DEFAULT_MESSAGE_FORMAT
|
87
|
+
@default_level = opts[:default_level]
|
88
|
+
|
89
|
+
if @default_level.is_a?( String ) || @default_level.is_a?( Symbol )
|
90
|
+
@default_level = LEVEL_TRANSLATOR[@default_level.to_s]
|
91
|
+
end
|
92
|
+
|
93
|
+
@levels = Hash.new "level" => nil, "date-format" => nil,
|
94
|
+
"message-format" => nil
|
95
|
+
|
96
|
+
( opts[:levels] || {} ).each_pair do |key, value|
|
97
|
+
key = Regexp.new( "^" + key.gsub( /\./, "\\." ).gsub( /\*/, ".*" ) )
|
98
|
+
|
99
|
+
if value.is_a?( String ) || value.is_a?( Symbol )
|
100
|
+
value = { "level" => value.to_s }
|
101
|
+
end
|
102
|
+
|
103
|
+
@levels[ key ] = value
|
104
|
+
end
|
105
|
+
|
106
|
+
@loggers = Hash.new
|
107
|
+
@mutex = Mutex.new
|
108
|
+
@closed = false
|
109
|
+
end
|
110
|
+
|
111
|
+
# Changes the device that the loggers write to. Every log that was created
|
112
|
+
# by this log factory will be changed to use the given device.
|
113
|
+
def write_to( device, shift_age = 0, shift_size = 1048576 )
|
114
|
+
saved_critical = Thread.critical
|
115
|
+
Thread.critical = true
|
116
|
+
|
117
|
+
@device.close if @device unless [ $stdout, $stderr ].include?( @device )
|
118
|
+
if device.respond_to?( :write ) && device.respond_to?( :close )
|
119
|
+
@device = device
|
120
|
+
else
|
121
|
+
@device = Logger::LogDevice.new( device.to_str,
|
122
|
+
:shift_age => shift_age,
|
123
|
+
:shift_size => shift_size )
|
124
|
+
end
|
125
|
+
|
126
|
+
@loggers.each_value { |logger| logger.write_to( @device ) }
|
127
|
+
device
|
128
|
+
|
129
|
+
ensure
|
130
|
+
Thread.critical = saved_critical
|
131
|
+
end
|
132
|
+
|
133
|
+
# Searches for a level definition hash that matches the given log
|
134
|
+
# name.
|
135
|
+
def find_definition( name )
|
136
|
+
best_match = { :length => 0, :value => Hash.new }
|
137
|
+
|
138
|
+
@levels.each_pair do |key, value|
|
139
|
+
length = key.to_s.length
|
140
|
+
if best_match[ :length ] < length && key === name
|
141
|
+
best_match[ :length ] = length
|
142
|
+
best_match[ :value ] = value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
return best_match[ :value ]
|
147
|
+
end
|
148
|
+
private :find_definition
|
149
|
+
|
150
|
+
# Retrieves the logger with the given name. If no such log has been
|
151
|
+
# created, the log will be created and initialized. Otherwise, the
|
152
|
+
# log with the given name is returned.
|
153
|
+
def get( name )
|
154
|
+
name = name.fullname if name.respond_to?( :fullname )
|
155
|
+
name = name.name if name.respond_to?( :name )
|
156
|
+
name = name.to_s
|
157
|
+
|
158
|
+
# the common case first, outside the synchronize, for speed
|
159
|
+
return @loggers[ name ] if @loggers[ name ]
|
160
|
+
|
161
|
+
@mutex.synchronize do
|
162
|
+
# check again, inside the synchronize, to avoid race conditions
|
163
|
+
return @loggers[ name ] if @loggers[ name ]
|
164
|
+
|
165
|
+
definition = find_definition( name )
|
166
|
+
|
167
|
+
level = definition[ "level" ] || @default_level
|
168
|
+
date_format = definition[ "date-format" ] || @default_date_format
|
169
|
+
message_format = definition[ "message-format" ] ||
|
170
|
+
@default_message_format
|
171
|
+
|
172
|
+
level = LEVEL_TRANSLATOR[ level ] || level
|
173
|
+
|
174
|
+
logger = Needle::Logger.new( @device )
|
175
|
+
logger.level = level if level
|
176
|
+
logger.progname = name
|
177
|
+
logger.datetime_format = date_format if date_format
|
178
|
+
logger.message_format = message_format if message_format
|
179
|
+
|
180
|
+
@loggers[ name ] = logger
|
181
|
+
return logger
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Closes the logging device and makes this factory invalid for future
|
186
|
+
# accesses.
|
187
|
+
def close
|
188
|
+
@mutex.synchronize do
|
189
|
+
return if @closed
|
190
|
+
|
191
|
+
if @device
|
192
|
+
@device.close unless [ $stdout, $stderr ].include?( @device )
|
193
|
+
end
|
194
|
+
|
195
|
+
@loggers = nil
|
196
|
+
@closed = true
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Returns true if the factory has been closed.
|
201
|
+
def closed?
|
202
|
+
@closed
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
#--
|
2
|
+
# =============================================================================
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# This source file is distributed as part of the Needle dependency injection
|
7
|
+
# library for Ruby. This file (and the library as a whole) may be used only as
|
8
|
+
# allowed by either the BSD license, or the Ruby license (or, by association
|
9
|
+
# with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
|
10
|
+
# distribution for the texts of these licenses.
|
11
|
+
# -----------------------------------------------------------------------------
|
12
|
+
# needle website : http://needle.rubyforge.org
|
13
|
+
# project website: http://rubyforge.org/projects/needle
|
14
|
+
# =============================================================================
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'logger'
|
18
|
+
require 'strscan'
|
19
|
+
require 'needle/errors'
|
20
|
+
|
21
|
+
module Needle
|
22
|
+
|
23
|
+
# A specialization of the standard Logger class that comes with Ruby. This
|
24
|
+
# provides the additional functionality of a fully-customizable message
|
25
|
+
# format, whereas the original only provided a customizable date format.
|
26
|
+
class Logger < ::Logger
|
27
|
+
|
28
|
+
# The brief name of this logger (derived from #progname).
|
29
|
+
attr_reader :name
|
30
|
+
|
31
|
+
# The format string for the message (+nil+ if the default should be used)
|
32
|
+
attr_reader :message_format
|
33
|
+
|
34
|
+
# The map of specifier options supported by this class.
|
35
|
+
SPECIFIER_OPTIONS = {
|
36
|
+
"c" => { :type => "s", :value => "@name" },
|
37
|
+
"C" => { :type => "s", :value => "self.progname" },
|
38
|
+
"d" => { :type => "s", :value => "opts[:timestamp]" },
|
39
|
+
"F" => { :type => "s", :value => "opts[:caller_file]" },
|
40
|
+
"l" => { :type => "s", :value => "opts[:caller_info]" },
|
41
|
+
"L" => { :type => "d", :value => "opts[:caller_line]" },
|
42
|
+
"m" => { :type => "s", :value => "opts[:msg]" },
|
43
|
+
"M" => { :type => "s", :value => "opts[:caller_method]" },
|
44
|
+
"n" => { :type => "s", :value => "$/" },
|
45
|
+
"p" => { :type => "s", :value => "opts[:severity]" },
|
46
|
+
"t" => { :type => "d", :value => "Thread.current.__id__" },
|
47
|
+
"%" => { :type => "s", :value => "'%'" },
|
48
|
+
"P" => { :type => "s", :value => "opts[:progname]" },
|
49
|
+
"$" => { :type => "d", :value => "$$" }
|
50
|
+
}
|
51
|
+
|
52
|
+
# The regular expression for matching specifier patterns in the
|
53
|
+
# format strings.
|
54
|
+
SPECIFIER_PATTERN = /(.*?)%(-?\d*(?:\.\d+)?)?([cCdFlLmMnpt%$P])/
|
55
|
+
|
56
|
+
# Extracts the unqualified name from the progname, after setting the
|
57
|
+
# progname.
|
58
|
+
def progname=( progname )
|
59
|
+
super
|
60
|
+
@name = progname.split( /\./ ).last
|
61
|
+
end
|
62
|
+
|
63
|
+
# Changes the device that the given logger writes to, to be the given
|
64
|
+
# device. Does so in a thread-safe manner.
|
65
|
+
def write_to( device, shift_age = 0, shift_size = 1048576 )
|
66
|
+
saved_critical = Thread.critical
|
67
|
+
Thread.critical = true
|
68
|
+
|
69
|
+
if device
|
70
|
+
if device.respond_to?( :write ) && device.respond_to?( :close )
|
71
|
+
@logdev = device
|
72
|
+
else
|
73
|
+
@logdev = Logger::LogDevice.new( device,
|
74
|
+
:shift_age => shift_age,
|
75
|
+
:shift_size => shift_size )
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
device
|
80
|
+
ensure
|
81
|
+
Thread.critical = saved_critical
|
82
|
+
end
|
83
|
+
|
84
|
+
# Set the message format string to the given string. This also pre-parses
|
85
|
+
# the format for faster processing.
|
86
|
+
#
|
87
|
+
# The format string is a printf-formatted string, which supports the following
|
88
|
+
# format specifiers:
|
89
|
+
#
|
90
|
+
# c:: the unqualified name of the logger
|
91
|
+
# C:: the fully-qualified name of the logger
|
92
|
+
# d:: the date/time string (as formatted by the #datetime_format string)
|
93
|
+
# F:: the filename of the calling routine
|
94
|
+
# l:: the location of the calling routine
|
95
|
+
# L:: the line number of the calling routine
|
96
|
+
# m:: the message to log
|
97
|
+
# M:: the name of the calling method
|
98
|
+
# n:: the newline character
|
99
|
+
# p:: the name of the priority (or severity) used to log this method
|
100
|
+
# t:: the id of the current thread
|
101
|
+
# %:: a percentage character
|
102
|
+
# P:: the progname that was passed to the logger method
|
103
|
+
# $:: the current process id
|
104
|
+
def message_format=( format )
|
105
|
+
@message_format = format
|
106
|
+
return unless format
|
107
|
+
|
108
|
+
@needs_caller_info = false
|
109
|
+
|
110
|
+
format_string = ""
|
111
|
+
format_parameters = []
|
112
|
+
|
113
|
+
@message_format_tokens = []
|
114
|
+
format.scan( SPECIFIER_PATTERN ) do |v|
|
115
|
+
format_string << v[0] if v[0].length > 0
|
116
|
+
opts = SPECIFIER_OPTIONS[ v[2] ]
|
117
|
+
format_string << "%#{v[1]}#{opts[:type]}"
|
118
|
+
format_parameters << opts[:value]
|
119
|
+
@needs_caller_info = true if v[2] =~ /[FlLM]/
|
120
|
+
end
|
121
|
+
format_string << $' if $'.length > 0
|
122
|
+
format_string << "\n"
|
123
|
+
|
124
|
+
definition =
|
125
|
+
"proc { |opts| #{format_string.inspect} " +
|
126
|
+
"% [ #{format_parameters.join(',')} ] }"
|
127
|
+
@message_formatter = eval( definition )
|
128
|
+
end
|
129
|
+
|
130
|
+
# Format the message using the given parameters. If a message format has
|
131
|
+
# not been given, just call the superclass's implementation. Otherwise,
|
132
|
+
# process the tokens from the parsed message format.
|
133
|
+
def format_message( severity, timestamp, msg, progname )
|
134
|
+
if @message_format.nil?
|
135
|
+
super
|
136
|
+
else
|
137
|
+
opts = {
|
138
|
+
:severity => severity,
|
139
|
+
:timestamp => timestamp,
|
140
|
+
:msg => msg,
|
141
|
+
:progname => progname
|
142
|
+
}
|
143
|
+
|
144
|
+
if @needs_caller_info
|
145
|
+
stack = caller
|
146
|
+
stack.shift while stack.first =~ /\blogger\.rb/
|
147
|
+
opts[:caller_info] = caller_info = stack.first
|
148
|
+
match = caller_info.match( /(.*):(\d+)(?::in `(.*)')?/ )
|
149
|
+
opts[:caller_file] = match[1]
|
150
|
+
opts[:caller_line] = match[2]
|
151
|
+
opts[:caller_method] = match[3]
|
152
|
+
end
|
153
|
+
|
154
|
+
@message_formatter.call( opts )
|
155
|
+
end
|
156
|
+
end
|
157
|
+
private :format_message
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#--
|
2
|
+
# =============================================================================
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# This source file is distributed as part of the Needle dependency injection
|
7
|
+
# library for Ruby. This file (and the library as a whole) may be used only as
|
8
|
+
# allowed by either the BSD license, or the Ruby license (or, by association
|
9
|
+
# with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
|
10
|
+
# distribution for the texts of these licenses.
|
11
|
+
# -----------------------------------------------------------------------------
|
12
|
+
# needle website : http://needle.rubyforge.org
|
13
|
+
# project website: http://rubyforge.org/projects/needle
|
14
|
+
# =============================================================================
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'needle/include-exclude'
|
18
|
+
|
19
|
+
module Needle
|
20
|
+
|
21
|
+
# A LoggingInterceptor is an interceptor object that logs method
|
22
|
+
# invocations and exceptions. It includes the IncludeExclude functionality.
|
23
|
+
class LoggingInterceptor
|
24
|
+
include IncludeExclude
|
25
|
+
|
26
|
+
# Create a new LoggingInterceptor for the given service point, with the
|
27
|
+
# given list of include and exclude patterns. The logger object will be
|
28
|
+
# created as well, named with the service point's full name.
|
29
|
+
def initialize( point, parms )
|
30
|
+
@log = point.container.logs.get( point.fullname )
|
31
|
+
|
32
|
+
@includes = build_map( parms[ :include ] )
|
33
|
+
@excludes = build_map( parms[ :exclude ] )
|
34
|
+
end
|
35
|
+
|
36
|
+
# Processes a method invocation context. If the current log has debugging
|
37
|
+
# activated, and the requested method is not excluded by the
|
38
|
+
# interceptor's exclude and include patterns, then a message will be
|
39
|
+
# written for the method's invocation, its return code, and any exception
|
40
|
+
# that is thrown.
|
41
|
+
def process( chain, context )
|
42
|
+
if @log.debug? && match( context )
|
43
|
+
args = context.args.map { |i| i.inspect } .join( ', ' )
|
44
|
+
@log.debug "#{context.sym}( #{args} )"
|
45
|
+
|
46
|
+
begin
|
47
|
+
result = chain.process_next( context )
|
48
|
+
rescue Exception => e
|
49
|
+
@log.debug "#{context.sym} raised #{e.message.inspect} (#{e.class})"
|
50
|
+
raise
|
51
|
+
end
|
52
|
+
|
53
|
+
@log.debug "#{context.sym} => #{result.inspect}"
|
54
|
+
return result
|
55
|
+
else
|
56
|
+
return chain.process_next( context )
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#--
|
2
|
+
# =============================================================================
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# This source file is distributed as part of the Needle dependency injection
|
7
|
+
# library for Ruby. This file (and the library as a whole) may be used only as
|
8
|
+
# allowed by either the BSD license, or the Ruby license (or, by association
|
9
|
+
# with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
|
10
|
+
# distribution for the texts of these licenses.
|
11
|
+
# -----------------------------------------------------------------------------
|
12
|
+
# needle website : http://needle.rubyforge.org
|
13
|
+
# project website: http://rubyforge.org/projects/needle
|
14
|
+
# =============================================================================
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'needle/models/proxy'
|
18
|
+
|
19
|
+
module Needle
|
20
|
+
module Models
|
21
|
+
|
22
|
+
# The definition of the "prototype-deferred" lifecycle service model.
|
23
|
+
# This will result in deferred instantiation of the requested service,
|
24
|
+
# with a new instance being returned every time #instance is invoked.
|
25
|
+
class PrototypeDeferred
|
26
|
+
|
27
|
+
# Create a new PrototypeDeferred service model.
|
28
|
+
def initialize( container, opts={}, &callback )
|
29
|
+
@container = container
|
30
|
+
@callback = callback
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return a new Proxy instance that wraps the container and callback
|
34
|
+
# references given when the service model was instantiated.
|
35
|
+
def instance
|
36
|
+
Proxy.new( @container, &@callback )
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#--
|
2
|
+
# =============================================================================
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# This source file is distributed as part of the Needle dependency injection
|
7
|
+
# library for Ruby. This file (and the library as a whole) may be used only as
|
8
|
+
# allowed by either the BSD license, or the Ruby license (or, by association
|
9
|
+
# with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
|
10
|
+
# distribution for the texts of these licenses.
|
11
|
+
# -----------------------------------------------------------------------------
|
12
|
+
# needle website : http://needle.rubyforge.org
|
13
|
+
# project website: http://rubyforge.org/projects/needle
|
14
|
+
# =============================================================================
|
15
|
+
#++
|
16
|
+
|
17
|
+
module Needle
|
18
|
+
module Models
|
19
|
+
|
20
|
+
# The definition of the "prototype" lifecycle service model. This will
|
21
|
+
# result in immediate instantiation of the requested service, with a new
|
22
|
+
# instance being returned every time #instance is invoked.
|
23
|
+
class Prototype
|
24
|
+
|
25
|
+
# Create a new instance of the Prototype service model.
|
26
|
+
def initialize( container, opts={}, &callback )
|
27
|
+
@container = container
|
28
|
+
@callback = callback
|
29
|
+
end
|
30
|
+
|
31
|
+
# Invoke the callback that was given when the service model was
|
32
|
+
# instantiated, passing the service model's container reference.
|
33
|
+
def instance
|
34
|
+
@callback.call( @container )
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|