diff --git a/Gemfile b/Gemfile
index 652cc04ecbc1d1ac7304329f0a0f457b71afe808..0c5fffe73ea4b4131ed9791e24765482318b5187 100644
--- a/Gemfile
+++ b/Gemfile
@@ -301,7 +301,7 @@ gem 'sentry-raven', '~> 2.9'
 gem 'premailer-rails', '~> 1.10.3'
 
 # LabKit: Tracing and Correlation
-gem 'gitlab-labkit', '~> 0.5'
+gem 'gitlab-labkit', '0.8.0'
 
 # I18n
 gem 'ruby_parser', '~> 3.8', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 9ca82644a972237b801a7e852d178f836c7a2e65..15ceff6e86814bbd9ab93c44ef7d4f109aa02d17 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -117,7 +117,7 @@ GEM
       activemodel (>= 5.0)
     brakeman (4.2.1)
     browser (2.5.3)
-    builder (3.2.3)
+    builder (3.2.4)
     bullet (6.0.2)
       activesupport (>= 3.0.0)
       uniform_notifier (~> 1.11)
@@ -362,7 +362,7 @@ GEM
     github-markup (1.7.0)
     gitlab-chronic (0.10.5)
       numerizer (~> 0.2)
-    gitlab-labkit (0.7.0)
+    gitlab-labkit (0.8.0)
       actionpack (>= 5.0.0, < 6.1.0)
       activesupport (>= 5.0.0, < 6.1.0)
       grpc (~> 1.19)
@@ -589,7 +589,7 @@ GEM
       activesupport (>= 4)
       railties (>= 4)
       request_store (~> 1.0)
-    loofah (2.3.1)
+    loofah (2.4.0)
       crass (~> 1.0.2)
       nokogiri (>= 1.5.9)
     lumberjack (1.0.13)
@@ -1204,7 +1204,7 @@ DEPENDENCIES
   gitaly (~> 1.73.0)
   github-markup (~> 1.7.0)
   gitlab-chronic (~> 0.10.5)
-  gitlab-labkit (~> 0.5)
+  gitlab-labkit (= 0.8.0)
   gitlab-license (~> 1.0)
   gitlab-markup (~> 1.7.0)
   gitlab-net-dns (~> 0.9.1)
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f5306801c049f4a695581af8ab2bc8051d361554..60b5d9b6da8352a158e06b91f19cd4cac4608de1 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -33,6 +33,7 @@ class ApplicationController < ActionController::Base
   before_action :check_impersonation_availability
   before_action :required_signup_info
 
+  around_action :set_current_context
   around_action :set_locale
   around_action :set_session_storage
 
@@ -448,6 +449,14 @@ def u2f_app_id
     request.base_url
   end
 
+  def set_current_context(&block)
+    Gitlab::ApplicationContext.with_context(
+      user: -> { auth_user },
+      project: -> { @project },
+      namespace: -> { @group },
+      &block)
+  end
+
   def set_locale(&block)
     Gitlab::I18n.with_user_locale(current_user, &block)
   end
diff --git a/config/initializers/correlation_id.rb b/config/initializers/correlation_id.rb
deleted file mode 100644
index 2a7c138dc40fa34f8acf04380dd71aa420e20581..0000000000000000000000000000000000000000
--- a/config/initializers/correlation_id.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-Rails.application.config.middleware.use(Gitlab::Middleware::CorrelationId)
diff --git a/config/initializers/labkit_middleware.rb b/config/initializers/labkit_middleware.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ea4103f052fd966642085d736910f41d60b4870e
--- /dev/null
+++ b/config/initializers/labkit_middleware.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+Rails.application.config.middleware.use(Labkit::Middleware::Rack)
diff --git a/config/initializers/tracing.rb b/config/initializers/tracing.rb
index 0ae57021fcf394fc0efb338097dc0b7afabf35e5..aaf74eb4cd304097f685cdfb53d4c5984cbd5867 100644
--- a/config/initializers/tracing.rb
+++ b/config/initializers/tracing.rb
@@ -2,7 +2,7 @@
 
 if Labkit::Tracing.enabled?
   Rails.application.configure do |config|
