Skip to content

Commit

Permalink
Merge pull request #1166 from appsignal/sample-data-helpers
Browse files Browse the repository at this point in the history
Add sample data helpers
  • Loading branch information
tombruijn authored Jul 10, 2024
2 parents 9f6ed72 + 7d82dff commit c35e519
Show file tree
Hide file tree
Showing 6 changed files with 567 additions and 36 deletions.
10 changes: 10 additions & 0 deletions .changesets/add-appsignal-set_headers-helper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
bump: patch
type: add
---

Add `Appsignal.set_headers` helper. Set custom request headers on the current transaction with the `Appsignal.set_headers` helper. Note that this will overwrite any request headers that would be set automatically on the transaction. When this method is called multiple times, it will overwrite the previously set value.

```ruby
Appsignal.set_headers("PATH_INFO" => "/some-path", "HTTP_USER_AGENT" => "Firefox")
```
10 changes: 10 additions & 0 deletions .changesets/add-appsignal-set_session_data-helper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
bump: patch
type: add
---

Add `Appsignal.set_session_data` helper. Set custom session data on the current transaction with the `Appsignal.set_session_data` helper. Note that this will overwrite any request session data that would be set automatically on the transaction. When this method is called multiple times, it will overwrite the previously set value.

```ruby
Appsignal.set_session_data("data1" => "value1", "data2" => "value2")
```
123 changes: 121 additions & 2 deletions lib/appsignal/helpers/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -565,8 +565,8 @@ def tag_request(tags = {})
# A block can be given to this method to defer the fetching and parsing
# of the parameters until and only when the transaction is sampled.
#
# When both the `params` and a block is given to this method, the
# `params` argument is leading and the block will _not_ be called.
# When both the `params` argument and a block is given to this method,
# the `params` argument is leading and the block will _not_ be called.
#
# @example Set parameters
# Appsignal.set_params("param1" => "value1")
Expand Down Expand Up @@ -608,6 +608,125 @@ def set_params(params = nil, &block)
transaction.set_params(params, &block)
end

# Set session data on the current transaction.
#
# Session data is automatically set by most of our integrations. It
# should not be necessary to call this method unless you want to report
# different session data.
#
# To filter session data, see our session data filtering guide.
#
# When this method is called multiple times, it will overwrite the
# previously set value.
#
# A block can be given to this method to defer the fetching and parsing
# of the session data until and only when the transaction is sampled.
#
# When both the `session_data` argument and a block is given to this
# method, the `session_data` argument is leading and the block will _not_
# be called.
#
# @example Set session data
# Appsignal.set_session_data("data" => "value")
#
# @example Calling `set_session_data` multiple times will only keep the last call
# Appsignal.set_session_data("data1" => "value1")
# Appsignal.set_session_data("data2" => "value2")
# # The session data is: { "data2" => "value2" }
#
# @example Calling `set_session_data` with a block
# Appsignal.set_session_data do
# # Some slow code to parse session data
# JSON.parse('{"data": "value"}')
# end
# # The session data is: { "data" => "value" }
#
# @example Calling `set_session_data` with a session_data argument and a block
# Appsignal.set_session_data("argument" => "argument value") do
# # Some slow code to parse session data
# JSON.parse('{"data": "value"}')
# end
# # The session data is: { "argument" => "argument value" }
#
# @since 3.11.0
# @param session_data [Hash] The session data to set on the transaction.
# @yield This block is called when the transaction is sampled. The block's
# return value will become the new session data.
# @see https://docs.appsignal.com/guides/custom-data/sample-data.html
# Sample data guide
# @see https://docs.appsignal.com/guides/filter-data/filter-session-data.html
# Session data filtering guide
# @see Transaction#set_session_data
# @return [void]
def set_session_data(session_data = nil, &block)
return unless active?
return unless Appsignal::Transaction.current?

transaction = Appsignal::Transaction.current
transaction.set_session_data(session_data, &block)
end

