racknga 0.9.2 → 0.9.4

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