-    config.middleware.insert_after Gitlab::Middleware::CorrelationId, ::Labkit::Tracing::RackMiddleware
+    config.middleware.insert_after Labkit::Middleware::Rack, ::Labkit::Tracing::RackMiddleware
   end
 
   # Instrument the Sidekiq client
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 56eccb036b6f12c51f37609a8976f0fa6df36eeb..eae10738f32b2b67417f3ea643abcfd4b24b1a7d 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -43,6 +43,14 @@ class API < Grape::API
       header['X-Content-Type-Options'] = 'nosniff'
     end
 
+    before do
+      Gitlab::ApplicationContext.push(
+        user: -> { current_user },
+        project: -> { @project },
+        namespace: -> { @group }
+      )
+    end
+
     # The locale is set to the current user's locale when `current_user` is loaded
     after { Gitlab::I18n.use_default_locale }
 
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index 607e07844153918aaaa2f725455384f2db55a689..b719e5c0886f9c693c1471f611c0df20983430f6 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -107,8 +107,10 @@ def set_project
         if params[:gl_repository]
           @project, @repo_type = Gitlab::GlRepository.parse(params[:gl_repository])
           @redirected_path = nil
-        else
+        elsif params[:project]
           @project, @repo_type, @redirected_path = Gitlab::RepoPath.parse(params[:project])
+        else
+          @project, @repo_type, @redirected_path = nil, nil, nil
         end
       end
       # rubocop:enable Gitlab/ModuleWithInstanceVariables
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index 11f2a2ea1c0271005d53e46dcfb2220bc7fe164d..d64de2bb465124b7eeed75093f092faa7bda7e6c 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -6,6 +6,13 @@ module Internal
     class Base < Grape::API
       before { authenticate_by_gitlab_shell_token! }
 
+      before do
+        Gitlab::ApplicationContext.push(
+          user: -> { actor&.user },
+          project: -> { project }
+        )
+      end
+
       helpers ::API::Helpers::InternalHelpers
 
       UNKNOWN_CHECK_RESULT_ERROR = 'Unknown check result'.freeze
@@ -205,7 +212,12 @@ def check_allowed(params)
           status 200
 
           response = Gitlab::InternalPostReceive::Response.new
+
+          # Try to load the project and users so we have the application context
+          # available for logging before we schedule any jobs.
           user = actor.user
+          project
+
           push_options = Gitlab::PushOptions.new(params[:push_options])
 
           response.reference_counter_decreased = Gitlab::ReferenceCounter.new(params[:gl_repository]).decrease
