racknga 0.9.2 → 0.9.4

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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -12
  3. data/README.textile +9 -8
  4. data/Rakefile +19 -272
  5. data/doc/text/news.textile +20 -0
  6. data/lib/racknga/access_log_parser.rb +147 -0
  7. data/lib/racknga/api-keys.rb +40 -0
  8. data/lib/racknga/cache_database.rb +15 -5
  9. data/lib/racknga/exception_mail_notifier.rb +6 -6
  10. data/lib/racknga/log_database.rb +2 -1
  11. data/lib/racknga/log_entry.rb +85 -0
  12. data/lib/racknga/middleware/auth/api-key.rb +95 -0
  13. data/lib/racknga/middleware/cache.rb +22 -8
  14. data/lib/racknga/middleware/deflater.rb +13 -3
  15. data/lib/racknga/middleware/exception_notifier.rb +1 -2
  16. data/lib/racknga/middleware/instance_name.rb +92 -9
  17. data/lib/racknga/middleware/jsonp.rb +25 -5
  18. data/lib/racknga/middleware/log.rb +8 -3
  19. data/lib/racknga/middleware/nginx_raw_uri.rb +65 -0
  20. data/lib/racknga/middleware/range.rb +1 -1
  21. data/lib/racknga/reverse_line_reader.rb +1 -1
  22. data/lib/racknga/utils.rb +1 -1
  23. data/lib/racknga/version.rb +2 -2
  24. data/lib/racknga.rb +4 -2
  25. data/munin/plugins/passenger_application_ +61 -47
  26. data/munin/plugins/passenger_status +64 -33
  27. data/test/racknga-test-utils.rb +2 -1
  28. data/test/run-test.rb +1 -3
  29. data/test/{test-nginx-log-parser.rb → test-access-log-parser.rb} +115 -26
  30. data/test/test-api-keys.rb +80 -0
  31. data/test/test-middleware-auth-api-key.rb +144 -0
  32. data/test/test-middleware-cache.rb +1 -1
  33. data/test/test-middleware-instance-name.rb +50 -16
  34. data/test/test-middleware-jsonp.rb +14 -2
  35. data/test/test-middleware-nginx-raw-uri.rb +70 -0
  36. data/test/test-middleware-range.rb +1 -1
  37. metadata +159 -155
  38. data/lib/racknga/nginx_access_log_parser.rb +0 -139
  39. data/lib/racknga/will_paginate.rb +0 -48
@@ -14,7 +14,7 @@
14
14
  #
15
15
  # You should have received a copy of the GNU Lesser General Public
16
16
  # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
19
19
  require 'time'
20
20
  require 'net/smtp'
@@ -132,10 +132,10 @@ EOE
132
132
  end
133
133
 
134
134
  def send_summaries
135
- subject = "summaries of #{@summaries.size} notifications"
136
- mail = create_mail(:subject => subject,
137
- :body => report_body)
138
- send(mail)
135
+ subject = "summaries of #{@summaries.size} notifications"
136
+ mail = create_mail(:subject => subject,
137
+ :body => report_body)
138
+ send(mail)
139
139
  end
140
140
 
141
141
  def report_body
@@ -149,7 +149,7 @@ Timestamp: #{Time.now.rfc2822}
149
149
  --
150
150
  URL: #{request.url}
151
151
  --
152
- #{exception.class}: #{exception}
152
+ #{exception.class}: #{exception.message}
153
153
  EOB
154
154
  end
155
155
 
@@ -14,7 +14,7 @@
14
14
  #
15
15
  # You should have received a copy of the GNU Lesser General Public
16
16
  # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
19
19
  require 'fileutils'
20
20
 
@@ -95,6 +95,7 @@ module Racknga
95
95
  table.reference("path", "Paths")
96
96
  table.reference("user_agent", "UserAgents")
97
97
  table.float("runtime")
98
+ table.float("request_time")
98
99
  table.short_text("message", :compress => :zlib)
99
100
  end
100
101
 
