Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add action mailer instrumentation #1280

Merged
merged 24 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5050c05
adds process.action_mailer instrumentation without spec
ericmustin Apr 22, 2019
af4c122
fix typo on active support notificaiton event name
ericmustin Apr 22, 2019
0e4945b
add specs and contrib doc information
ericmustin Apr 22, 2019
94ca301
fix comments typo
ericmustin Apr 22, 2019
aa3615a
rubocop and typo fix
ericmustin Apr 22, 2019
4e6c209
update actionmailer integration, add delivery event
ericmustin Dec 8, 2020
e06f91e
add tests and seperate payload information for different spans
ericmustin Dec 8, 2020
ad9d215
Merge branch 'master' into issue_250_actionmailer_instrumentation
ericmustin Dec 8, 2020
1457b14
remove old specs
ericmustin Dec 8, 2020
954a4b9
Merge branch 'master' into issue_250_actionmailer_instrumentation
marcotc Dec 8, 2020
26bd78c
update with master
ericmustin Jul 29, 2021
759a627
update to check auto instrumentation
ericmustin Aug 4, 2021
1be73af
update specs for action mailer span types
ericmustin Aug 4, 2021
ef5133e
linting
ericmustin Aug 4, 2021
96402e3
add span type to events, wip tests
ericmustin Aug 4, 2021
93e1177
merge upstream
ericmustin Aug 4, 2021
668958c
use correct span type constants and linting
ericmustin Aug 4, 2021
4f48406
Merge branch 'master' into issue_250_actionmailer_instrumentation
ericmustin Aug 4, 2021
b43fa1a
sorbet updates
ericmustin Aug 5, 2021
6f39604
add note on test helpers
ericmustin Aug 5, 2021
fb70948
Merge branch 'master' into issue_250_actionmailer_instrumentation
ericmustin Aug 18, 2021
e2cac58
action_mailer: add email_data config option
ericmustin Aug 18, 2021
c929fc6
action_mailer: clean up formatting and fix config naming
ericmustin Aug 18, 2021
c21540f
action_mailer: fix constants
ericmustin Aug 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ namespace :spec do

