mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Instrument and log the services
This commit is contained in:
parent
57fd9d2247
commit
4bfe7af68f
7 changed files with 169 additions and 32 deletions
|
@ -6,6 +6,14 @@ module ActiveStorage
|
||||||
|
|
||||||
config.eager_load_namespaces << ActiveStorage
|
config.eager_load_namespaces << ActiveStorage
|
||||||
|
|
||||||
|
initializer "active_storage.logger" do
|
||||||
|
require "active_storage/service"
|
||||||
|
|
||||||
|
config.after_initialize do |app|
|
||||||
|
ActiveStorage::Service.logger = app.config.active_storage.logger || Rails.logger
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
initializer "active_storage.routes" do
|
initializer "active_storage.routes" do
|
||||||
require "active_storage/disk_controller"
|
require "active_storage/disk_controller"
|
||||||
|
|
||||||
|
|
51
lib/active_storage/log_subscriber.rb
Normal file
51
lib/active_storage/log_subscriber.rb
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
require "active_support/log_subscriber"
|
||||||
|
|
||||||
|
# Implements the ActiveSupport::LogSubscriber for logging notifications when
|
||||||
|
# email is delivered or received.
|
||||||
|
class ActiveStorage::LogSubscriber < ActiveSupport::LogSubscriber
|
||||||
|
def service_upload(event)
|
||||||
|
message = color("Uploaded file to key: #{key_in(event)}", GREEN)
|
||||||
|
message << color(" (checksum: #{event.payload[:checksum]})", GREEN) if event.payload[:checksum]
|
||||||
|
info event, message
|
||||||
|
end
|
||||||
|
|
||||||
|
def service_download(event)
|
||||||
|
info event, color("Downloaded file from key: #{key_in(event)}", BLUE)
|
||||||
|
end
|
||||||
|
|
||||||
|
def service_delete(event)
|
||||||
|
info event, color("Deleted file from key: #{key_in(event)}", RED)
|
||||||
|
end
|
||||||
|
|
||||||
|
def service_exist(event)
|
||||||
|
debug event, color("Checked if file exist at key: #{key_in(event)} (#{event.payload[:exist] ? "yes" : "no"})", BLUE)
|
||||||
|
end
|
||||||
|
|
||||||
|
def service_url(event)
|
||||||
|
debug event, color("Generated URL for file at key: #{key_in(event)} (#{event.payload[:url]})", BLUE)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use the logger configured for ActiveStorage::Base.logger
|
||||||
|
def logger
|
||||||
|
ActiveStorage::Service.logger
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def info(event, colored_message)
|
||||||
|
super log_prefix_for_service(event) + colored_message
|
||||||
|
end
|
||||||
|
|
||||||
|
def debug(event, colored_message)
|
||||||
|
super log_prefix_for_service(event) + colored_message
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_prefix_for_service(event)
|
||||||
|
color " #{event.payload[:service]} Storage (#{event.duration.round(1)}ms) ", CYAN
|
||||||
|
end
|
||||||
|
|
||||||
|
def key_in(event)
|
||||||
|
event.payload[:key]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveStorage::LogSubscriber.attach_to :active_storage
|
|
@ -1,3 +1,5 @@
|
||||||
|
require_relative "log_subscriber"
|
||||||
|
|
||||||
# Abstract class serving as an interface for concrete services.
|
# Abstract class serving as an interface for concrete services.
|
||||||
#
|
#
|
||||||
# The available services are:
|
# The available services are:
|
||||||
|
@ -35,6 +37,8 @@ class ActiveStorage::Service
|
||||||
extend ActiveSupport::Autoload
|
extend ActiveSupport::Autoload
|
||||||
autoload :Configurator
|
autoload :Configurator
|
||||||
|
|
||||||
|
class_attribute :logger
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
# Configure an Active Storage service by name from a set of configurations,
|
# Configure an Active Storage service by name from a set of configurations,
|
||||||
# typically loaded from a YAML file. The Active Storage engine uses this
|
# typically loaded from a YAML file. The Active Storage engine uses this
|
||||||
|
@ -73,4 +77,16 @@ class ActiveStorage::Service
|
||||||
def url(key, expires_in:, disposition:, filename:)
|
def url(key, expires_in:, disposition:, filename:)
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def instrument(operation, key, payload = {}, &block)
|
||||||
|
ActiveSupport::Notifications.instrument(
|
||||||
|
"service_#{operation}.active_storage",
|
||||||
|
payload.merge(key: key, service: service_name), &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def service_name
|
||||||
|
# ActiveStorage::Service::DiskService => Disk
|
||||||
|
self.class.name.split("::").third.remove("Service")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,38 +11,61 @@ class ActiveStorage::Service::DiskService < ActiveStorage::Service
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload(key, io, checksum: nil)
|
def upload(key, io, checksum: nil)
|
||||||
|
instrument :upload, key, checksum: checksum do
|
||||||
IO.copy_stream(io, make_path_for(key))
|
IO.copy_stream(io, make_path_for(key))
|
||||||
ensure_integrity_of(key, checksum) if checksum
|
ensure_integrity_of(key, checksum) if checksum
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def download(key)
|
def download(key)
|
||||||
if block_given?
|
if block_given?
|
||||||
|
instrument :streaming_download, key do
|
||||||
File.open(path_for(key)) do |file|
|
File.open(path_for(key)) do |file|
|
||||||
while data = file.binread(64.kilobytes)
|
while data = file.binread(64.kilobytes)
|
||||||
yield data
|
yield data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
|
instrument :download, key do
|
||||||
File.binread path_for(key)
|
File.binread path_for(key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def delete(key)
|
def delete(key)
|
||||||
File.delete path_for(key) rescue Errno::ENOENT # Ignore files already deleted
|
instrument :delete, key do
|
||||||
|
begin
|
||||||
|
File.delete path_for(key)
|
||||||
|
rescue Errno::ENOENT
|
||||||
|
# Ignore files already deleted
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def exist?(key)
|
def exist?(key)
|
||||||
File.exist? path_for(key)
|
instrument :exist, key do |payload|
|
||||||
|
answer = File.exist? path_for(key)
|
||||||
|
payload[:exist] = answer
|
||||||
|
answer
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def url(key, expires_in:, disposition:, filename:)
|
def url(key, expires_in:, disposition:, filename:)
|
||||||
|
instrument :url, key do |payload|
|
||||||
verified_key_with_expiration = ActiveStorage::VerifiedKeyWithExpiration.encode(key, expires_in: expires_in)
|
verified_key_with_expiration = ActiveStorage::VerifiedKeyWithExpiration.encode(key, expires_in: expires_in)
|
||||||
|
|
||||||
|
generated_url =
|
||||||
if defined?(Rails) && defined?(Rails.application)
|
if defined?(Rails) && defined?(Rails.application)
|
||||||
Rails.application.routes.url_helpers.rails_disk_blob_path(verified_key_with_expiration, disposition: disposition, filename: filename)
|
Rails.application.routes.url_helpers.rails_disk_blob_path(verified_key_with_expiration, disposition: disposition, filename: filename)
|
||||||
else
|
else
|
||||||
"/rails/blobs/#{verified_key_with_expiration}/#{filename}?disposition=#{disposition}"
|
"/rails/blobs/#{verified_key_with_expiration}/#{filename}?disposition=#{disposition}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
payload[:url] = generated_url
|
||||||
|
|
||||||
|
generated_url
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -10,29 +10,47 @@ class ActiveStorage::Service::GCSService < ActiveStorage::Service
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload(key, io, checksum: nil)
|
def upload(key, io, checksum: nil)
|
||||||
|
instrument :upload, key, checksum: checksum do
|
||||||
|
begin
|
||||||
bucket.create_file(io, key, md5: checksum)
|
bucket.create_file(io, key, md5: checksum)
|
||||||
rescue Google::Cloud::InvalidArgumentError
|
rescue Google::Cloud::InvalidArgumentError
|
||||||
raise ActiveStorage::IntegrityError
|
raise ActiveStorage::IntegrityError
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# FIXME: Add streaming when given a block
|
# FIXME: Add streaming when given a block
|
||||||
def download(key)
|
def download(key)
|
||||||
|
instrument :download, key do
|
||||||
io = file_for(key).download
|
io = file_for(key).download
|
||||||
io.rewind
|
io.rewind
|
||||||
io.read
|
io.read
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def delete(key)
|
def delete(key)
|
||||||
|
instrument :delete, key do
|
||||||
file_for(key)&.delete
|
file_for(key)&.delete
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def exist?(key)
|
def exist?(key)
|
||||||
file_for(key).present?
|
instrument :exist, key do |payload|
|
||||||
|
answer = file_for(key).present?
|
||||||
|
payload[:exist] = answer
|
||||||
|
answer
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def url(key, expires_in:, disposition:, filename:)
|
def url(key, expires_in:, disposition:, filename:)
|
||||||
file_for(key).signed_url(expires: expires_in) + "&" +
|
instrument :url, key do |payload|
|
||||||
|
generated_url = file_for(key).signed_url(expires: expires_in) + "&" +
|
||||||
{ "response-content-disposition" => "#{disposition}; filename=\"#{filename}\"" }.to_query
|
{ "response-content-disposition" => "#{disposition}; filename=\"#{filename}\"" }.to_query
|
||||||
|
|
||||||
|
payload[:url] = generated_url
|
||||||
|
|
||||||
|
generated_url
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -10,30 +10,50 @@ class ActiveStorage::Service::S3Service < ActiveStorage::Service
|
||||||
end
|
end
|
||||||
|
|
||||||
def upload(key, io, checksum: nil)
|
def upload(key, io, checksum: nil)
|
||||||
|
instrument :upload, key, checksum: checksum do
|
||||||
|
begin
|
||||||
object_for(key).put(body: io, content_md5: checksum)
|
object_for(key).put(body: io, content_md5: checksum)
|
||||||
rescue Aws::S3::Errors::BadDigest
|
rescue Aws::S3::Errors::BadDigest
|
||||||
raise ActiveStorage::IntegrityError
|
raise ActiveStorage::IntegrityError
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def download(key)
|
def download(key)
|
||||||
if block_given?
|
if block_given?
|
||||||
|
instrument :streaming_download, key do
|
||||||
stream(key, &block)
|
stream(key, &block)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
|
instrument :download, key do
|
||||||
object_for(key).get.body.read.force_encoding(Encoding::BINARY)
|
object_for(key).get.body.read.force_encoding(Encoding::BINARY)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def delete(key)
|
def delete(key)
|
||||||
|
instrument :delete, key do
|
||||||
object_for(key).delete
|
object_for(key).delete
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def exist?(key)
|
def exist?(key)
|
||||||
object_for(key).exists?
|
instrument :exist, key do |payload|
|
||||||
|
answer = object_for(key).exists?
|
||||||
|
payload[:exist] = answer
|
||||||
|
answer
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def url(key, expires_in:, disposition:, filename:)
|
def url(key, expires_in:, disposition:, filename:)
|
||||||
object_for(key).presigned_url :get, expires_in: expires_in,
|
instrument :url, key do |payload|
|
||||||
|
generated_url = object_for(key).presigned_url :get, expires_in: expires_in,
|
||||||
response_content_disposition: "#{disposition}; filename=\"#{filename}\""
|
response_content_disposition: "#{disposition}; filename=\"#{filename}\""
|
||||||
|
|
||||||
|
payload[:url] = generated_url
|
||||||
|
|
||||||
|
generated_url
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -7,6 +7,7 @@ require "byebug"
|
||||||
require "active_storage"
|
require "active_storage"
|
||||||
require "active_storage/service/disk_service"
|
require "active_storage/service/disk_service"
|
||||||
ActiveStorage::Blob.service = ActiveStorage::Service::DiskService.new(root: File.join(Dir.tmpdir, "active_storage"))
|
ActiveStorage::Blob.service = ActiveStorage::Service::DiskService.new(root: File.join(Dir.tmpdir, "active_storage"))
|
||||||
|
ActiveStorage::Service.logger = ActiveSupport::Logger.new(STDOUT)
|
||||||
|
|
||||||
require "active_storage/verified_key_with_expiration"
|
require "active_storage/verified_key_with_expiration"
|
||||||
ActiveStorage::VerifiedKeyWithExpiration.verifier = ActiveSupport::MessageVerifier.new("Testing")
|
ActiveStorage::VerifiedKeyWithExpiration.verifier = ActiveSupport::MessageVerifier.new("Testing")
|
||||||
|
|
Loading…
Reference in a new issue