@@ -0,0 +1,85 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2011 Ryo Onodera <onodera@clear-code.com>
4
+ # Copyright (C) 2011 Kouhei Sutou <kou@clear-code.com>
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License as published by the Free Software Foundation; either
9
+ # version 2.1 of the License, or (at your option) any later version.
10
+ #
11
+ # This library is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public
17
+ # License along with this library; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+
20
+ module Racknga
21
+ class LogEntry
22
+ ATTRIBUTES = [
23
+ :remote_address,
24
+ :remote_user,
25
+ :time_local,
26
+ :runtime,
27
+ :request_time,
28
+ :request,
29
+ :status,
30
+ :body_bytes_sent,
31
+ :http_referer,
32
+ :http_user_agent,
33
+ ]
34
+
35
+ attr_reader(*ATTRIBUTES)
36
+ def initialize(options=nil)
37
+ options ||= {}
38
+ @remote_address = options[:remote_address]
39
+ @remote_user = normalize_string_value(options[:remote_user])
40
+ @time_local = options[:time_local] || Time.at(0)
41
+ @runtime = normalize_float_value(options[:runtime])
42
+ @request_time = normalize_float_value(options[:request_time])
43
+ @request = options[:request]
44
+ @status = options[:status]
45
+ @body_bytes_sent = normalize_int_value(options[:body_bytes_sent])
46
+ @http_referer = normalize_string_value(options[:http_referer])
47
+ @http_user_agent = normalize_string_value(options[:http_user_agent])
48
+ end
49
+
50
+ def attributes
51
+ ATTRIBUTES.collect do |attribute|
52
+ __send__(attribute)
53
+ end
54
+ end
55
+
56
+ def ==(other)
57
+ other.is_a?(self.class) and attributes == other.attributes
58
+ end
59
+
60
+ private
61
+ def normalize_string_value(value)
62
+ if value.nil? or value == "-"
63
+ nil
64
+ else
65
+ value.to_s
66
+ end
67
+ end
68
+
69
+ def normalize_float_value(value)
70
+ if value.nil?
71
+ value
72
+ else
73
+ value.to_f
74
+ end
75
+ end
76
+
77
+ def normalize_int_value(value)
78
+ if value.nil? or value == "-"
79
+ nil
80
+ else
81
+ value.to_i
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,95 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012 Haruka Yoshihara <yoshihara@clear-code.com>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+
19
+ module Racknga
20
+ module Middleware
21
+ module Auth
22
+ # This is a middleware that provides an authentication of API key.
23
+ # This middleware checks whether a URL requests API and
24
+ # whether API key in its request is authorized.
25
+ # When a URL requests API with unauthorized API key,
26
+ # this middleware returns json response with :error_message.
27
+ # Racknga::APIKeys is used as authorized API keys.
28
+ # Usage:
29
+ # require 'racknga'
30
+ #
31
+ # authorized_api_keys =
32
+ # Racknga::APIKeys.new("api-query-parameter", ["key1", "key2"])
33
+ # error_response =
34
+ # api_key_options = {
35
+ # :api_url_prefixes => ["/api"],
36
+ # :authorized_api_keys => authorized_api_keys,
37
+ # :error_response => {"error" => "this api key is not authorized"}
38
+ # }
39
+ # use Racknga::Middleware::Auth::APIKey, api_key_options
40
+ # run YourApplication
41
+ class APIKey
42
+ def initialize(application, options={})
43
+ @application = application
44
+ @authorized_api_keys = options[:authorized_api_keys]
45
+ url_prefixes_option = options[:api_url_prefixes]
46
+ @api_url_prefixes = url_prefixes_option.collect do |url|
47
+ /\A#{Regexp.escape(url)}/
48
+ end
49
+ @error_response = options[:error_response].to_json
50
+ @disable_authorization = options[:disable_authorization]
51
+ end
52
+
53
+ # For Rack.
54
+ def call(environment)
55
+ if api_url?(environment) and not authorized?(environment)
56
+ unauthorized_access_response
57
+ else
58
+ @application.call(environment)
59
+ end
60
+ end
61
+
62
+ private
63
+ def api_url?(environment)
64
+ @api_url_prefixes.any? do |prefix|
65
+ environment["PATH_INFO"] =~ prefix
66
+ end
67
+ end
68
+
69
+ def authorized?(environment)
70
+ if disable_authorization?
71
+ true
72
+ else
73
+ if @authorized_api_keys
74
+ @authorized_api_keys.include?(environment)
75
+ else
76
+ false
77
+ end
78
+ end
79
+ end
80
+
81
+ def disable_authorization?
82
+ @disable_authorization
83
+ end
84
+
85
+ def unauthorized_access_response
86
+ [
87
+ 401,
88
+ {"Content-Type" => "application/json; charset=utf-8"},
89
+ [@error_response],
90
+ ]
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -14,10 +14,11 @@
14
14
  #
15
15
  # You should have received a copy of the GNU Lesser General Public
16
16
  # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
19
19
  require 'digest'
20
20
  require 'yaml'
21
+ require 'zlib'
21
22
  require 'racknga/cache_database'
22
23
 
23
24
  module Racknga
