Skip to content

Commit

Permalink
Merge pull request #1364 from appsignal/ownership-integration
Browse files Browse the repository at this point in the history
Ownership integration
  • Loading branch information
unflxw authored Jan 17, 2025
2 parents 0f2a138 + 865b348 commit c307d46
Show file tree
Hide file tree
Showing 17 changed files with 915 additions and 2 deletions.
22 changes: 22 additions & 0 deletions .changesets/add-support-for-ownership-gem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
bump: patch
type: add
---

Add support for the [Ownership](https://github.com/ankane/ownership) gem, which is used to mark different segments of the application as owned by specific teams.

The AppSignal sample will be tagged with the given owner:

```ruby
class OrdersController < ApplicationController
owner :logistics
# Transactions for requests handled by this controller will be tagged
# in AppSignal with the "owner" tag set to "logistics"
end
```

If several owners are set within the same transaction, the last owner will take precedence. If an error is reported in the transaction, the owner tag will be set to the owner that was set when the error was raised.

Set the [`ownership_set_namespace` configuration option](https://docs.appsignal.com/ruby/configuration/options.html#option-ownership_set_namespace) to `true` to also set the AppSignal sample's namespace to the owner. Note that doing so will cause existing performance and error incidents to be re-created with the new namespace.

Set the [`instrument_ownership` configuration option](https://docs.appsignal.com/ruby/configuration/options.html#option-instrument_ownership) to `false` to disable the integration with the Ownership gem.
164 changes: 163 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# This is a generated file by the `rake build_matrix:github:generate` task.
# See `build_matrix.yml` for the build matrix.
# Generate this file with `rake build_matrix:github:generate`.
# Generated job count: 151
# Generated job count: 157
---
name: Ruby gem CI
'on':
Expand Down Expand Up @@ -318,6 +318,33 @@ jobs:
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/http5.gemfile
ruby_3-4-1__ownership_ubuntu-latest:
name: Ruby 3.4.1 - ownership
needs: ruby_3-4-1_ubuntu-latest
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Install Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.4.1
bundler-cache: true
- name: Install gem extension
run: "./script/bundler_wrapper exec rake extension:install"
- name: Print extension install report
run: "[ -e ext/install.report ] && cat ext/install.report || echo 'No ext/install.report
file found'"
- name: Print Makefile log file
run: "[ -f ext/mkmf.log ] && cat ext/mkmf.log || echo 'No ext/mkmf.log file
found'"
- name: Run tests
run: "./script/bundler_wrapper exec rake test"
env:
RAILS_ENV: test
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/ownership.gemfile
ruby_3-4-1__padrino_ubuntu-latest:
name: Ruby 3.4.1 - padrino
needs: ruby_3-4-1_ubuntu-latest
Expand Down Expand Up @@ -970,6 +997,33 @@ jobs:
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/http5.gemfile
ruby_3-3-4__ownership_ubuntu-latest:
name: Ruby 3.3.4 - ownership
needs: ruby_3-3-4_ubuntu-latest
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Install Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.3.4
bundler-cache: true
- name: Install gem extension
run: "./script/bundler_wrapper exec rake extension:install"
- name: Print extension install report
run: "[ -e ext/install.report ] && cat ext/install.report || echo 'No ext/install.report
file found'"
- name: Print Makefile log file
run: "[ -f ext/mkmf.log ] && cat ext/mkmf.log || echo 'No ext/mkmf.log file
found'"
- name: Run tests
run: "./script/bundler_wrapper exec rake test"
env:
RAILS_ENV: test
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/ownership.gemfile
ruby_3-3-4__padrino_ubuntu-latest:
name: Ruby 3.3.4 - padrino
needs: ruby_3-3-4_ubuntu-latest
Expand Down Expand Up @@ -1649,6 +1703,33 @@ jobs:
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/http5.gemfile
ruby_3-2-5__ownership_ubuntu-latest:
name: Ruby 3.2.5 - ownership
needs: ruby_3-2-5_ubuntu-latest
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Install Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.2.5
bundler-cache: true
- name: Install gem extension
run: "./script/bundler_wrapper exec rake extension:install"
- name: Print extension install report
run: "[ -e ext/install.report ] && cat ext/install.report || echo 'No ext/install.report
file found'"
- name: Print Makefile log file
run: "[ -f ext/mkmf.log ] && cat ext/mkmf.log || echo 'No ext/mkmf.log file
found'"
- name: Run tests
run: "./script/bundler_wrapper exec rake test"
env:
RAILS_ENV: test
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/ownership.gemfile
ruby_3-2-5__padrino_ubuntu-latest:
name: Ruby 3.2.5 - padrino
needs: ruby_3-2-5_ubuntu-latest
Expand Down Expand Up @@ -2328,6 +2409,33 @@ jobs:
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/http5.gemfile
ruby_3-1-6__ownership_ubuntu-latest:
name: Ruby 3.1.6 - ownership
needs: ruby_3-1-6_ubuntu-latest
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Install Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.1.6
bundler-cache: true
- name: Install gem extension
run: "./script/bundler_wrapper exec rake extension:install"
- name: Print extension install report
run: "[ -e ext/install.report ] && cat ext/install.report || echo 'No ext/install.report
file found'"
- name: Print Makefile log file
run: "[ -f ext/mkmf.log ] && cat ext/mkmf.log || echo 'No ext/mkmf.log file
found'"
- name: Run tests
run: "./script/bundler_wrapper exec rake test"
env:
RAILS_ENV: test
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/ownership.gemfile
ruby_3-1-6__padrino_ubuntu-latest:
name: Ruby 3.1.6 - padrino
needs: ruby_3-1-6_ubuntu-latest
Expand Down Expand Up @@ -2953,6 +3061,33 @@ jobs:
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/http5.gemfile
ruby_3-0-7__ownership_ubuntu-latest:
name: Ruby 3.0.7 - ownership
needs: ruby_3-0-7_ubuntu-latest
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Install Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.0.7
bundler-cache: true
- name: Install gem extension
run: "./script/bundler_wrapper exec rake extension:install"
- name: Print extension install report
run: "[ -e ext/install.report ] && cat ext/install.report || echo 'No ext/install.report
file found'"
- name: Print Makefile log file
run: "[ -f ext/mkmf.log ] && cat ext/mkmf.log || echo 'No ext/mkmf.log file
found'"
- name: Run tests
run: "./script/bundler_wrapper exec rake test"
env:
RAILS_ENV: test
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/ownership.gemfile
ruby_3-0-7__padrino_ubuntu-latest:
name: Ruby 3.0.7 - padrino
needs: ruby_3-0-7_ubuntu-latest
Expand Down Expand Up @@ -3497,6 +3632,33 @@ jobs:
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/http5.gemfile
ruby_2-7-8__ownership_ubuntu-latest:
name: Ruby 2.7.8 - ownership
needs: ruby_2-7-8_ubuntu-latest
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Install Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.7.8
bundler-cache: true
- name: Install gem extension
run: "./script/bundler_wrapper exec rake extension:install"
- name: Print extension install report
run: "[ -e ext/install.report ] && cat ext/install.report || echo 'No ext/install.report
file found'"
- name: Print Makefile log file
run: "[ -f ext/mkmf.log ] && cat ext/mkmf.log || echo 'No ext/mkmf.log file
found'"
- name: Run tests
run: "./script/bundler_wrapper exec rake test"
env:
RAILS_ENV: test
JRUBY_OPTS: ''
COV: '1'
BUNDLE_GEMFILE: gemfiles/ownership.gemfile
ruby_2-7-8__padrino_ubuntu-latest:
name: Ruby 2.7.8 - padrino
needs: ruby_2-7-8_ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions build_matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ matrix:
- "3.2.5"
- "3.1.6"
- gem: "http5"
- gem: "ownership"
- gem: "padrino"
- gem: "psych-3"
only:
Expand Down
5 changes: 5 additions & 0 deletions gemfiles/ownership.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
source 'https://rubygems.org'

gem 'ownership'

gemspec :path => '../'
4 changes: 4 additions & 0 deletions lib/appsignal/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,12 @@ def dsl_config_file?
:ignore_namespaces => [],
:instrument_http_rb => true,
:instrument_net_http => true,
:instrument_ownership => true,
:instrument_redis => true,
:instrument_sequel => true,
:log => "file",
:logging_endpoint => "https://appsignal-endpoint.net",
:ownership_set_namespace => false,
:request_headers => %w[
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_CONNECTION
Expand Down Expand Up @@ -181,8 +183,10 @@ def dsl_config_file?
:files_world_accessible => "APPSIGNAL_FILES_WORLD_ACCESSIBLE",
:instrument_http_rb => "APPSIGNAL_INSTRUMENT_HTTP_RB",
:instrument_net_http => "APPSIGNAL_INSTRUMENT_NET_HTTP",
:instrument_ownership => "APPSIGNAL_INSTRUMENT_OWNERSHIP",
:instrument_redis => "APPSIGNAL_INSTRUMENT_REDIS",
:instrument_sequel => "APPSIGNAL_INSTRUMENT_SEQUEL",
:ownership_set_namespace => "APPSIGNAL_OWNERSHIP_SET_NAMESPACE",
:running_in_container => "APPSIGNAL_RUNNING_IN_CONTAINER",
:send_environment_metadata => "APPSIGNAL_SEND_ENVIRONMENT_METADATA",
:send_params => "APPSIGNAL_SEND_PARAMS",
Expand Down
1 change: 1 addition & 0 deletions lib/appsignal/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def self.report(key)
hanami
hiredis
mongo_ruby_driver
ownership
padrino
passenger
puma
Expand Down
1 change: 1 addition & 0 deletions lib/appsignal/hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def truncate(text)
require "appsignal/hooks/http"
require "appsignal/hooks/mri"
require "appsignal/hooks/net_http"
require "appsignal/hooks/ownership"
require "appsignal/hooks/passenger"
require "appsignal/hooks/puma"
require "appsignal/hooks/rake"
Expand Down
44 changes: 44 additions & 0 deletions lib/appsignal/hooks/ownership.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

module Appsignal
class Hooks
# @api private
class OwnershipHook < Appsignal::Hooks::Hook
register :ownership

def dependencies_present?
defined?(::Ownership) &&
Gem::Version.new(::Ownership::VERSION) >= Gem::Version.new("0.2.0") &&
Appsignal.config &&
Appsignal.config[:instrument_ownership]
end

def install
require "appsignal/integrations/ownership"

# If a transaction is created in a code context that has an owner,
# set the namespace of the transaction to the owner.
Appsignal::Transaction.after_create <<
Appsignal::Integrations::OwnershipIntegrationHelper.method(:after_create)

# If an error was reported in a code context that has an owner,
# set the namespace of the transaction to the owner.
# In some circumstances, this will be more accurate than the last owner
# that was set for the transaction, which is what would otherwise be
# reported.
Appsignal::Transaction.before_complete <<
Appsignal::Integrations::OwnershipIntegrationHelper.method(:before_complete)

# If an owner is set in a code context that has an active transaction,
# set the namespace of the transaction to the owner.
unless ::Ownership.singleton_class.included_modules.include?(
Appsignal::Integrations::OwnershipIntegration
)
::Ownership.singleton_class.prepend Appsignal::Integrations::OwnershipIntegration
end

Appsignal::Environment.report_enabled("ownership")
end
end
end
end
51 changes: 51 additions & 0 deletions lib/appsignal/integrations/ownership.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

module Appsignal
module Integrations
# @api private
module OwnershipIntegration
# Implement the `around_change` logic by monkey-patching the reader,
# instead of by using the `around_change=` writer. This allows customers
# to use the `around_change=` writer in their own code without
# accidentally overriding AppSignal's instrumentation.
def around_change
proc do |owner, block|
OwnershipIntegrationHelper.set(Appsignal::Transaction.current, owner)

original = super

if original
original.call(owner, block)
else
block.call
end
end
end
end

module OwnershipIntegrationHelper
class << self
def set(transaction, owner)
return if owner.nil?

transaction.add_tags(:owner => owner)
transaction.set_namespace(owner) if set_namespace?
end

def after_create(transaction)
set(transaction, ::Ownership.owner)
end

def before_complete(transaction, error)
set(transaction, error.owner) if error.respond_to?(:owner)
end

private

def set_namespace?
Appsignal.config && Appsignal.config[:ownership_set_namespace]
end
end
end
end
end
Loading

0 comments on commit c307d46

Please sign in to comment.