# Set request headers on the current transaction.
#
# Request headers are automatically set by most of our integrations. It
# should not be necessary to call this method unless you want to report
# different request headers.
#
# To filter request headers, see our session data filtering guide.
#
# When this method is called multiple times, it will overwrite the
# previously set value.
#
# A block can be given to this method to defer the fetching and parsing
# of the request headers until and only when the transaction is sampled.
#
# When both the `request_headers` argument and a block is given to this
# method, the `request_headers` argument is leading and the block will
# _not_ be called.
#
# @example Set request headers
# Appsignal.set_headers(
# "PATH_INFO" => "/some-path",
# "HTTP_USER_AGENT" => "Firefox"
# )
#
# @example Calling `set_headers` multiple times will only keep the last call
# Appsignal.set_headers("PATH_INFO" => "/some-path")
# Appsignal.set_headers("HTTP_USER_AGENT" => "Firefox")
# # The request headers are: { "HTTP_USER_AGENT" => "Firefox" }
#
# @example Calling `set_headers` with a block
# Appsignal.set_headers do
# # Some slow code to parse request headers
# JSON.parse('{"PATH_INFO": "/some-path"}')
# end
# # The session data is: { "PATH_INFO" => "/some-path" }
#
# @example Calling `set_headers` with a headers argument and a block
# Appsignal.set_headers("PATH_INFO" => "/some-path") do
# # Some slow code to parse session data
# JSON.parse('{"PATH_INFO": "/block-path"}')
# end
# # The session data is: { "PATH_INFO" => "/some-path" }
#
# @since 3.11.0
# @param headers [Hash] The request headers to set on the transaction.
# @yield This block is called when the transaction is sampled. The block's
# return value will become the new request headers.
# @see https://docs.appsignal.com/guides/custom-data/sample-data.html
# Sample data guide
# @see https://docs.appsignal.com/guides/filter-data/filter-headers.html
# Request headers filtering guide
# @see Transaction#set_headers
# @return [void]
def set_headers(headers = nil, &block)
return unless active?
return unless Appsignal::Transaction.current?

transaction = Appsignal::Transaction.current
transaction.set_headers(headers, &block)
end

# Add breadcrumbs to the transaction.
#
# Breadcrumbs can be used to trace what path a user has taken
Expand Down
145 changes: 118 additions & 27 deletions lib/appsignal/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ def initialize(transaction_id, namespace, request, options = {})
@options = options
@options[:params_method] ||= :params
@params = nil
@session_data = nil
@headers = nil