@@ -168,13 +169,15 @@ module Racknga
168
169
  now = Time.now
169
170
  headers = Rack::Utils::HeaderHash.new(headers)
170
171
  headers["Last-Modified"] ||= now.httpdate
171
- stringified_body = ''
172
+ encoded_body = ''.force_encoding("ASCII-8BIT")
173
+ deflater = ::Zlib::Deflate.new
172
174
  body.each do |data|
173
- stringified_body << data
175
+ encoded_body << deflater.deflate(data)
174
176
  end
177
+ body.close if body.respond_to?(:close)
178
+ encoded_body << deflater.finish
175
179
  headers = headers.to_hash
176
180
  encoded_headers = headers.to_yaml
177
- encoded_body = stringified_body.force_encoding("ASCII-8BIT")
178
181
  cache[key] = {
179
182
  :status => status,
180
183
  :headers => encoded_headers,
@@ -183,7 +186,7 @@ module Racknga
183
186
  :age => age,
184
187
  :created_at => now,
185
188
  }
186
- body = [stringified_body]
189
+ body = Inflater.new(encoded_body)
187
190
  log("store", request)
188
191
  [status, headers, body]
189
192
  end
@@ -199,7 +202,7 @@ module Racknga
199
202
  end
200
203
 
201
204
  log("hit", request)
202
- [status, YAML.load(headers), [body]]
205
+ [status, YAML.load(headers), Inflater.new(body)]
203
206
  end
204
207
 
205
208
  def compute_checksum(status, encoded_headers, encoded_body)
@@ -221,12 +224,23 @@ module Racknga
221
224
  def log(tag, request)
222
225
  return unless Middleware.const_defined?(:Log)
223
226
  env = request.env
224
- logger = env[Middleware::Log::LOGGER_KEY]
227
+ logger = env[Middleware::Log::LOGGER]
225
228
  return if logger.nil?
226
- start_time = env[START_TIME_KEY]
229
+ start_time = env[START_TIME]
227
230
  runtime = Time.now - start_time
228
231
  logger.log("cache-#{tag}", request.fullpath, :runtime => runtime)
229
232
  end
233
+
234
+ # @private
235
+ class Inflater
236
+ def initialize(deflated_string)
237
+ @deflated_string = deflated_string
238
+ end
239
+
240
+ def each
241
+ yield ::Zlib::Inflate.inflate(@deflated_string)
242
+ end
243
+ end
230
244
  end
231
245
  end
232
246
  end
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2010 Kouhei Sutou <kou@clear-code.com>
3
+ # Copyright (C) 2010-2011 Kouhei Sutou <kou@clear-code.com>
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
@@ -14,7 +14,7 @@
14
14
  #
15
15
  # You should have received a copy of the GNU Lesser General Public
16
16
  # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
19
19
  module Racknga
20
20
  module Middleware
@@ -63,7 +63,7 @@ module Racknga
63
63
 
64
64
  # For Rack.
65
65
  def call(environment)
66
- if ie6?(environment)
66
+ if ie6?(environment) or not valid_accept_encoding?(environment)
67
67
  @application.call(environment)
68
68
  else
69
69
  @deflater.call(environment)
@@ -74,6 +74,16 @@ module Racknga
74
74
  def ie6?(environment)
75
75
  /MSIE 6.0;/ =~ (environment["HTTP_USER_AGENT"] || '')
76
76
  end
77
+
78
+ def valid_accept_encoding?(environment)
79
+ request = Rack::Request.new(environment)
80
+ begin
81
+ request.accept_encoding
82
+ true
83
+ rescue
84
+ false
85
+ end
86
+ end
77
87
  end
78
88
  end
79
89
  end
@@ -14,7 +14,7 @@
14
14
  #
15
15
  # You should have received a copy of the GNU Lesser General Public
16
16
  # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
19
19
  require 'racknga/exception_mail_notifier'
20
20
 
@@ -26,7 +26,6 @@ module Racknga
26
26
  #
27
27
  # Usage:
28
28
  # require "racknga"
29
- # require "racknga/middleware/exception_notifier"
30
29
  #
31
30
  # notifier_options = {
32
31
  # :subject_label => "[YourApplication]",
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
3
  # Copyright (C) 2011 Ryo Onodera <onodera@clear-code.com>
4
+ # Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
4
5
  #
5
6
  # This library is free software; you can redistribute it and/or
6
7
  # modify it under the terms of the GNU Lesser General Public
@@ -14,7 +15,7 @@
14
15
  #
15
16
  # You should have received a copy of the GNU Lesser General Public