diff --git a/lib/gitlab/application_context.rb b/lib/gitlab/application_context.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b9190b519a0cf6b15a208c3e35712b276d656936
--- /dev/null
+++ b/lib/gitlab/application_context.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Gitlab
+  # A GitLab-rails specific accessor for `Labkit::Logging::ApplicationContext`
+  class ApplicationContext
+    include Gitlab::Utils::LazyAttributes
+
+    def self.with_context(args, &block)
+      application_context = new(**args)
+      Labkit::Context.with_context(application_context.to_lazy_hash, &block)
+    end
+
+    def self.push(args)
+      application_context = new(**args)
+      Labkit::Context.push(application_context.to_lazy_hash)
+    end
+
+    def initialize(user: nil, project: nil, namespace: nil)
+      @user, @project, @namespace = user, project, namespace
+    end
+
+    def to_lazy_hash
+      { user: -> { username },
+        project: -> { project_path },
+        root_namespace: -> { root_namespace_path } }
+    end
+
+    private
+
+    lazy_attr_reader :user, type: User
+    lazy_attr_reader :project, type: Project
+    lazy_attr_reader :namespace, type: Namespace
+
+    def project_path
+      project&.full_path
+    end
+
+    def username
+      user&.username
+    end
+
+    def root_namespace_path
+      if namespace
+        namespace.full_path_components.first
+      else
+        project&.full_path_components&.first
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/middleware/correlation_id.rb b/lib/gitlab/middleware/correlation_id.rb
deleted file mode 100644
index fffd5da827fd88141909749edd6a0e4476490553..0000000000000000000000000000000000000000
--- a/lib/gitlab/middleware/correlation_id.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-# A dumb middleware that steals correlation id
-# and sets it as a global context for the request
-module Gitlab
-  module Middleware
-    class CorrelationId
-      include ActionView::Helpers::TagHelper
-
-      def initialize(app)
-        @app = app
-      end
-
-      def call(env)
-        ::Labkit::Correlation::CorrelationId.use_id(correlation_id(env)) do
-          @app.call(env)
-        end
-      end
-
-      private
-
-      def correlation_id(env)
-        request(env).request_id
-      end
-
-      def request(env)
-        ActionDispatch::Request.new(env)
-      end
-    end
-  end
-end
diff --git a/lib/gitlab/sidekiq_middleware.rb b/lib/gitlab/sidekiq_middleware.rb
index c6726dcfa675c8c657ec13abb33b622ad84b6f88..4893cbc1f45f7a1443c460c8f86e6b690681339f 100644
--- a/lib/gitlab/sidekiq_middleware.rb
+++ b/lib/gitlab/sidekiq_middleware.rb
@@ -15,7 +15,7 @@ def self.server_configurator(metrics: true, arguments_logger: true, memory_kille
         chain.add Gitlab::SidekiqMiddleware::MemoryKiller if memory_killer
         chain.add Gitlab::SidekiqMiddleware::RequestStoreMiddleware if request_store
         chain.add Gitlab::SidekiqMiddleware::BatchLoader
-        chain.add Gitlab::SidekiqMiddleware::CorrelationLogger
+        chain.add Labkit::Middleware::Sidekiq::Server
         chain.add Gitlab::SidekiqMiddleware::InstrumentationLogger
         chain.add Gitlab::SidekiqStatus::ServerMiddleware
       end
@@ -27,7 +27,7 @@ def self.server_configurator(metrics: true, arguments_logger: true, memory_kille
     def self.client_configurator
       lambda do |chain|
         chain.add Gitlab::SidekiqStatus::ClientMiddleware
-        chain.add Gitlab::SidekiqMiddleware::CorrelationInjector
+        chain.add Labkit::Middleware::Sidekiq::Client
       end
     end
   end
diff --git a/lib/gitlab/sidekiq_middleware/correlation_injector.rb b/lib/gitlab/sidekiq_middleware/correlation_injector.rb
deleted file mode 100644
index 1539fd706abe64f67f9b23c0284d088dcb5f6572..0000000000000000000000000000000000000000
--- a/lib/gitlab/sidekiq_middleware/correlation_injector.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
-  module SidekiqMiddleware
-    class CorrelationInjector
-      def call(worker_class, job, queue, redis_pool)
-        job[Labkit::Correlation::CorrelationId::LOG_KEY] ||=
-          Labkit::Correlation::CorrelationId.current_or_new_id
-
-        yield
-      end
-    end
-  end
-end
diff --git a/lib/gitlab/sidekiq_middleware/correlation_logger.rb b/lib/gitlab/sidekiq_middleware/correlation_logger.rb
deleted file mode 100644
index cffc44835738260a32ab2366d22c06a4772752ec..0000000000000000000000000000000000000000
--- a/lib/gitlab/sidekiq_middleware/correlation_logger.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
-  module SidekiqMiddleware
-    class CorrelationLogger
-      def call(worker, job, queue)
-        correlation_id = job[Labkit::Correlation::CorrelationId::LOG_KEY]
-
-        Labkit::Correlation::CorrelationId.use_id(correlation_id) do
-          yield
-        end
-      end
-    end
-  end
-end
diff --git a/lib/gitlab/utils/lazy_attributes.rb b/lib/gitlab/utils/lazy_attributes.rb
new file mode 100644
index 0000000000000000000000000000000000000000..79f3a7dcb532c3641d886ed48525e843cc30d2fe
--- /dev/null
+++ b/lib/gitlab/utils/lazy_attributes.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Gitlab
+  module Utils
+    module LazyAttributes
+      extend ActiveSupport::Concern
+      include Gitlab::Utils::StrongMemoize
+
+      class_methods do
+        def lazy_attr_reader(*one_or_more_names, type: nil)
+          names = Array.wrap(one_or_more_names)
+          names.each { |name| define_lazy_reader(name, type: type) }
+        end
+
+        def lazy_attr_accessor(*one_or_more_names, type: nil)
+          names = Array.wrap(one_or_more_names)
+          names.each do |name|
+            define_lazy_reader(name, type: type)
+            define_lazy_writer(name)
+          end
+        end
+
+        private
+
+        def define_lazy_reader(name, type:)
+          define_method(name) do
+            strong_memoize("#{name}_lazy_loaded") do
+              value = instance_variable_get("@#{name}")
+              value = value.call if value.respond_to?(:call)
+              value = nil if type && !value.is_a?(type)
+              value
+            end
+          end
+        end
+
+        def define_lazy_writer(name)
+          define_method("#{name}=") do |value|
+            clear_memoization("#{name}_lazy_loaded")
+            instance_variable_set("@#{name}", value)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index e72ab16f62a7b08c451e2e6449629bb885700c3c..0c299dcda3484768d76426a6a3f5c05cef8f01c8 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -895,4 +895,50 @@ def index
       end
     end
   end
+
+  context '#set_current_context' do
+    controller(described_class) do
+      def index
+        Labkit::Context.with_context do |context|
+          render json: context.to_h
+        end
+      end
+    end
+
+    let_it_be(:user) { create(:user) }
+
+    before do
+      sign_in(user)
+    end
+
+    it 'does not break anything when no group or project method is defined' do
+      get :index
+
+      expect(response).to have_gitlab_http_status(:success)
+    end
+
+    it 'sets the username in the context when signed in' do
+      get :index
+
+      expect(json_response['meta.user']).to eq(user.username)
+    end
+
+    it 'sets the group if it was available' do
+      group = build_stubbed(:group)
+      controller.instance_variable_set(:@group, group)
+
+      get :index, format: :json
+
+      expect(json_response['meta.root_namespace']).to eq(group.path)
+    end
+
+    it 'sets the project if one was available' do
+      project = build_stubbed(:project)
+      controller.instance_variable_set(:@project, project)
+
+      get :index, format: :json
+
+      expect(json_response['meta.project']).to eq(project.full_path)
+    end
+  end
 end
diff --git a/spec/lib/gitlab/application_context_spec.rb b/spec/lib/gitlab/application_context_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1c8fcb8d737d6749d4adf1814475cbbf15bed4a8
--- /dev/null
+++ b/spec/lib/gitlab/application_context_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::ApplicationContext do
+  describe '.with_context' do
+    it 'yields the block' do
+      expect { |b| described_class.with_context({}, &b) }.to yield_control
+    end
+
+    it 'passes the expected context on to labkit' do
+      fake_proc = duck_type(:call)
+      expected_context = hash_including(user: fake_proc, project: fake_proc, root_namespace: fake_proc)
+
+      expect(Labkit::Context).to receive(:with_context).with(expected_context)
+
+      described_class.with_context(
+        user: build(:user),
+        project: build(:project),
+        namespace: build(:namespace)) {}
+    end
+
+    it 'raises an error when passing invalid options' do
+      expect { described_class.with_context(no: 'option') {} }.to raise_error(ArgumentError)
+    end
+  end
+
+  describe '.push' do
+    it 'passes the expected context on to labkit' do
+      fake_proc = duck_type(:call)
+      expected_context = hash_including(user: fake_proc, project: fake_proc, root_namespace: fake_proc)
+
+      expect(Labkit::Context).to receive(:push).with(expected_context)
+
+      described_class.push(user: build(:user))
+    end
+
+    it 'raises an error when passing invalid options' do
+      expect { described_class.push(no: 'option')}.to raise_error(ArgumentError)
+    end
+  end
+
+  describe '#to_lazy_hash' do
+    let(:user) { build(:user) }
+    let(:project) { build(:project) }
+    let(:namespace) { build(:group) }
+    let(:subgroup) { build(:group, parent: namespace) }
+
+    def result(context)
+      context.to_lazy_hash.transform_values { |v| v.call }
+    end
+
+    it 'does not call the attributes until needed' do
+      fake_proc = double('Proc')
+
+      expect(fake_proc).not_to receive(:call)
+
+      described_class.new(user: fake_proc, project: fake_proc, namespace: fake_proc).to_lazy_hash
+    end
+
+    it 'correctly loads the expected values when they are wrapped in a block' do
+      context = described_class.new(user: -> { user }, project: -> { project }, namespace: -> { subgroup })
+
+      expect(result(context))
+        .to include(user: user.username, project: project.full_path, root_namespace: namespace.full_path)
+    end
+
+    it 'correctly loads the expected values when passed directly' do
+      context = described_class.new(user: user, project: project, namespace: subgroup)
+
+      expect(result(context))
+        .to include(user: user.username, project: project.full_path, root_namespace: namespace.full_path)
+    end
+
+    it 'falls back to a projects namespace when a project is passed but no namespace' do
+      context = described_class.new(project: project)
+
+      expect(result(context))
+        .to include(project: project.full_path, root_namespace: project.full_path_components.first)
+    end
+  end
+end
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index ec1b935ad63095cf1570124465b868400930c80c..dae5b028c1593565ea19a7b87414f2b6e22b0848 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -654,13 +654,10 @@
     let(:user) { create(:user) }
     let!(:project) { create(:project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') }
     let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) }
-    let(:correlation_id) { 'my-correlation-id' }
 
     before do
       setup_import_export_config('with_invalid_records')
 
-      # Import is running from the rake task, `correlation_id` is not assigned
-      expect(Labkit::Correlation::CorrelationId).to receive(:new_id).and_return(correlation_id)
       subject
     end
 
@@ -682,7 +679,7 @@
         expect(import_failure.relation_index).to be_present
         expect(import_failure.exception_class).to eq('ActiveRecord::RecordInvalid')
         expect(import_failure.exception_message).to be_present
-        expect(import_failure.correlation_id_value).to eq('my-correlation-id')
+        expect(import_failure.correlation_id_value).not_to be_empty
         expect(import_failure.created_at).to be_present
       end
     end
diff --git a/spec/lib/gitlab/profiler_spec.rb b/spec/lib/gitlab/profiler_spec.rb
index c27e3ca7ace24aeb0e9ce42a0c8f04a4d49ab990..8f6fb6eda653d9d3e89a9b2b577fae98454f50bb 100644
--- a/spec/lib/gitlab/profiler_spec.rb
+++ b/spec/lib/gitlab/profiler_spec.rb
@@ -84,7 +84,7 @@
           expect(severity).to eq(Logger::DEBUG)
           expect(message).to include('public').and include(described_class::FILTERED_STRING)
           expect(message).not_to include(private_token)
-        end.twice
+        end.at_least(1) # This spec could be wrapped in more blocks in the future
 
         custom_logger.debug("public #{private_token}")
       end
diff --git a/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb b/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb
deleted file mode 100644
index d5ed939485a48952f0da0635d9562817dc8b2b8c..0000000000000000000000000000000000000000
--- a/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::SidekiqMiddleware::CorrelationInjector do
-  class TestWorker
-    include ApplicationWorker
-  end
-
-  before do |example|
-    Sidekiq.client_middleware do |chain|
-      chain.add described_class
-    end
-  end
-
-  after do |example|
-    Sidekiq.client_middleware do |chain|
-      chain.remove described_class
-    end
-
-    Sidekiq::Queues.clear_all
-  end
-
-  around do |example|
-    Sidekiq::Testing.fake! do
-      example.run
-    end
-  end
-
-  it 'injects into payload the correlation id' do
-    expect_next_instance_of(described_class) do |instance|
-      expect(instance).to receive(:call).and_call_original
-    end
-
-    Labkit::Correlation::CorrelationId.use_id('new-correlation-id') do
-      TestWorker.perform_async(1234)
-    end
-
-    expected_job_params = {
-      "class" => "TestWorker",
-      "args" => [1234],
-      "correlation_id" => "new-correlation-id"
-    }
-
-    expect(Sidekiq::Queues.jobs_by_worker).to a_hash_including(
-      "TestWorker" => a_collection_containing_exactly(
-        a_hash_including(expected_job_params)))
-  end
-end
diff --git a/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb b/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb
deleted file mode 100644
index 27eea96340238c2591b978453836a23fadf41fb4..0000000000000000000000000000000000000000
--- a/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::SidekiqMiddleware::CorrelationLogger do
-  class TestWorker
-    include ApplicationWorker
-  end
-
-  before do |example|
-    Sidekiq::Testing.server_middleware do |chain|
-      chain.add described_class
-    end
-  end
-
-  after do |example|
-    Sidekiq::Testing.server_middleware do |chain|
-      chain.remove described_class
-    end
-  end
-
-  it 'injects into payload the correlation id', :sidekiq_might_not_need_inline do
-    expect_any_instance_of(described_class).to receive(:call).and_call_original
-
-    expect_any_instance_of(TestWorker).to receive(:perform).with(1234) do
-      expect(Labkit::Correlation::CorrelationId.current_id).to eq('new-correlation-id')
-    end
-
-    Sidekiq::Client.push(
-      'queue' => 'test',
-      'class' => TestWorker,
-      'args' => [1234],
-      'correlation_id' => 'new-correlation-id')
-  end
-end
diff --git a/spec/lib/gitlab/sidekiq_middleware_spec.rb b/spec/lib/gitlab/sidekiq_middleware_spec.rb
index aef472e0648c172ed00f81e50f0e9b893ad6e561..ef4a898bdb6b1dced10f8d167919b9d137da081b 100644
--- a/spec/lib/gitlab/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware_spec.rb
@@ -38,7 +38,7 @@ def perform(_arg)
       [
        Gitlab::SidekiqMiddleware::Monitor,
        Gitlab::SidekiqMiddleware::BatchLoader,
-       Gitlab::SidekiqMiddleware::CorrelationLogger,
+       Labkit::Middleware::Sidekiq::Server,
        Gitlab::SidekiqMiddleware::InstrumentationLogger,
        Gitlab::SidekiqStatus::ServerMiddleware,
        Gitlab::SidekiqMiddleware::Metrics,
@@ -120,7 +120,7 @@ def perform(_arg)
       # This test ensures that this does not happen
       it "invokes the chain" do
         expect_any_instance_of(Gitlab::SidekiqStatus::ClientMiddleware).to receive(:call).with(*middleware_expected_args).once.and_call_original
-        expect_any_instance_of(Gitlab::SidekiqMiddleware::CorrelationInjector).to receive(:call).with(*middleware_expected_args).once.and_call_original
+        expect_any_instance_of(Labkit::Middleware::Sidekiq::Client).to receive(:call).with(*middleware_expected_args).once.and_call_original
 
         expect { |b| chain.invoke(worker_class_arg, job, queue, redis_pool, &b) }.to yield_control.once
       end
diff --git a/spec/lib/gitlab/utils/lazy_attributes_spec.rb b/spec/lib/gitlab/utils/lazy_attributes_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c0005c194c4c3cc6522cb6fbdb68b1724d614927
--- /dev/null
+++ b/spec/lib/gitlab/utils/lazy_attributes_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+require 'fast_spec_helper'
+require 'active_support/concern'
+
+describe Gitlab::Utils::LazyAttributes do
+  subject(:klass) do
+    Class.new do
+      include Gitlab::Utils::LazyAttributes
+
+      lazy_attr_reader :number, type: Numeric
+      lazy_attr_reader :reader_1, :reader_2
+      lazy_attr_accessor :incorrect_type, :string_attribute, :accessor_2, type: String
+
+      def initialize
+        @number = -> { 1 }
+        @reader_1, @reader_2 = 'reader_1', -> { 'reader_2' }
+        @incorrect_type, @accessor_2 = -> { :incorrect_type }, -> { 'accessor_2' }
+      end
+    end
+  end
+
+  context 'class methods' do
+    it { is_expected.to respond_to(:lazy_attr_reader, :lazy_attr_accessor) }
+    it { is_expected.not_to respond_to(:define_lazy_reader, :define_lazy_writer) }
+  end
+
+  context 'instance methods' do
+    subject(:instance) { klass.new }
+
+    it do
+      is_expected.to respond_to(:number, :reader_1, :reader_2, :incorrect_type,
+                                :incorrect_type=, :accessor_2, :accessor_2=,
+                                :string_attribute, :string_attribute=)
+    end
+
+    context 'reading attributes' do
+      it 'returns the correct values for procs', :aggregate_failures do
+        expect(instance.number).to eq(1)
+        expect(instance.reader_2).to eq('reader_2')
+        expect(instance.accessor_2).to eq('accessor_2')
+      end
+
+      it 'does not return the value if the type did not match what was specified' do
+        expect(instance.incorrect_type).to be_nil
+      end
+
+      it 'only calls the block once even if it returned `nil`', :aggregate_failures do
+        expect(instance.instance_variable_get('@number')).to receive(:call).once.and_call_original
+        expect(instance.instance_variable_get('@accessor_2')).to receive(:call).once.and_call_original
+        expect(instance.instance_variable_get('@incorrect_type')).to receive(:call).once.and_call_original
+
+        2.times do
+          instance.number
+          instance.incorrect_type
+          instance.accessor_2
+        end
+      end
+    end
+
+    context 'writing attributes' do
+      it 'sets the correct values', :aggregate_failures do
+        instance.string_attribute = -> { 'updated 1' }
+        instance.accessor_2 = nil
+
+        expect(instance.string_attribute).to eq('updated 1')
+        expect(instance.accessor_2).to be_nil
+      end
+    end
+  end
+end
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 4fe0d4a59832bb7ad50624bb5b56d016e80bfb40..12e6e7c7a093bd37197448fc4fa897a1168e26a7 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -389,6 +389,12 @@
           end
         end
       end
+
+      it_behaves_like 'storing arguments in the application context' do
+        let(:expected_params) { { user: key.user.username, project: project.full_path } }
+
+        subject { push(key, project) }
+      end
     end
 
     context "access denied" do
@@ -885,6 +891,12 @@
       post api('/internal/post_receive'), params: valid_params
     end
 
+    it_behaves_like 'storing arguments in the application context' do
+      let(:expected_params) { { user: user.username, project: project.full_path } }
+
+      subject { post api('/internal/post_receive'), params: valid_params }
+    end
+
     context 'when there are merge_request push options' do
       before do
         valid_params[:push_options] = ['merge_request.create']
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 9af4f484f9993c76cb2d0b528ff43f17d5eb9c31..975e59346b8594f8c032e6693bf7a0ba39c366e5 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1626,6 +1626,14 @@
         end
       end
     end
+
+    it_behaves_like 'storing arguments in the application context' do
+      let_it_be(:user) { create(:user) }
+      let_it_be(:project) { create(:project, :public) }
+      let(:expected_params) { { user: user.username, project: project.full_path } }
+
+      subject { get api("/projects/#{project.id}", user) }
+    end
   end
 
   describe 'GET /projects/:id/users' do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 1f0119108a8504128bba602dbc93c392b4483552..6393e4829045343a8810cfb033bd9880635cb2ee 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -245,6 +245,12 @@
     Rails.cache = caching_store
   end
 
+  config.around do |example|
+    # Wrap each example in it's own context to make sure the contexts don't
+    # leak
+    Labkit::Context.with_context { example.run }
+  end
+
   config.around(:each, :clean_gitlab_redis_cache) do |example|
     redis_cache_cleanup!
 
diff --git a/spec/support/shared_examples/logging_application_context_shared_examples.rb b/spec/support/shared_examples/logging_application_context_shared_examples.rb
new file mode 100644
index 0000000000000000000000000000000000000000..038ede884c88c403b395ec6c0c997fee9e9dbc16
--- /dev/null
+++ b/spec/support/shared_examples/logging_application_context_shared_examples.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'storing arguments in the application context' do
+  around do |example|
+    Labkit::Context.with_context { example.run }
+  end
+
+  it 'places the expected params in the application context' do
+    # Stub the clearing of the context so we can validate it later
+    # The `around` block above makes sure we do clean it up later
+    allow(Labkit::Context).to receive(:pop)
+
+    subject
+
+    Labkit::Context.with_context do |context|
+      expect(context.to_h)
+        .to include(log_hash(expected_params))
+    end
+  end
+
+  def log_hash(hash)
+    hash.transform_keys! { |key| "meta.#{key}" }
+  end
+end