Skip to main content

Instrumentation

The instrumentation plugin publishes events for various operations to a centralized notification component. In addition to that, it provides default logging for these events.

Shrine.plugin :instrumentation

By default, the notification component is assumed to be ActiveSupport::Notifications, but dry-monitor is supported as well:

# Gemfile
gem "dry-monitor"
require "dry-monitor"

Shrine.plugin :instrumentation, notifications: Dry::Monitor::Notifications.new(:test)

Logging

By default, the instrumentation plugin adds logging to the instrumented events:

uploaded_file = Shrine.upload(StringIO.new("file"), :store)
uploaded_file.exists?
uploaded_file.download
uploaded_file.delete
Metadata (32ms) – {:storage=>:store, :io=>StringIO, :uploader=>Shrine}
Upload (1523ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :io=>StringIO, :upload_options=>{}, :uploader=>Shrine}
Exists (755ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
Download (1002ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :download_options=>{}, :uploader=>Shrine}
Delete (700ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}

It uses Shrine.logger for logging, which allows you to change where and how are the logs going to be written:

Shrine.logger = Rails.logger # in Rails apps

You can choose to log only certain events, e.g. we can exclude metadata extraction:

Shrine.plugin :instrumentation, log_events: [
  :upload,
  :exists,
  :download,
  :delete,
]

You can also use your own log subscriber:

Shrine.plugin :instrumentation, log_subscriber: -> (event) {
  Shrine.logger.info JSON.generate(name: event.name, duration: event.duration, **event.payload)
}
{"name":"metadata","duration":0,"storage":"store","io":"#<StringIO:0x00007fd1d4a1b9d8>","options":{},"uploader":"Shrine"}
{"name":"upload","duration":0,"storage":"store","location":"dbeb3c3ed664059eb41a608e54a29f54","io":"#<StringIO:0x00007fd1d4a1b9d8>","upload_options":{},"options":{"location":"dbeb3c3ed664059eb41a608e54a29f54","metadata":{"filename":null,"size":4,"mime_type":null}},"uploader":"Shrine"}
{"name":"exists","duration":0,"storage":"store","location":"dbeb3c3ed664059eb41a608e54a29f54","uploader":"Shrine"}
{"name":"download","duration":0,"storage":"store","location":"dbeb3c3ed664059eb41a608e54a29f54","download_options":{},"uploader":"Shrine"}
{"name":"delete","duration":0,"storage":"store","location":"dbeb3c3ed664059eb41a608e54a29f54","uploader":"Shrine"}

Or disable logging altogether:

Shrine.plugin :instrumentation, log_subscriber: nil

Events

The following events are instrumented by the instrumentation plugin:

upload.shrine

The upload.shrine event is logged on Shrine#upload, and contains the following payload:

KeyDescription
:storageThe storage identifier
:locationThe location of the uploaded file
:ioThe uploaded IO object
:upload_optionsAny upload options that were specified
:metadataMetadata extracted during upload
:optionsAny additional uploader options
:uploaderThe uploader class that sent the event

download.shrine

The download.shrine event is logged on UploadedFile#stream (which includes UploadedFile#download), and contains the following payload:

KeyDescription
:storageThe storage identifier
:locationThe location of the uploaded file
:download_optionsAny download options that were specified
:uploaderThe uploader class that sent the event

open.shrine

The download.shrine event is logged on UploadedFile#open or when uploaded file is implicitly opened on calling an IO method.

KeyDescription
:storageThe storage identifier
:locationThe location of the uploaded file
:download_optionsAny download options that were specified
:uploaderThe uploader class that sent the event

exists.shrine

The exists.shrine event is logged on UploadedFile#exists?, and contains the following payload:

KeyDescription
:storageThe storage identifier
:locationThe location of the uploaded file
:uploaderThe uploader class that sent the event

delete.shrine

The delete.shrine event is logged on UploadedFile#delete, and contains the following payload:

KeyDescription
:storageThe storage identifier
:locationThe location of the uploaded file
:uploaderThe uploader class that sent the event

metadata.shrine

The metadata.shrine event is logged on Shrine#upload, and contains the following payload:

KeyDescription
:storageThe storage identifier
:ioThe uploaded IO object
:optionsAny options sent to the uploader
:uploaderThe uploader class that sent the event

API

The instrumentation plugin adds Shrine.instrument and Shrine.subscribe methods:

# sends a `my_event.shrine` event to the notifications component
Shrine.instrument(:my_event, { foo: "bar" }) do
  # do work
end
# subscribes to `my_event.shrine` events on the notifications component
Shrine.subscribe(:my_event) do |event|
  event.name #=> :my_event
  event.payload #=> { foo: "bar", uploader: Shrine }
  event[:foo] #=> "bar"
  event.duration #=> 15 (in milliseconds)
end