16
17
  # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19
 
19
20
  module Racknga
20
21
  module Middleware
@@ -23,12 +24,18 @@ module Racknga
23
24
  # server when your Rack applications are deployed behind
24
25
  # load balancers.
25
26
  #
26
- # Usage:
27
+ # @example A simple usage
27
28
  # require "racknga"
28
29
  # use Racknga::Middleware::InstanceName
29
30
  # run YourApplication
30
31
  class InstanceName
31
32
  attr_reader :header
33
+ # @param [application] application rack application.
34
+ # @param [Hash] options options to create header.
35
+ # @option options [String] :header_name ("Responsed-By") header name.
36
+ # @option options [String] :application_name (application.class.name)
37
+ # application name.
38
+ # @option options [Integer] :version application version.
32
39
  def initialize(application, options={})
33
40
  @application = application
34
41
  @options = options
@@ -57,7 +64,14 @@ module Racknga
57
64
  end
58
65
 
59
66
  def revision
60
- `git describe --abbrev=7 HEAD`.strip # XXX be SCM-agonostic
67
+ case using_scm_name
68
+ when :git
69
+ `git describe --abbrev=7 HEAD`.strip
70
+ when :subversion
71
+ `LANG=C svn info | grep Revision`.strip
72
+ else
73
+ nil
74
+ end
61
75
  end
62
76
 
63
77
  def server
@@ -68,6 +82,21 @@ module Racknga
68
82
  `id --user --name`.strip
69
83
  end
70
84
 
85
+ def branch
86
+ case using_scm_name
87
+ when :git
88
+ git_branch_name
89
+ when :subversion
90
+ subversion_branch_name
91
+ else
92
+ nil
93
+ end
94
+ end
95
+
96
+ def ruby
97
+ RUBY_DESCRIPTION
98
+ end
99
+
71
100
  private
72
101
  DEFAULT_HEADER_NAME = "X-Responsed-By"
73
102
  def header_name
@@ -83,9 +112,10 @@ module Racknga
83
112
  def construct_header
84
113
  format_header(format_application_name(application_name),
85
114
  format_version(version),
86
- format_revision(revision),
115
+ format_revision(branch, revision),
87
116
  format_server(server),
88
- format_user(user))
117
+ format_user(user),
118
+ format_ruby(ruby))
89
119
  end
90
120
 
91
121
  def format_header(*arguments)
@@ -104,9 +134,15 @@ module Racknga
104
134
  end
105
135
  end
106
136
 
107
- def format_revision(revision)
137
+ def format_revision(branch, revision)
108
138
  format_if_possible(revision) do
109
- "(at #{revision})"
139
+ "(at #{revision}#{format_branch(branch)})"
140
+ end
141
+ end
142
+
143
+ def format_branch(branch)
144
+ format_if_possible(branch) do
145
+ " (#{branch})"
110
146
  end
111
147
  end
112
148
 
@@ -122,12 +158,59 @@ module Racknga
122
158
  end
123
159
  end
124
160
 
161
+ def format_ruby(ruby)
162
+ format_if_possible(ruby) do
163
+ "with #{ruby}"
164
+ end
165
+ end
166
+
125
167
  def format_if_possible(data)
126
168
  if data and (data.respond_to?(:to_s) and not data.to_s.empty?)
127
- result = yield
169
+ yield
170
+ else
171
+ nil
172
+ end
173
+ end
174
+
175
+ SVN_URL_KEY = /\AURL:.*/
176
+ SVN_REPOSITORY_ROOT_KEY = /\ARepository Root:.*/
177
+ SVN_KEY = /\A.*:/
178
+ SVN_BRANCHES_NAME = /\A\/branches\//
179
+ def subversion_branch_name
180
+ url = ""
181
+ repository_root = ""
182
+
183
+ `LANG=C svn info`.each_line do |line|
184
+ case line
185
+ when SVN_URL_KEY
186
+ url = line.sub(SVN_KEY, "").strip
187
+ when SVN_REPOSITORY_ROOT_KEY
188
+ repository_root = line.sub(SVN_KEY, "").strip
189
+ end
128
190
  end
