diff --git a/Gemfile b/Gemfile
index abf9f323fb4552b05fa0f71975a4422acab547ef..ddda5901a0883247dbf0c3564d76809bab8bb83d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -390,3 +390,6 @@ gem 'toml-rb', '~> 0.3.15', require: false
 # Feature toggles
 gem 'flipper', '~> 0.10.2'
 gem 'flipper-active_record', '~> 0.10.2'
+
+# Structured logging
+gem 'lograge', '~> 0.5'
diff --git a/Gemfile.lock b/Gemfile.lock
index a24636ad512a51eb3081afc264bfa436c5ec71c5..f9783a3468c768a26a35971fc46be68c856f38c8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -443,6 +443,10 @@ GEM
     logging (2.2.2)
       little-plugger (~> 1.1)
       multi_json (~> 1.10)
+    lograge (0.5.1)
+      actionpack (>= 4, < 5.2)
+      activesupport (>= 4, < 5.2)
+      railties (>= 4, < 5.2)
     loofah (2.0.3)
       nokogiri (>= 1.5.9)
     mail (2.6.5)
@@ -998,6 +1002,7 @@ DEPENDENCIES
   letter_opener_web (~> 1.3.0)
   license_finder (~> 2.1.0)
   licensee (~> 8.7.0)
+  lograge (~> 0.5)
   loofah (~> 2.0.3)
   mail_room (~> 0.9.1)
   method_source (~> 0.8)
diff --git a/changelogs/unreleased/sh-structured-logging.yml b/changelogs/unreleased/sh-structured-logging.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d89eb93f689cdd6312129e4b3a8d137a92a72e6e
--- /dev/null
+++ b/changelogs/unreleased/sh-structured-logging.yml
@@ -0,0 +1,4 @@
+---
+title: Add structured logging for Rails processes
+merge_request:
+author:
diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb
new file mode 100644
index 0000000000000000000000000000000000000000..149023162400ddc4207537e1a346eea5aa61ef55
--- /dev/null
+++ b/config/initializers/lograge.rb
@@ -0,0 +1,21 @@
+# Only use Lograge for Rails
+unless Sidekiq.server?
+  filename = File.join(Rails.root, 'log', "#{Rails.env}_json.log")
+
+  Rails.application.configure do
+    config.lograge.enabled = true
+    # Store the lograge JSON files in a separate file
+    config.lograge.keep_original_rails_log = true
+    # Don't use the Logstash formatter since this requires logstash-event, an
+    # unmaintained gem that monkey patches `Time`
+    config.lograge.formatter = Lograge::Formatters::Json.new
+    config.lograge.logger = ActiveSupport::Logger.new(filename)
+    # Add request parameters to log output
+    config.lograge.custom_options = lambda do |event|
+      {
+        time: event.time,
+        params: event.payload[:params].except(%w(controller action format))
+      }
+    end
+  end
+end