mongrel2 0.0.1
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.tar.gz.sig +0 -0
- data/.gemtest +0 -0
- data/History.rdoc +4 -0
- data/Manifest.txt +66 -0
- data/README.rdoc +169 -0
- data/Rakefile +77 -0
- data/bin/m2sh.rb +600 -0
- data/data/mongrel2/bootstrap.html +25 -0
- data/data/mongrel2/config.sql +84 -0
- data/data/mongrel2/mimetypes.sql +855 -0
- data/examples/README.txt +6 -0
- data/examples/config.rb +54 -0
- data/examples/helloworld-handler.rb +31 -0
- data/examples/request-dumper.rb +45 -0
- data/examples/request-dumper.tmpl +71 -0
- data/examples/run +17 -0
- data/lib/mongrel2.rb +62 -0
- data/lib/mongrel2/config.rb +212 -0
- data/lib/mongrel2/config/directory.rb +78 -0
- data/lib/mongrel2/config/dsl.rb +206 -0
- data/lib/mongrel2/config/handler.rb +124 -0
- data/lib/mongrel2/config/host.rb +88 -0
- data/lib/mongrel2/config/log.rb +48 -0
- data/lib/mongrel2/config/mimetype.rb +15 -0
- data/lib/mongrel2/config/proxy.rb +15 -0
- data/lib/mongrel2/config/route.rb +51 -0
- data/lib/mongrel2/config/server.rb +58 -0
- data/lib/mongrel2/config/setting.rb +15 -0
- data/lib/mongrel2/config/statistic.rb +23 -0
- data/lib/mongrel2/connection.rb +212 -0
- data/lib/mongrel2/constants.rb +159 -0
- data/lib/mongrel2/control.rb +165 -0
- data/lib/mongrel2/exceptions.rb +59 -0
- data/lib/mongrel2/handler.rb +309 -0
- data/lib/mongrel2/httprequest.rb +51 -0
- data/lib/mongrel2/httpresponse.rb +187 -0
- data/lib/mongrel2/jsonrequest.rb +43 -0
- data/lib/mongrel2/logging.rb +241 -0
- data/lib/mongrel2/mixins.rb +143 -0
- data/lib/mongrel2/request.rb +148 -0
- data/lib/mongrel2/response.rb +74 -0
- data/lib/mongrel2/table.rb +216 -0
- data/lib/mongrel2/xmlrequest.rb +36 -0
- data/spec/lib/constants.rb +237 -0
- data/spec/lib/helpers.rb +246 -0
- data/spec/lib/matchers.rb +50 -0
- data/spec/mongrel2/config/directory_spec.rb +91 -0
- data/spec/mongrel2/config/dsl_spec.rb +218 -0
- data/spec/mongrel2/config/handler_spec.rb +118 -0
- data/spec/mongrel2/config/host_spec.rb +30 -0
- data/spec/mongrel2/config/log_spec.rb +95 -0
- data/spec/mongrel2/config/proxy_spec.rb +30 -0
- data/spec/mongrel2/config/route_spec.rb +83 -0
- data/spec/mongrel2/config/server_spec.rb +84 -0
- data/spec/mongrel2/config/setting_spec.rb +30 -0
- data/spec/mongrel2/config/statistic_spec.rb +30 -0
- data/spec/mongrel2/config_spec.rb +111 -0
- data/spec/mongrel2/connection_spec.rb +172 -0
- data/spec/mongrel2/constants_spec.rb +32 -0
- data/spec/mongrel2/control_spec.rb +192 -0
- data/spec/mongrel2/handler_spec.rb +261 -0
- data/spec/mongrel2/httpresponse_spec.rb +232 -0
- data/spec/mongrel2/logging_spec.rb +76 -0
- data/spec/mongrel2/mixins_spec.rb +62 -0
- data/spec/mongrel2/request_spec.rb +157 -0
- data/spec/mongrel2/response_spec.rb +81 -0
- data/spec/mongrel2/table_spec.rb +176 -0
- data/spec/mongrel2_spec.rb +34 -0
- metadata +294 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'mongrel2/request' unless defined?( Mongrel2::Request )
|
4
|
+
require 'mongrel2/mixins'
|
5
|
+
require 'mongrel2/httpresponse'
|
6
|
+
|
7
|
+
|
8
|
+
# The Mongrel2 HTTP Request class. Instances of this class represent an HTTP request from
|
9
|
+
# a Mongrel2 server.
|
10
|
+
class Mongrel2::HTTPRequest < Mongrel2::Request
|
11
|
+
include Mongrel2::Loggable
|
12
|
+
|
13
|
+
# HTTP verbs from RFC2616
|
14
|
+
HANDLED_HTTP_METHODS = [ :OPTIONS, :GET, :HEAD, :POST, :PUT, :DELETE, :TRACE, :CONNECT ]
|
15
|
+
|
16
|
+
register_request_type( self, *HANDLED_HTTP_METHODS )
|
17
|
+
|
18
|
+
|
19
|
+
### Create a Mongrel2::HTTPResponse that corresponds to the receiver.
|
20
|
+
def response
|
21
|
+
return Mongrel2::HTTPResponse.from_request( self )
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
### Return +true+ if the request is an HTTP/1.1 request and its
|
26
|
+
### 'Connection' header indicates that the connection should stay
|
27
|
+
### open.
|
28
|
+
def keepalive?
|
29
|
+
unless self.headers[:version] == 'HTTP/1.1'
|
30
|
+
self.log.debug "Not an http/1.1 request: not persistent"
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
conn_header = self.headers[:connection]
|
34
|
+
if !conn_header
|
35
|
+
self.log.debug "No Connection header: assume persistence"
|
36
|
+
return true
|
37
|
+
end
|
38
|
+
|
39
|
+
if conn_header.split( /\s*,\s*/ ).include?( 'close' )
|
40
|
+
self.log.debug "Connection: close header."
|
41
|
+
return false
|
42
|
+
else
|
43
|
+
self.log.debug "Connection header didn't contain 'close': assume persistence"
|
44
|
+
return true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end # class Mongrel2::HTTPRequest
|
49
|
+
|
50
|
+
# vim: set nosta noet ts=4 sw=4:
|
51
|
+
|
@@ -0,0 +1,187 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
require 'mongrel2/response' unless defined?( Mongrel2::Response )
|
6
|
+
require 'mongrel2/mixins'
|
7
|
+
require 'mongrel2/constants'
|
8
|
+
|
9
|
+
|
10
|
+
# The Mongrel2 HTTP Response class.
|
11
|
+
class Mongrel2::HTTPResponse < Mongrel2::Response
|
12
|
+
include Mongrel2::Loggable,
|
13
|
+
Mongrel2::Constants
|
14
|
+
|
15
|
+
# The format for building valid HTTP responses
|
16
|
+
STATUS_LINE_FORMAT = "HTTP/1.1 %03d %s".freeze
|
17
|
+
|
18
|
+
# The default status
|
19
|
+
DEFAULT_HTTP_STATUS = 200
|
20
|
+
|
21
|
+
# A network End-Of-Line
|
22
|
+
EOL = "\r\n".freeze
|
23
|
+
|
24
|
+
# The default content type
|
25
|
+
DEFAULT_CONTENT_TYPE = 'application/octet-stream'.freeze
|
26
|
+
|
27
|
+
|
28
|
+
### Set up a few things specific to HTTP responses
|
29
|
+
def initialize( sender_id, conn_id, body='', headers={} ) # :notnew:
|
30
|
+
if body.is_a?( Hash )
|
31
|
+
headers = body
|
32
|
+
body = ''
|
33
|
+
end
|
34
|
+
|
35
|
+
super( sender_id, conn_id, body )
|
36
|
+
|
37
|
+
@headers = Mongrel2::Table.new( headers )
|
38
|
+
@status = nil
|
39
|
+
self.reset
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
######
|
44
|
+
public
|
45
|
+
######
|
46
|
+
|
47
|
+
# The response headers (a Mongrel2::Table)
|
48
|
+
attr_reader :headers
|
49
|
+
|
50
|
+
# The HTTP status code
|
51
|
+
attr_accessor :status
|
52
|
+
|
53
|
+
|
54
|
+
### Stringify the response
|
55
|
+
def to_s
|
56
|
+
return [
|
57
|
+
self.status_line,
|
58
|
+
self.header_data,
|
59
|
+
self.body
|
60
|
+
].join( "\r\n" )
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
### Send the response status to the client
|
65
|
+
def status_line
|
66
|
+
self.log.warn "Building status line for unset status" if self.status.nil?
|
67
|
+
|
68
|
+
st = self.status || DEFAULT_HTTP_STATUS
|
69
|
+
return STATUS_LINE_FORMAT % [ st, HTTP::STATUS_NAME[st] ]
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
### Returns true if the response is ready to be sent to the client.
|
74
|
+
def handled?
|
75
|
+
return ! @status.nil?
|
76
|
+
end
|
77
|
+
alias_method :is_handled?, :handled?
|
78
|
+
|
79
|
+
|
80
|
+
### Return the numeric category of the response's status code (1-5)
|
81
|
+
def status_category
|
82
|
+
return 0 if self.status.nil?
|
83
|
+
return (self.status / 100).ceil
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
### Return true if response is in the 1XX range
|
88
|
+
def status_is_informational?
|
89
|
+
return self.status_category == 1
|
90
|
+
end
|
91
|
+
|
92
|
+
### Return true if response is in the 2XX range
|
93
|
+
def status_is_successful?
|
94
|
+
return self.status_category == 2
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
### Return true if response is in the 3XX range
|
99
|
+
def status_is_redirect?
|
100
|
+
return self.status_category == 3
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
### Return true if response is in the 4XX range
|
105
|
+
def status_is_clienterror?
|
106
|
+
return self.status_category == 4
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
### Return true if response is in the 5XX range
|
111
|
+
def status_is_servererror?
|
112
|
+
return self.status_category == 5
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
### Return the current response Content-Type.
|
117
|
+
def content_type
|
118
|
+
return self.headers[ :content_type ]
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
### Set the current response Content-Type.
|
123
|
+
def content_type=( type )
|
124
|
+
return self.headers[ :content_type ] = type
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
### Clear any existing headers and body and restore them to their defaults
|
129
|
+
def reset
|
130
|
+
@headers.clear
|
131
|
+
@headers[:server] = Mongrel2.version_string( true )
|
132
|
+
@status = nil
|
133
|
+
@body = ''
|
134
|
+
|
135
|
+
return true
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
### Return the current response header as a valid HTTP string.
|
140
|
+
def header_data
|
141
|
+
self.headers[:date] ||= Time.now.httpdate
|
142
|
+
self.headers[:content_length] ||= self.get_content_length
|
143
|
+
|
144
|
+
return self.headers.to_s
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
### Get the length of the body, either by calling its #length method if it has
|
149
|
+
### one, or using #seek and #tell if it implements those. If neither of those are
|
150
|
+
### possible, an exception is raised.
|
151
|
+
def get_content_length
|
152
|
+
if @body.respond_to?( :length )
|
153
|
+
return @body.length
|
154
|
+
elsif @body.respond_to?( :seek ) && @body.respond_to?( :tell )
|
155
|
+
starting_pos = @body.tell
|
156
|
+
@body.seek( 0, IO::SEEK_END )
|
157
|
+
length = @body.tell - starting_pos
|
158
|
+
@body.seek( starting_pos, IO::SEEK_SET )
|
159
|
+
|
160
|
+
return length
|
161
|
+
else
|
162
|
+
raise Mongrel2::ResponseError,
|
163
|
+
"No way to calculate the content length of the response (a %s)." %
|
164
|
+
[ @body.class.name ]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
### Set the Connection header to allow pipelined HTTP.
|
170
|
+
def keepalive=( value )
|
171
|
+
self.headers[:connection] = value ? 'keep-alive' : 'close'
|
172
|
+
end
|
173
|
+
alias_method :pipelining_enabled=, :keepalive=
|
174
|
+
|
175
|
+
|
176
|
+
### Returns +true+ if the response has pipelining enabled.
|
177
|
+
def keepalive?
|
178
|
+
ka_header = self.headers[:connection]
|
179
|
+
return !ka_header.nil? && ka_header =~ /keep-alive/i
|
180
|
+
return false
|
181
|
+
end
|
182
|
+
alias_method :pipelining_enabled?, :keepalive?
|
183
|
+
|
184
|
+
end # class Mongrel2::Response
|
185
|
+
|
186
|
+
# vim: set nosta noet ts=4 sw=4:
|
187
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'yajl'
|
4
|
+
|
5
|
+
require 'mongrel2/request' unless defined?( Mongrel2::Request )
|
6
|
+
require 'mongrel2/mixins'
|
7
|
+
|
8
|
+
|
9
|
+
# The Mongrel2 JSON Request class. Instances of this class represent a JSSocket request from
|
10
|
+
# a Mongrel2 server.
|
11
|
+
class Mongrel2::JSONRequest < Mongrel2::Request
|
12
|
+
include Mongrel2::Loggable
|
13
|
+
|
14
|
+
register_request_type( self, :JSON )
|
15
|
+
|
16
|
+
|
17
|
+
### Parse the body as JSON.
|
18
|
+
def initialize( sender_id, conn_id, path, headers, body, raw=nil )
|
19
|
+
super
|
20
|
+
self.log.debug "Parsing JSON request body"
|
21
|
+
@data = Yajl.load( body )
|
22
|
+
self.log.debug " body is: %p" % [ @data ]
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
######
|
27
|
+
public
|
28
|
+
######
|
29
|
+
|
30
|
+
# The parsed request data
|
31
|
+
attr_reader :data
|
32
|
+
|
33
|
+
|
34
|
+
### Returns +true+ if the request is a special Mongrel2 'disconnect'
|
35
|
+
### notification.
|
36
|
+
def is_disconnect?
|
37
|
+
return true if self.data['type'] == 'disconnect'
|
38
|
+
end
|
39
|
+
|
40
|
+
end # class Mongrel2::JSONRequest
|
41
|
+
|
42
|
+
# vim: set nosta noet ts=4 sw=4:
|
43
|
+
|
@@ -0,0 +1,241 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
require 'mongrel2' unless defined?( Mongrel2 )
|
7
|
+
require 'mongrel2/mixins'
|
8
|
+
|
9
|
+
|
10
|
+
# A mixin that adds a logging subsystem to the extended object.
|
11
|
+
module Mongrel2::Logging
|
12
|
+
|
13
|
+
### Logging
|
14
|
+
# Log levels
|
15
|
+
LOG_LEVELS = {
|
16
|
+
'debug' => Logger::DEBUG,
|
17
|
+
'info' => Logger::INFO,
|
18
|
+
'warn' => Logger::WARN,
|
19
|
+
'error' => Logger::ERROR,
|
20
|
+
'fatal' => Logger::FATAL,
|
21
|
+
}.freeze
|
22
|
+
LOG_LEVEL_NAMES = LOG_LEVELS.invert.freeze
|
23
|
+
|
24
|
+
|
25
|
+
### Inclusion hook
|
26
|
+
def self::extended( mod )
|
27
|
+
super
|
28
|
+
|
29
|
+
class << mod
|
30
|
+
# the log formatter that will be used when the logging subsystem is reset
|
31
|
+
attr_accessor :default_log_formatter
|
32
|
+
|
33
|
+
# the logger that will be used when the logging subsystem is reset
|
34
|
+
attr_accessor :default_logger
|
35
|
+
|
36
|
+
# the logger that's currently in effect
|
37
|
+
attr_accessor :logger
|
38
|
+
alias_method :log, :logger
|
39
|
+
alias_method :log=, :logger=
|
40
|
+
end
|
41
|
+
|
42
|
+
mod.default_logger = mod.logger = Logger.new( $stderr )
|
43
|
+
mod.default_logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
|
44
|
+
mod.default_log_formatter = Mongrel2::Logging::Formatter.new( mod.default_logger )
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
### Reset the global logger object to the default
|
49
|
+
def reset_logger
|
50
|
+
self.logger = self.default_logger
|
51
|
+
self.logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
|
52
|
+
self.logger.formatter = self.default_log_formatter
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
### Returns +true+ if the global logger has not been set to something other than
|
57
|
+
### the default one.
|
58
|
+
def using_default_logger?
|
59
|
+
return self.logger == self.default_logger
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# A alternate formatter for Logger instances.
|
64
|
+
class Formatter < Logger::Formatter
|
65
|
+
|
66
|
+
# The format to output unless debugging is turned on
|
67
|
+
DEFAULT_FORMAT = "[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"
|
68
|
+
|
69
|
+
# The format to output if debugging is turned on
|
70
|
+
DEFAULT_DEBUG_FORMAT = "[%1$s.%2$06d %3$d/%4$s] %5$5s {%6$s} -- %7$s\n"
|
71
|
+
|
72
|
+
|
73
|
+
### Initialize the formatter with a reference to the logger so it can check for log level.
|
74
|
+
def initialize( logger, format=DEFAULT_FORMAT, debug=DEFAULT_DEBUG_FORMAT ) # :notnew:
|
75
|
+
@logger = logger
|
76
|
+
@format = format
|
77
|
+
@debug_format = debug
|
78
|
+
|
79
|
+
super()
|
80
|
+
end
|
81
|
+
|
82
|
+
######
|
83
|
+
public
|
84
|
+
######
|
85
|
+
|
86
|
+
# The Logger object associated with the formatter
|
87
|
+
attr_accessor :logger
|
88
|
+
|
89
|
+
# The logging format string
|
90
|
+
attr_accessor :format
|
91
|
+
|
92
|
+
# The logging format string that's used when outputting in debug mode
|
93
|
+
attr_accessor :debug_format
|
94
|
+
|
95
|
+
|
96
|
+
### Log using either the DEBUG_FORMAT if the associated logger is at ::DEBUG level or
|
97
|
+
### using FORMAT if it's anything less verbose.
|
98
|
+
def call( severity, time, progname, msg )
|
99
|
+
args = [
|
100
|
+
time.strftime( '%Y-%m-%d %H:%M:%S' ), # %1$s
|
101
|
+
time.usec, # %2$d
|
102
|
+
Process.pid, # %3$d
|
103
|
+
Thread.current == Thread.main ? 'main' : Thread.object_id, # %4$s
|
104
|
+
severity, # %5$s
|
105
|
+
progname, # %6$s
|
106
|
+
msg # %7$s
|
107
|
+
]
|
108
|
+
|
109
|
+
if @logger.level == Logger::DEBUG
|
110
|
+
return self.debug_format % args
|
111
|
+
else
|
112
|
+
return self.format % args
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end # class LogFormatter
|
116
|
+
|
117
|
+
|
118
|
+
# A ANSI-colorized formatter for Logger instances.
|
119
|
+
class ColorFormatter < Logger::Formatter
|
120
|
+
extend Mongrel2::ANSIColorUtilities
|
121
|
+
|
122
|
+
# Color settings
|
123
|
+
LEVEL_FORMATS = {
|
124
|
+
:debug => colorize( :bold, :black ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s {%6$s} -- %7$s\n"},
|
125
|
+
:info => colorize( :normal ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
|
126
|
+
:warn => colorize( :bold, :yellow ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
|
127
|
+
:error => colorize( :red ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
|
128
|
+
:fatal => colorize( :bold, :red ) {"[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"},
|
129
|
+
}
|
130
|
+
|
131
|
+
|
132
|
+
### Initialize the formatter with a reference to the logger so it can check for log level.
|
133
|
+
def initialize( logger, settings={} ) # :notnew:
|
134
|
+
settings = LEVEL_FORMATS.merge( settings )
|
135
|
+
|
136
|
+
@logger = logger
|
137
|
+
@settings = settings
|
138
|
+
|
139
|
+
super()
|
140
|
+
end
|
141
|
+
|
142
|
+
######
|
143
|
+
public
|
144
|
+
######
|
145
|
+
|
146
|
+
# The Logger object associated with the formatter
|
147
|
+
attr_accessor :logger
|
148
|
+
|
149
|
+
# The formats, by level
|
150
|
+
attr_accessor :settings
|
151
|
+
|
152
|
+
|
153
|
+
### Log using the format associated with the severity
|
154
|
+
def call( severity, time, progname, msg )
|
155
|
+
args = [
|
156
|
+
time.strftime( '%Y-%m-%d %H:%M:%S' ), # %1$s
|
157
|
+
time.usec, # %2$d
|
158
|
+
Process.pid, # %3$d
|
159
|
+
Thread.current == Thread.main ? 'main' : Thread.object_id, # %4$s
|
160
|
+
severity, # %5$s
|
161
|
+
progname, # %6$s
|
162
|
+
msg # %7$s
|
163
|
+
]
|
164
|
+
|
165
|
+
return self.settings[ severity.downcase.to_sym ] % args
|
166
|
+
end
|
167
|
+
end # class LogFormatter
|
168
|
+
|
169
|
+
|
170
|
+
# An alternate formatter for Logger instances that outputs +div+ HTML
|
171
|
+
# fragments.
|
172
|
+
class HtmlFormatter < Logger::Formatter
|
173
|
+
|
174
|
+
# The default HTML fragment that'll be used as the template for each log message.
|
175
|
+
HTML_LOG_FORMAT = %q{
|
176
|
+
<div class="log-message %5$s">
|
177
|
+
<span class="log-time">%1$s.%2$06d</span>
|
178
|
+
[
|
179
|
+
<span class="log-pid">%3$d</span>
|
180
|
+
/
|
181
|
+
<span class="log-tid">%4$s</span>
|
182
|
+
]
|
183
|
+
<span class="log-level">%5$s</span>
|
184
|
+
:
|
185
|
+
<span class="log-name">%6$s</span>
|
186
|
+
<span class="log-message-text">%7$s</span>
|
187
|
+
</div>
|
188
|
+
}
|
189
|
+
|
190
|
+
### Override the logging formats with ones that generate HTML fragments
|
191
|
+
def initialize( logger, format=HTML_LOG_FORMAT ) # :notnew:
|
192
|
+
@logger = logger
|
193
|
+
@format = format
|
194
|
+
super()
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
######
|
199
|
+
public
|
200
|
+
######
|
201
|
+
|
202
|
+
# The HTML fragment that will be used as a format() string for the log
|
203
|
+
attr_accessor :format
|
204
|
+
|
205
|
+
|
206
|
+
### Return a log message composed out of the arguments formatted using the
|
207
|
+
### formatter's format string
|
208
|
+
def call( severity, time, progname, msg )
|
209
|
+
args = [
|
210
|
+
time.strftime( '%Y-%m-%d %H:%M:%S' ), # %1$s
|
211
|
+
time.usec, # %2$d
|
212
|
+
Process.pid, # %3$d
|
213
|
+
Thread.current == Thread.main ? 'main' : Thread.object_id, # %4$s
|
214
|
+
severity.downcase, # %5$s
|
215
|
+
progname, # %6$s
|
216
|
+
html_escape( msg ).gsub(/\n/, '<br />') # %7$s
|
217
|
+
]
|
218
|
+
|
219
|
+
return self.format % args
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
#######
|
224
|
+
private
|
225
|
+
#######
|
226
|
+
|
227
|
+
### Return a copy of the specified +string+ with HTML special characters escaped as
|
228
|
+
### HTML entities.
|
229
|
+
def html_escape( string )
|
230
|
+
return string.
|
231
|
+
gsub( /&/, '&' ).
|
232
|
+
gsub( /</, '<' ).
|
233
|
+
gsub( />/, '>' )
|
234
|
+
end
|
235
|
+
|
236
|
+
end # class HtmlLogFormatter
|
237
|
+
|
238
|
+
end # module Mongrel2
|
239
|
+
|
240
|
+
# vim: set nosta noet ts=4 sw=4:
|
241
|
+
|