191
+ base_path = url.sub(/#{Regexp.escape(repository_root)}/, "")
192
+ base_path.sub(SVN_BRANCHES_NAME, "")
193
+ end
129
194
 
130
- result
195
+ GIT_CURRENT_BRANCH_MARKER = /\A\* /
196
+ def git_branch_name
197
+ `git branch -a`.each_line do |line|
198
+ case line
199
+ when GIT_CURRENT_BRANCH_MARKER
200
+ return line.sub(GIT_CURRENT_BRANCH_MARKER, "").strip
201
+ end
202
+ end
203
+ nil
204
+ end
205
+
206
+ def using_scm_name
207
+ if File.exist?(".git")
208
+ :git
209
+ elsif File.exist?(".svn")
210
+ :subversion
211
+ else
212
+ nil
213
+ end
131
214
  end
132
215
  end
133
216
  end
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2010 Kouhei Sutou <kou@clear-code.com>
3
+ # Copyright (C) 2010-2012 Kouhei Sutou <kou@clear-code.com>
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
@@ -14,7 +14,7 @@
14
14
  #
15
15
  # You should have received a copy of the GNU Lesser General Public
16
16
  # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
19
19
  module Racknga
20
20
  module Middleware
@@ -91,7 +91,7 @@ module Racknga
91
91
  header_hash = Rack::Utils::HeaderHash.new(headers)
92
92
  return [status, headers, body] unless json_response?(header_hash)
93
93
  body = Writer.new(callback, body)
94
- update_content_type(header_hash)
94
+ update_header_hash(header_hash, body)
95
95
  [status, header_hash, body]
96
96
  end
97
97
 
@@ -122,26 +122,46 @@ module Racknga
122
122
  media_type == "text/javascript"
123
123
  end
124
124
 
125
+ def update_header_hash(header_hash, body)
126
+ update_content_type(header_hash)
127
+ update_content_length(header_hash, body)
128
+ end
129
+
125
130
  def update_content_type(header_hash)
126
131
  content_type = header_hash["Content-Type"]
127
132
  media_type, parameters = content_type.split(/\s*;\s*/, 2)
133
+ _ = media_type # FIXME: suppress a warning. :<
128
134
  # We should use application/javascript not
129
135
  # text/javascript when all IE <= 8 are deprecated. :<
130
136
  updated_content_type = ["text/javascript", parameters].compact.join("; ")
131
137
  header_hash["Content-Type"] = updated_content_type
132
138
  end
133
139
 
140
+ def update_content_length(header_hash, body)
141
+ return unless header_hash["Content-Length"]
142
+
143
+ content_length = header_hash["Content-Length"].to_i
144
+ updated_content_length = content_length + body.additional_content_length
145
+ header_hash["Content-Length"] = updated_content_length.to_s
146
+ end
147
+
134
148
  # @private
135
149
  class Writer
136
150
  def initialize(callback, body)
137
151
  @callback = callback
138
152
  @body = body
153
+ @header = "#{@callback}("
154
+ @footer = ");"
139
155
  end
140
156
 
141
157
  def each(&block)
142
- block.call("#{@callback}(")
158
+ block.call(@header)
143
159
  @body.each(&block)
144
- block.call(");")
160
+ block.call(@footer)
161
+ end
162
+
163
+ def additional_content_length
164
+ @header.bytesize + @footer.bytesize
145
165
  end
146
166
  end
147
167
  end
@@ -14,7 +14,7 @@
14
14
  #
15
15
  # You should have received a copy of the GNU Lesser General Public
16
16
  # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
19
19
  require 'racknga/log_database'
20
20
 
@@ -70,9 +70,12 @@ module Racknga
70
70
  private
71
71
  def log(start_time, end_time, request, status, headers, body)
72
72
  request_time = end_time - start_time
73
+ runtime = headers["X-Runtime"]
74
+ runtime_in_float = nil
75
+ runtime_in_float = runtime.to_f if runtime
73
76
  length = headers["Content-Length"] || "-"
74
77
  length = "-" if length == "0"
75
- format = "%s - %s [%s] \"%s %s %s\" %s %s \"%s\" \"%s\" %0.8f"
78
+ format = "%s - %s [%s] \"%s %s %s\" %s %s \"%s\" \"%s\" %s %0.8f"
76
79
  message = format % [request.ip || "-",
77
80
  request.env["REMOTE_USER"] || "-",
78
81
  end_time.dup.utc.strftime("%d/%b/%Y:%H:%M:%S %z"),
@@ -83,12 +86,14 @@ module Racknga
83
86
  length,
84
87
  request.env["HTTP_REFERER"] || "-",
85
88
  request.user_agent || "-",
89
+ runtime || "-",
86
90
  request_time]
87
91
  @logger.log("access",
88
92
  request.fullpath,
89
93
  :message => message,
90
94
  :user_agent => request.user_agent,
91
- :runtime => request_time)
95
+ :runtime => runtime_in_float,
96
+ :request_time => request_time)
92
97
  end
93
98
 
94
99
  # @private