@ext = Appsignal::Extension.start_transaction(
@transaction_id,
Expand Down Expand Up @@ -207,6 +209,8 @@ def params=(given_params)
# @yield This block is called when the transaction is sampled. The block's
# return value will become the new parameters.
# @return [void]
#
# @see #set_params
# @see Helpers::Instrumentation#set_params_if_nil
def set_params_if_nil(given_params = nil, &block)
set_params(given_params, &block) unless @params
Expand All @@ -230,6 +234,84 @@ def set_tags(given_tags = {})
@tags.merge!(given_tags)
end

# Set session data on the transaction.
#
# When both the `given_session_data` and a block is given to this method,
# the `given_session_data` argument is leading and the block will _not_ be
# called.
#
# @param given_session_data [Hash] A hash containing session data.
# @yield This block is called when the transaction is sampled. The block's
# return value will become the new session data.
# @return [void]
#
# @since 3.10.1
# @see Helpers::Instrumentation#set_session_data
# @see https://docs.appsignal.com/guides/custom-data/sample-data.html
# Sample data guide
def set_session_data(given_session_data = nil, &block)
@session_data = block if block
@session_data = given_session_data if given_session_data
end

# Set session data on the transaction if not already set.
#
# When both the `given_session_data` and a block is given to this method,
# the `given_session_data` argument is leading and the block will _not_ be
# called.
#
# @param given_session_data [Hash] A hash containing session data.
# @yield This block is called when the transaction is sampled. The block's
# return value will become the new session data.
# @return [void]
#
# @since 3.10.1
# @see #set_session_data
# @see https://docs.appsignal.com/guides/custom-data/sample-data.html
# Sample data guide
def set_session_data_if_nil(given_session_data = nil, &block)
set_session_data(given_session_data, &block) unless @session_data
end

# Set headers on the transaction.
#
# When both the `given_headers` and a block is given to this method,
# the `given_headers` argument is leading and the block will _not_ be
# called.
#
# @param given_headers [Hash] A hash containing headers.
# @yield This block is called when the transaction is sampled. The block's
# return value will become the new headers.
# @return [void]
#
# @since 3.10.1
# @see Helpers::Instrumentation#set_headers
# @see https://docs.appsignal.com/guides/custom-data/sample-data.html
# Sample data guide
def set_headers(given_headers = nil, &block)
@headers = block if block
@headers = given_headers if given_headers
end

# Set headers on the transaction if not already set.
#
# When both the `given_headers` and a block is given to this method,
# the `given_headers` argument is leading and the block will _not_ be
# called.
#
# @param given_headers [Hash] A hash containing headers.
# @yield This block is called when the transaction is sampled. The block's
# return value will become the new headers.
# @return [void]
#
# @since 3.10.1
# @see #set_headers
# @see https://docs.appsignal.com/guides/custom-data/sample-data.html
# Sample data guide
def set_headers_if_nil(given_headers = nil, &block)
set_headers(given_headers, &block) unless @headers
end

# Set custom data on the transaction.
#
# When this method is called multiple times, it will overwrite the
Expand Down Expand Up @@ -619,21 +701,15 @@ def request_params
end
end

# Returns sanitized environment for a transaction.
#
# The environment of a transaction can contain a lot of information, not
# all of it useful for debugging.
#
# @return [nil] if no environment is present.
# @return [Hash<String, Object>]
def sanitized_environment
env = environment
return if env.empty?

{}.tap do |out|
Appsignal.config[:request_headers].each do |key|
out[key] = env[key] if env[key]
def session_data
if @session_data
if @session_data.respond_to? :call
@session_data.call
else
@session_data
end
elsif request.respond_to?(:session)
request.session
end
end

Expand All @@ -646,14 +722,10 @@ def sanitized_environment
# @return [nil] if the {#request} session data is `nil`.
# @return [Hash<String, Object>]
def sanitized_session_data
return if !Appsignal.config[:send_session_data] ||
!request.respond_to?(:session)

session = request.session
return unless session
return unless Appsignal.config[:send_session_data]

Appsignal::Utils::HashSanitizer.sanitize(
session.to_hash, Appsignal.config[:filter_session_data]
session_data&.to_hash, Appsignal.config[:filter_session_data]
)
end

Expand All @@ -670,17 +742,36 @@ def sanitized_metadata
.reject { |key, _value| Appsignal.config[:filter_metadata].include?(key) }
end

# Returns the environment for a transaction.
def environment
if @headers
if @headers.respond_to? :call
@headers.call
else
@headers
end
elsif request.respond_to?(:env)
request.env || {}
else
{}
end
end

# Returns sanitized environment for a transaction.
#
# Returns an empty Hash when the {#request} object doesn't listen to the
# `#env` method or the `#env` is nil.
# The environment of a transaction can contain a lot of information, not
# all of it useful for debugging.
#
# @return [nil] if no environment is present.
# @return [Hash<String, Object>]
def environment
return {} unless request.respond_to?(:env)
return {} unless request.env
def sanitized_environment
env = environment
return if env.empty?

request.env
{}.tap do |out|
Appsignal.config[:request_headers].each do |key|
out[key] = env[key] if env[key]
end
end
end

# Only keep tags if they meet the following criteria:
Expand Down
Loading

0 comments on commit c35e519

Please sign in to comment.