le88-aws-s3 0.6.3.1310482014
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +19 -0
- data/INSTALL +55 -0
- data/README +545 -0
- data/Rakefile +334 -0
- data/bin/s3sh +6 -0
- data/bin/setup.rb +10 -0
- data/lib/aws/s3/acl.rb +636 -0
- data/lib/aws/s3/authentication.rb +221 -0
- data/lib/aws/s3/base.rb +240 -0
- data/lib/aws/s3/bittorrent.rb +58 -0
- data/lib/aws/s3/bucket.rb +319 -0
- data/lib/aws/s3/connection.rb +278 -0
- data/lib/aws/s3/error.rb +69 -0
- data/lib/aws/s3/exceptions.rb +133 -0
- data/lib/aws/s3/extensions.rb +340 -0
- data/lib/aws/s3/logging.rb +314 -0
- data/lib/aws/s3/object.rb +612 -0
- data/lib/aws/s3/owner.rb +44 -0
- data/lib/aws/s3/parsing.rb +99 -0
- data/lib/aws/s3/response.rb +180 -0
- data/lib/aws/s3/service.rb +51 -0
- data/lib/aws/s3/version.rb +12 -0
- data/lib/aws/s3.rb +60 -0
- data/support/faster-xml-simple/lib/faster_xml_simple.rb +187 -0
- data/support/faster-xml-simple/test/regression_test.rb +47 -0
- data/support/faster-xml-simple/test/test_helper.rb +17 -0
- data/support/faster-xml-simple/test/xml_simple_comparison_test.rb +46 -0
- data/support/rdoc/code_info.rb +211 -0
- data/test/acl_test.rb +254 -0
- data/test/authentication_test.rb +114 -0
- data/test/base_test.rb +136 -0
- data/test/bucket_test.rb +74 -0
- data/test/connection_test.rb +215 -0
- data/test/error_test.rb +70 -0
- data/test/extensions_test.rb +340 -0
- data/test/fixtures/buckets.yml +133 -0
- data/test/fixtures/errors.yml +34 -0
- data/test/fixtures/headers.yml +3 -0
- data/test/fixtures/logging.yml +15 -0
- data/test/fixtures/loglines.yml +5 -0
- data/test/fixtures/logs.yml +7 -0
- data/test/fixtures/policies.yml +16 -0
- data/test/fixtures.rb +89 -0
- data/test/logging_test.rb +89 -0
- data/test/mocks/fake_response.rb +26 -0
- data/test/object_test.rb +205 -0
- data/test/parsing_test.rb +66 -0
- data/test/remote/acl_test.rb +117 -0
- data/test/remote/bittorrent_test.rb +45 -0
- data/test/remote/bucket_test.rb +146 -0
- data/test/remote/logging_test.rb +82 -0
- data/test/remote/object_test.rb +371 -0
- data/test/remote/test_file.data +0 -0
- data/test/remote/test_helper.rb +33 -0
- data/test/response_test.rb +68 -0
- data/test/service_test.rb +23 -0
- data/test/test_helper.rb +110 -0
- metadata +179 -0
@@ -0,0 +1,314 @@
|
|
1
|
+
module AWS
|
2
|
+
module S3
|
3
|
+
# A bucket can be set to log the requests made on it. By default logging is turned off. You can check if a bucket has logging enabled:
|
4
|
+
#
|
5
|
+
# Bucket.logging_enabled_for? 'jukebox'
|
6
|
+
# # => false
|
7
|
+
#
|
8
|
+
# Enabling it is easy:
|
9
|
+
#
|
10
|
+
# Bucket.enable_logging_for('jukebox')
|
11
|
+
#
|
12
|
+
# Unless you specify otherwise, logs will be written to the bucket you want to log. The logs are just like any other object. By default they will start with the prefix 'log-'. You can customize what bucket you want the logs to be delivered to, as well as customize what the log objects' key is prefixed with by setting the <tt>target_bucket</tt> and <tt>target_prefix</tt> option:
|
13
|
+
#
|
14
|
+
# Bucket.enable_logging_for(
|
15
|
+
# 'jukebox', 'target_bucket' => 'jukebox-logs'
|
16
|
+
# )
|
17
|
+
#
|
18
|
+
# Now instead of logging right into the jukebox bucket, the logs will go into the bucket called jukebox-logs.
|
19
|
+
#
|
20
|
+
# Once logs have accumulated, you can access them using the <tt>logs</tt> method:
|
21
|
+
#
|
22
|
+
# pp Bucket.logs('jukebox')
|
23
|
+
# [#<AWS::S3::Logging::Log '/jukebox-logs/log-2006-11-14-07-15-24-2061C35880A310A1'>,
|
24
|
+
# #<AWS::S3::Logging::Log '/jukebox-logs/log-2006-11-14-08-15-27-D8EEF536EC09E6B3'>,
|
25
|
+
# #<AWS::S3::Logging::Log '/jukebox-logs/log-2006-11-14-08-15-29-355812B2B15BD789'>]
|
26
|
+
#
|
27
|
+
# Each log has a <tt>lines</tt> method that gives you information about each request in that log. All the fields are available
|
28
|
+
# as named methods. More information is available in Logging::Log::Line.
|
29
|
+
#
|
30
|
+
# logs = Bucket.logs('jukebox')
|
31
|
+
# log = logs.first
|
32
|
+
# line = log.lines.first
|
33
|
+
# line.operation
|
34
|
+
# # => 'REST.GET.LOGGING_STATUS'
|
35
|
+
# line.request_uri
|
36
|
+
# # => 'GET /jukebox?logging HTTP/1.1'
|
37
|
+
# line.remote_ip
|
38
|
+
# # => "67.165.183.125"
|
39
|
+
#
|
40
|
+
# Disabling logging is just as simple as enabling it:
|
41
|
+
#
|
42
|
+
# Bucket.disable_logging_for('jukebox')
|
43
|
+
module Logging
|
44
|
+
# Logging status captures information about the calling bucket's logging settings. If logging is enabled for the bucket
|
45
|
+
# the status object will indicate what bucket the logs are written to via the <tt>target_bucket</tt> method as well as
|
46
|
+
# the logging key prefix with via <tt>target_prefix</tt>.
|
47
|
+
#
|
48
|
+
# See the documentation for Logging::Management::ClassMethods for more information on how to get the logging status of a bucket.
|
49
|
+
class Status
|
50
|
+
include SelectiveAttributeProxy
|
51
|
+
attr_reader :enabled
|
52
|
+
alias_method :logging_enabled?, :enabled
|
53
|
+
|
54
|
+
def initialize(attributes = {}) #:nodoc:
|
55
|
+
attributes = {'target_bucket' => nil, 'target_prefix' => nil}.merge(attributes)
|
56
|
+
@enabled = attributes.has_key?('logging_enabled')
|
57
|
+
@attributes = attributes.delete('logging_enabled') || attributes
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_xml #:nodoc:
|
61
|
+
Builder.new(self).to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
attr_reader :attributes
|
66
|
+
|
67
|
+
class Builder < XmlGenerator #:nodoc:
|
68
|
+
attr_reader :logging_status
|
69
|
+
def initialize(logging_status)
|
70
|
+
@logging_status = logging_status
|
71
|
+
super()
|
72
|
+
end
|
73
|
+
|
74
|
+
def build
|
75
|
+
xml.tag!('BucketLoggingStatus', 'xmlns' => 'http://s3.amazonaws.com/doc/2006-03-01/') do
|
76
|
+
if logging_status.target_bucket && logging_status.target_prefix
|
77
|
+
xml.LoggingEnabled do
|
78
|
+
xml.TargetBucket logging_status.target_bucket
|
79
|
+
xml.TargetPrefix logging_status.target_prefix
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# A bucket log exposes requests made on the given bucket. Lines of the log represent a single request. The lines of a log
|
88
|
+
# can be accessed with the lines method.
|
89
|
+
#
|
90
|
+
# log = Bucket.logs_for('marcel').first
|
91
|
+
# log.lines
|
92
|
+
#
|
93
|
+
# More information about the logged requests can be found in the documentation for Log::Line.
|
94
|
+
class Log
|
95
|
+
def initialize(log_object) #:nodoc:
|
96
|
+
@log = log_object
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns the lines for the log. Each line is wrapped in a Log::Line.
|
100
|
+
if RUBY_VERSION >= '1.8.7'
|
101
|
+
def lines
|
102
|
+
log.value.lines.map {|line| Line.new(line)}
|
103
|
+
end
|
104
|
+
else
|
105
|
+
def lines
|
106
|
+
log.value.map {|line| Line.new(line)}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
memoized :lines
|
110
|
+
|
111
|
+
def path
|
112
|
+
log.path
|
113
|
+
end
|
114
|
+
|
115
|
+
def inspect #:nodoc:
|
116
|
+
"#<%s:0x%s '%s'>" % [self.class.name, object_id, path]
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
attr_reader :log
|
121
|
+
|
122
|
+
# Each line of a log exposes the raw line, but it also has method accessors for all the fields of the logged request.
|
123
|
+
#
|
124
|
+
# The list of supported log line fields are listed in the S3 documentation: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/LogFormat.html
|
125
|
+
#
|
126
|
+
# line = log.lines.first
|
127
|
+
# line.remote_ip
|
128
|
+
# # => '72.21.206.5'
|
129
|
+
#
|
130
|
+
# If a certain field does not apply to a given request (for example, the <tt>key</tt> field does not apply to a bucket request),
|
131
|
+
# or if it was unknown or unavailable, it will return <tt>nil</tt>.
|
132
|
+
#
|
133
|
+
# line.operation
|
134
|
+
# # => 'REST.GET.BUCKET'
|
135
|
+
# line.key
|
136
|
+
# # => nil
|
137
|
+
class Line < String
|
138
|
+
DATE = /\[([^\]]+)\]/
|
139
|
+
QUOTED_STRING = /"([^"]+)"/
|
140
|
+
REST = /(\S+)/
|
141
|
+
LINE_SCANNER = /#{DATE}|#{QUOTED_STRING}|#{REST}/
|
142
|
+
|
143
|
+
cattr_accessor :decorators
|
144
|
+
@@decorators = Hash.new {|hash, key| hash[key] = lambda {|entry| CoercibleString.coerce(entry)}}
|
145
|
+
cattr_reader :fields
|
146
|
+
@@fields = []
|
147
|
+
|
148
|
+
class << self
|
149
|
+
def field(name, offset, type = nil, &block) #:nodoc:
|
150
|
+
decorators[name] = block if block_given?
|
151
|
+
fields << name
|
152
|
+
class_eval(<<-EVAL, __FILE__, __LINE__)
|
153
|
+
def #{name}
|
154
|
+
value = parts[#{offset} - 1]
|
155
|
+
if value == '-'
|
156
|
+
nil
|
157
|
+
else
|
158
|
+
self.class.decorators[:#{name}].call(value)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
memoized :#{name}
|
162
|
+
EVAL
|
163
|
+
end
|
164
|
+
|
165
|
+
# Time.parse doesn't like %d/%B/%Y:%H:%M:%S %z so we have to transform it unfortunately
|
166
|
+
def typecast_time(datetime) #:nodoc:
|
167
|
+
datetime.sub!(%r|^(\w{2})/(\w{3})/(\w{4})|, '\2 \1 \3')
|
168
|
+
datetime.sub!(':', ' ')
|
169
|
+
Time.parse(datetime)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def initialize(line) #:nodoc:
|
174
|
+
super(line)
|
175
|
+
@parts = parse
|
176
|
+
end
|
177
|
+
|
178
|
+
field(:owner, 1) {|entry| Owner.new('id' => entry) }
|
179
|
+
field :bucket, 2
|
180
|
+
field(:time, 3) {|entry| typecast_time(entry)}
|
181
|
+
field :remote_ip, 4
|
182
|
+
field(:requestor, 5) {|entry| Owner.new('id' => entry) }
|
183
|
+
field :request_id, 6
|
184
|
+
field :operation, 7
|
185
|
+
field :key, 8
|
186
|
+
field :request_uri, 9
|
187
|
+
field :http_status, 10
|
188
|
+
field :error_code, 11
|
189
|
+
field :bytes_sent, 12
|
190
|
+
field :object_size, 13
|
191
|
+
field :total_time, 14
|
192
|
+
field :turn_around_time, 15
|
193
|
+
field :referrer, 16
|
194
|
+
field :user_agent, 17
|
195
|
+
|
196
|
+
# Returns all fields of the line in a hash of the form <tt>:field_name => :field_value</tt>.
|
197
|
+
#
|
198
|
+
# line.attributes.values_at(:bucket, :key)
|
199
|
+
# # => ['marcel', 'kiss.jpg']
|
200
|
+
def attributes
|
201
|
+
self.class.fields.inject({}) do |attribute_hash, field|
|
202
|
+
attribute_hash[field] = send(field)
|
203
|
+
attribute_hash
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
attr_reader :parts
|
209
|
+
|
210
|
+
def parse
|
211
|
+
scan(LINE_SCANNER).flatten.compact
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
module Management #:nodoc:
|
217
|
+
def self.included(klass) #:nodoc:
|
218
|
+
klass.extend(ClassMethods)
|
219
|
+
klass.extend(LoggingGrants)
|
220
|
+
end
|
221
|
+
|
222
|
+
module ClassMethods
|
223
|
+
# Returns the logging status for the bucket named <tt>name</tt>. From the logging status you can determine the bucket logs are delivered to
|
224
|
+
# and what the bucket object's keys are prefixed with. For more information see the Logging::Status class.
|
225
|
+
#
|
226
|
+
# Bucket.logging_status_for 'marcel'
|
227
|
+
def logging_status_for(name = nil, status = nil)
|
228
|
+
if name.is_a?(Status)
|
229
|
+
status = name
|
230
|
+
name = nil
|
231
|
+
end
|
232
|
+
|
233
|
+
path = path(name) << '?logging'
|
234
|
+
status ? put(path, {}, status.to_xml) : Status.new(get(path).parsed)
|
235
|
+
end
|
236
|
+
alias_method :logging_status, :logging_status_for
|
237
|
+
|
238
|
+
# Enables logging for the bucket named <tt>name</tt>. You can specify what bucket to log to with the <tt>'target_bucket'</tt> option as well
|
239
|
+
# as what prefix to add to the log files with the <tt>'target_prefix'</tt> option. Unless you specify otherwise, logs will be delivered to
|
240
|
+
# the same bucket that is being logged and will be prefixed with <tt>log-</tt>.
|
241
|
+
def enable_logging_for(name = nil, options = {})
|
242
|
+
name = bucket_name(name)
|
243
|
+
default_options = {'target_bucket' => name, 'target_prefix' => 'log-'}
|
244
|
+
options = default_options.merge(options)
|
245
|
+
grant_logging_access_to_target_bucket(options['target_bucket'])
|
246
|
+
logging_status(name, Status.new(options))
|
247
|
+
end
|
248
|
+
alias_method :enable_logging, :enable_logging_for
|
249
|
+
|
250
|
+
# Disables logging for the bucket named <tt>name</tt>.
|
251
|
+
def disable_logging_for(name = nil)
|
252
|
+
logging_status(bucket_name(name), Status.new)
|
253
|
+
end
|
254
|
+
alias_method :disable_logging, :disable_logging_for
|
255
|
+
|
256
|
+
# Returns true if logging has been enabled for the bucket named <tt>name</tt>.
|
257
|
+
def logging_enabled_for?(name = nil)
|
258
|
+
logging_status(bucket_name(name)).logging_enabled?
|
259
|
+
end
|
260
|
+
alias_method :logging_enabled?, :logging_enabled_for?
|
261
|
+
|
262
|
+
# Returns the collection of logs for the bucket named <tt>name</tt>.
|
263
|
+
#
|
264
|
+
# Bucket.logs_for 'marcel'
|
265
|
+
#
|
266
|
+
# Accepts the same options as Bucket.find, such as <tt>:max_keys</tt> and <tt>:marker</tt>.
|
267
|
+
def logs_for(name = nil, options = {})
|
268
|
+
if name.is_a?(Hash)
|
269
|
+
options = name
|
270
|
+
name = nil
|
271
|
+
end
|
272
|
+
|
273
|
+
name = bucket_name(name)
|
274
|
+
logging_status = logging_status_for(name)
|
275
|
+
return [] unless logging_status.logging_enabled?
|
276
|
+
objects(logging_status.target_bucket, options.merge(:prefix => logging_status.target_prefix)).map do |log_object|
|
277
|
+
Log.new(log_object)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
alias_method :logs, :logs_for
|
281
|
+
end
|
282
|
+
|
283
|
+
module LoggingGrants #:nodoc:
|
284
|
+
def grant_logging_access_to_target_bucket(target_bucket)
|
285
|
+
acl = acl(target_bucket)
|
286
|
+
acl.grants << ACL::Grant.grant(:logging_write)
|
287
|
+
acl.grants << ACL::Grant.grant(:logging_read_acp)
|
288
|
+
acl(target_bucket, acl)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def logging_status
|
293
|
+
self.class.logging_status_for(name)
|
294
|
+
end
|
295
|
+
|
296
|
+
def enable_logging(*args)
|
297
|
+
self.class.enable_logging_for(name, *args)
|
298
|
+
end
|
299
|
+
|
300
|
+
def disable_logging(*args)
|
301
|
+
self.class.disable_logging_for(name, *args)
|
302
|
+
end
|
303
|
+
|
304
|
+
def logging_enabled?
|
305
|
+
self.class.logging_enabled_for?(name)
|
306
|
+
end
|
307
|
+
|
308
|
+
def logs(options = {})
|
309
|
+
self.class.logs_for(name, options)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|