mongrel2 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|