[
:action_cable,
:action_mailer,
:action_pack,
:action_view,
:active_model_serializers,
Expand Down Expand Up @@ -618,9 +619,11 @@ task :ci do
declare 'bundle exec appraisal rails6-postgres rake spec:railsdisableenv'
# Rails specs
declare 'bundle exec appraisal rails5-mysql2 rake spec:action_cable'
declare 'bundle exec appraisal rails5-mysql2 rake spec:action_mailer'
declare 'bundle exec appraisal rails5-mysql2 rake spec:rails'
declare 'bundle exec appraisal rails5-postgres rake spec:rails'
# declare 'bundle exec appraisal rails6-mysql2 rake spec:action_cable' # TODO: Hangs CI jobs... fix and re-enable.
declare 'bundle exec appraisal rails6-mysql2 rake spec:action_mailer'
declare 'bundle exec appraisal rails6-mysql2 rake spec:rails'
declare 'bundle exec appraisal rails6-postgres rake spec:rails'

Expand Down Expand Up @@ -702,9 +705,11 @@ task :ci do
declare 'bundle exec appraisal rails6-postgres rake spec:railsdisableenv'
# Rails specs
declare 'bundle exec appraisal rails5-mysql2 rake spec:action_cable'
declare 'bundle exec appraisal rails5-mysql2 rake spec:action_mailer'
declare 'bundle exec appraisal rails5-mysql2 rake spec:rails'
declare 'bundle exec appraisal rails5-postgres rake spec:rails'
# declare 'bundle exec appraisal rails6-mysql2 rake spec:action_cable' # TODO: Hangs CI jobs... fix and re-enable.
declare 'bundle exec appraisal rails6-mysql2 rake spec:action_mailer'
declare 'bundle exec appraisal rails6-mysql2 rake spec:rails'
declare 'bundle exec appraisal rails6-postgres rake spec:rails'

Expand Down Expand Up @@ -787,8 +792,10 @@ task :ci do
# Rails specs
declare 'bundle exec appraisal rails5-mysql2 rake spec:rails'
declare 'bundle exec appraisal rails5-postgres rake spec:rails'
declare 'bundle exec appraisal rails5-mysql2 rake spec:action_mailer'
declare 'bundle exec appraisal rails6-mysql2 rake spec:rails'
declare 'bundle exec appraisal rails6-postgres rake spec:rails'
declare 'bundle exec appraisal rails6-mysql2 rake spec:action_mailer'

# explicitly test resque-2x compatability
declare 'bundle exec appraisal resque2-redis3 rake spec:resque'
Expand Down
24 changes: 23 additions & 1 deletion docs/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ To contribute, check out the [contribution guidelines][contribution docs] and [d
- [Integration instrumentation](#integration-instrumentation)
- [Action Cable](#action-cable)
- [Action View](#action-view)
- [Action Mailer](#action-mailer)
- [Active Model Serializers](#active-model-serializers)
- [Action Pack](#action-pack)
- [Active Record](#active-record)
Expand Down Expand Up @@ -334,14 +335,15 @@ For a list of available integrations, and their configuration options, please re
| Name | Key | Versions Supported: MRI | Versions Supported: JRuby | How to configure | Gem source |
| ------------------------ | -------------------------- | ------------------------ | --------------------------| ----------------------------------- | ------------------------------------------------------------------------------ |
| Action Cable | `action_cable` | `>= 5.0` | `>= 5.0` | *[Link](#action-cable)* | *[Link](https://github.com/rails/rails/tree/master/actioncable)* |
| Action Mailer | `action_mailer` | `>= 5.0` | `>= 5.0` | *[Link](#action-mailer)* | *[Link](https://github.com/rails/rails/tree/master/actionmailer)* |
| Action View | `action_view` | `>= 3.0` | `>= 3.0` | *[Link](#action-view)* | *[Link](https://github.com/rails/rails/tree/master/actionview)* |
| Active Model Serializers | `active_model_serializers` | `>= 0.9` | `>= 0.9` | *[Link](#active-model-serializers)* | *[Link](https://github.com/rails-api/active_model_serializers)* |
| Action Pack | `action_pack` | `>= 3.0` | `>= 3.0` | *[Link](#action-pack)* | *[Link](https://github.com/rails/rails/tree/master/actionpack)* |
| Active Record | `active_record` | `>= 3.0` | `>= 3.0` | *[Link](#active-record)* | *[Link](https://github.com/rails/rails/tree/master/activerecord)* |
| Active Support | `active_support` | `>= 3.0` | `>= 3.0` | *[Link](#active-support)* | *[Link](https://github.com/rails/rails/tree/master/activesupport)* |
| AWS | `aws` | `>= 2.0` | `>= 2.0` | *[Link](#aws)* | *[Link](https://github.com/aws/aws-sdk-ruby)* |
| Concurrent Ruby | `concurrent_ruby` | `>= 0.9` | `>= 0.9` | *[Link](#concurrent-ruby)* | *[Link](https://github.com/ruby-concurrency/concurrent-ruby)* |
| Cucumber | `cucumber` | `>= 3.0` | `>= 1.7.16` | *[Link](#cucumber)* | *[Link](https://github.com/cucumber/cucumber-ruby)* |
| Cucumber | `cucumber` | `>= 3.0` | `>= 1.7.16` | *[Link](#cucumber)* | *[Link](https://github.com/cucumber/cucumber-ruby)* |
| Dalli | `dalli` | `>= 2.0` | `>= 2.0` | *[Link](#dalli)* | *[Link](https://github.com/petergoldstein/dalli)* |
| DelayedJob | `delayed_job` | `>= 4.1` | `>= 4.1` | *[Link](#delayedjob)* | *[Link](https://github.com/collectiveidea/delayed_job)* |
| Elasticsearch | `elasticsearch` | `>= 1.0` | `>= 1.0` | *[Link](#elasticsearch)* | *[Link](https://github.com/elastic/elasticsearch-ruby)* |
Expand Down Expand Up @@ -416,6 +418,26 @@ Where `options` is an optional `Hash` that accepts the following parameters:
| `service_name` | Service name used for rendering instrumentation. | `action_view` |
| `template_base_path` | Used when the template name is parsed. If you don't store your templates in the `views/` folder, you may need to change this value | `'views/'` |

### Action Mailer

The Action Mailer integration provides tracing for Rails 5 ActionMailer actions.

You can enable it through `Datadog.configure`:

```ruby
require 'ddtrace'
Datadog.configure do |c|
c.use :action_mailer, options
end
```

Where `options` is an optional `Hash` that accepts the following parameters:

| Key | Description | Default |
| --- | ----------- | ------- |
| `analytics_enabled` | Enable analytics for spans produced by this integration. `true` for on, `nil` to defer to global setting, `false` for off. | `false` |
| `service_name` | Service name used for `action_mailer` instrumentation | `'action_mailer'` |

### Active Model Serializers

The Active Model Serializers integration traces the `serialize` event for version 0.9+ and the `render` event for version 0.10+.
Expand Down
1 change: 1 addition & 0 deletions lib/ddtrace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ module Datadog
end

require 'ddtrace/contrib/action_cable/integration'
require 'ddtrace/contrib/action_mailer/integration'
require 'ddtrace/contrib/action_pack/integration'
require 'ddtrace/contrib/action_view/integration'
require 'ddtrace/contrib/active_model_serializers/integration'
Expand Down
30 changes: 30 additions & 0 deletions lib/ddtrace/contrib/action_mailer/configuration/settings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require 'ddtrace/contrib/configuration/settings'
require 'ddtrace/contrib/action_mailer/ext'

module Datadog
module Contrib
module ActionMailer
module Configuration
# Custom settings for the ActionMailer integration
class Settings < Contrib::Configuration::Settings
option :enabled do |o|
o.default { env_to_bool(Ext::ENV_ENABLED, true) }
o.lazy
end

option :analytics_enabled do |o|
o.default { env_to_bool([Ext::ENV_ANALYTICS_ENABLED, Ext::ENV_ANALYTICS_ENABLED_OLD], false) }
o.lazy
end

option :analytics_sample_rate do |o|
o.default { env_to_float([Ext::ENV_ANALYTICS_SAMPLE_RATE, Ext::ENV_ANALYTICS_SAMPLE_RATE_OLD], 1.0) }
o.lazy
end

option :service_name, default: Ext::SERVICE_NAME
end
end
end
end
end
49 changes: 49 additions & 0 deletions lib/ddtrace/contrib/action_mailer/event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require 'ddtrace/contrib/analytics'
require 'ddtrace/contrib/active_support/notifications/event'
require 'ddtrace/contrib/action_mailer/ext'

module Datadog
module Contrib
module ActionMailer
# Defines basic behaviors for an ActionMailer event.
module Event
def self.included(base)
base.send(:include, ActiveSupport::Notifications::Event)
base.send(:extend, ClassMethods)
end

# Class methods for ActionMailer events.
module ClassMethods
def span_options
{ service: configuration[:service_name] }
end

def tracer
-> { configuration[:tracer] }
end

def configuration
Datadog.configuration[:action_mailer]
end

def process(span, event, _id, payload)
span.service = configuration[:service_name]
span.resource = payload[:mailer]

# Set analytics sample rate
if Contrib::Analytics.enabled?(configuration[:analytics_enabled])
Contrib::Analytics.set_sample_rate(span, configuration[:analytics_sample_rate])
end

# Measure service stats
Contrib::Analytics.set_measured(span)

report_if_exception(span, payload)
rescue StandardError => e
Datadog.logger.debug(e.message)
end
end
end
end
end
end
30 changes: 30 additions & 0 deletions lib/ddtrace/contrib/action_mailer/events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require 'ddtrace/contrib/action_mailer/events/process'
require 'ddtrace/contrib/action_mailer/events/deliver'

module Datadog
module Contrib
module ActionMailer
# Defines collection of instrumented ActionMailer events
module Events
ALL = [
Events::Process,
Events::Deliver
].freeze

module_function

def all
self::ALL
end

def subscriptions
all.collect(&:subscriptions).collect(&:to_a).flatten
end

def subscribe!
all.each(&:subscribe!)
end
end
end
end
end
39 changes: 39 additions & 0 deletions lib/ddtrace/contrib/action_mailer/events/deliver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'ddtrace/contrib/action_mailer/ext'
require 'ddtrace/contrib/action_mailer/event'

module Datadog
module Contrib
module ActionMailer
module Events
# Defines instrumentation for process.action_mailer event
module Deliver
include ActionMailer::Event

EVENT_NAME = 'deliver.action_mailer'.freeze

module_function

def event_name
self::EVENT_NAME
end

def span_name
Ext::SPAN_DELIVER
end

def span_type
# ActionMailer creates emails like a controller
Datadog::Ext::AppTypes::Worker
end

def process(span, event, _id, payload)
super

span.set_tag(Ext::TAG_MAILER, payload[:mailer])
span.set_tag(Ext::TAG_MSG_ID, payload[:message_id])
end
end
end
end
end
end
39 changes: 39 additions & 0 deletions lib/ddtrace/contrib/action_mailer/events/process.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'ddtrace/contrib/action_mailer/ext'
require 'ddtrace/contrib/action_mailer/event'

module Datadog
module Contrib
module ActionMailer
module Events
# Defines instrumentation for process.action_mailer event
module Process
include ActionMailer::Event

EVENT_NAME = 'process.action_mailer'.freeze

module_function

def event_name
self::EVENT_NAME
end

def span_name
Ext::SPAN_PROCESS
end

def span_type
# ActionMailer creates emails like a controller
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify this? Are these top-level spans then? Do they happen on the same execution context as, say, a Rack request?

Copy link
Contributor Author

@ericmustin ericmustin Dec 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i believe they are not top level spans , but i am going to try to stress test this in a sample app, will report back when i have more. tl;dr i believe i've botched the span_type here and need to update, but want to confirm that and just generally get a bit more familiar with what these actions represent within the mailer lifecycle

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, to update: So this integration is similar to active record or action view in that there's a few different spans which could all be "top level" in the sense that they're the 1st span for the service, but generally speaking they'll rarely be the root span of the trace, usualy they're a child of action_pack controller span or a job consumer (delayed job, sidekiq, etc) span.

Here's a few examples:

  1. async job queue (deliver_later)

Image 2021-07-29 at 4 01 30 PM

  1. syncronous, no job queue (deliver_now)

Image 2021-07-29 at 1 51 29 PM

I've tried to update the span_types to reflect existing standards that are closer to their more appropriate usage, marking them as either a template (which is what .process spans do, they render the mailer templates) or worker (which is what .deliver spans do, they send emails).

Anyway lmk what you think

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I like the traces you showed above, they all seem reasonable to me 👍.

Datadog::Ext::AppTypes::Web
end

def process(span, event, _id, payload)
super

span.set_tag(Ext::TAG_ACTION, payload[:action])
span.set_tag(Ext::TAG_MAILER, payload[:mailer])
end
end
end
end
end
end
23 changes: 23 additions & 0 deletions lib/ddtrace/contrib/action_mailer/ext.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Datadog
module Contrib
module ActionMailer
# ActionMailer integration constants
module Ext
APP = 'action_mailer'.freeze
ENV_ENABLED = 'DD_TRACE_ACTION_MAILER_ENABLED'.freeze
ENV_ANALYTICS_ENABLED = 'DD_TRACE_ACTION_MAILER_ANALYTICS_ENABLED'.freeze
ENV_ANALYTICS_SAMPLE_RATE = 'DD_TRACE_ACTION_MAILER_ANALYTICS_SAMPLE_RATE'.freeze

ENV_ANALYTICS_ENABLED_OLD = 'DD_ACTION_MAILER_ANALYTICS_ENABLED'.freeze

ENV_ANALYTICS_SAMPLE_RATE_OLD = 'DD_ACTION_MAILER_ANALYTICS_SAMPLE_RATE'.freeze
SERVICE_NAME = 'action_mailer'.freeze
SPAN_PROCESS = 'action_mailer.process'.freeze
SPAN_DELIVER = 'action_mailer.deliver'.freeze
TAG_ACTION = 'action_mailer.action'.freeze
TAG_MAILER = 'action_mailer.mailer'.freeze
TAG_MSG_ID = 'action_mailer.message_id'.freeze
end
end
end
end
38 changes: 38 additions & 0 deletions lib/ddtrace/contrib/action_mailer/integration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require 'ddtrace/contrib/integration'
require 'ddtrace/contrib/action_mailer/configuration/settings'
require 'ddtrace/contrib/action_mailer/patcher'

module Datadog
module Contrib
module ActionMailer
# Description of ActionMailer integration
class Integration
include Contrib::Integration

MINIMUM_VERSION = Gem::Version.new('5.0.0')

register_as :action_mailer, auto_patch: false

def self.version
Gem.loaded_specs['actionmailer'] && Gem.loaded_specs['actionmailer'].version
end

def self.loaded?
!defined?(::ActionMailer).nil?
end

def self.compatible?
super && version >= MINIMUM_VERSION && !defined?(::ActiveSupport::Notifications).nil?
end

def default_configuration
Configuration::Settings.new
end

def patcher
Patcher
end
end
end
end
end
26 changes: 26 additions & 0 deletions lib/ddtrace/contrib/action_mailer/patcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'ddtrace/contrib/patcher'
require 'ddtrace/ext/app_types'
require 'ddtrace/contrib/action_mailer/ext'
require 'ddtrace/contrib/action_mailer/events'

module Datadog
module Contrib
module ActionMailer
# Patcher enables patching of 'action_mailer' module.
module Patcher
include Contrib::Patcher

module_function

def target_version
Integration.version
end

def patch
# Subscribe to ActionMailer events
Events.subscribe!
end
end
end
end
end
Loading