Skip to content

Commit

Permalink
Add set_custom_data instrumentation helper (#1149)
Browse files Browse the repository at this point in the history
I saw we tell people in our docs to use the
`Appsignal::Transaction#set_sample_data` method. Let's not do that.

Here's a helper to hide all those internals away and make it less likely
to break when setting custom data as sample data.

I considered adding logic to merge the custom data, but merging these
values is quite tricky because it can be both a Hash and an Array as the
root object. It's not something I want to think about right now. If we
want to add this in the future, we can also name that helper
`add_custom_data` to differentiate between setting and merging the
custom data.

Closes #1147
  • Loading branch information
tombruijn committed Jul 8, 2024
1 parent 1e6d0b3 commit 875e443
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 7 deletions.
6 changes: 6 additions & 0 deletions .changesets/add-set_custom_data-helper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
bump: patch
type: add
---

Add `Appsignal.set_custom_data` helper to set custom data on the transaction. Previously, this could only be set with `Appsignal::Transaction.current.set_custom_data("custom_data", ...)`. This helper makes setting the custom data more convenient.
29 changes: 29 additions & 0 deletions lib/appsignal/helpers/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,35 @@ def set_namespace(namespace)
Appsignal::Transaction.current.set_namespace(namespace)
end

# Set custom data on the current transaction.
#
# Add extra information about the request or background that cannot be
# expressed in tags, like nested data structures.
#
# When this method is called multiple times, it will overwrite the
# previously set value.
#
# @example
# Appsignal.set_custom_data(:user => { :locale => "en" })
# Appsignal.set_custom_data([
# "array with data",
# :options => { :verbose => true }
# ])
#
# @since 3.10.0
# @see Transaction#set_custom_data
# @see https://docs.appsignal.com/guides/custom-data/sample-data.html
# Sample data guide
# @param data [Hash/Array]
# @return [void]
def set_custom_data(data)
return unless active?
return unless Appsignal::Transaction.current?

transaction = Appsignal::Transaction.current
transaction.set_custom_data(data)
end

# Set tags on the current transaction.
#
# Tags are extra bits of information that are added to transaction and
Expand Down
27 changes: 25 additions & 2 deletions lib/appsignal/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def clear_current_transaction!
end

attr_reader :ext, :transaction_id, :action, :namespace, :request, :paused, :tags, :options,
:discarded, :breadcrumbs
:discarded, :breadcrumbs, :custom_data

def initialize(transaction_id, namespace, request, options = {})
@transaction_id = transaction_id
Expand All @@ -84,6 +84,7 @@ def initialize(transaction_id, namespace, request, options = {})
@paused = false
@discarded = false
@tags = {}
@custom_data = nil
@breadcrumbs = []
@store = Hash.new({})
@options = options
Expand Down Expand Up @@ -213,6 +214,27 @@ def set_tags(given_tags = {})
@tags.merge!(given_tags)
end

# Set custom data on the transaction.
#
# When this method is called multiple times, it will overwrite the
# previously set value.
#
# @since 3.10.0
# @see Appsignal.set_custom_data
# @see https://docs.appsignal.com/guides/custom-data/sample-data.html
# Sample data guide
# @param data [Hash/Array]
# @return [void]
def set_custom_data(data)
case data
when Array, Hash
@custom_data = data
else
Appsignal.internal_logger
.error("set_custom_data: Unsupported data type #{data.class} received.")
end
end

# Add breadcrumbs to the transaction.
#
# @param category [String] category of breadcrumb
Expand Down Expand Up @@ -395,7 +417,8 @@ def sample_data
:session_data => sanitized_session_data,
:metadata => sanitized_metadata,
:tags => sanitized_tags,
:breadcrumbs => breadcrumbs
:breadcrumbs => breadcrumbs,
:custom_data => custom_data
}.each do |key, data|
set_sample_data(key, data)
end
Expand Down
79 changes: 79 additions & 0 deletions spec/lib/appsignal/transaction_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,85 @@ def create_transaction(id = transaction_id)
end
end

describe "#set_custom_data" do
let(:log_stream) { std_stream }
let(:logs) { log_contents(log_stream) }
around { |example| use_logger_with(log_stream) { example.run } }

it "stores custom Hash data on the transaction" do
transaction.set_custom_data(
:user => {
:id => 123,
:locale => "abc"
},
:organization => {
:slug => "appsignal",
:plan => "enterprise"
}
)

transaction._sample
expect(transaction).to include_custom_data(
"user" => {
"id" => 123,
"locale" => "abc"
},
"organization" => {
"slug" => "appsignal",
"plan" => "enterprise"
}
)
end

it "stores custom Array data on the transaction" do
transaction.set_custom_data([
[123, "abc"],
["appsignal", "enterprise"]
])

transaction._sample
expect(transaction).to include_custom_data([
[123, "abc"],
["appsignal", "enterprise"]
])
end

it "does not store non Hash or Array custom data" do
transaction.set_custom_data("abc")
transaction._sample
expect(transaction).to_not include_custom_data

transaction.set_custom_data(123)
transaction._sample
expect(transaction).to_not include_custom_data

transaction.set_custom_data(Object.new)
transaction._sample
expect(transaction).to_not include_custom_data

expect(logs).to contains_log(
:error,
"set_custom_data: Unsupported data type String received."
)
expect(logs).to contains_log(
:error,
"set_custom_data: Unsupported data type Integer received."
)
expect(logs).to contains_log(
:error,
"set_custom_data: Unsupported data type String received."
)
end

it "overwrites the custom data if called multiple times" do
transaction.set_custom_data("user" => { "id" => 123 })
transaction.set_custom_data("user" => { "id" => 456 })

transaction._sample
expect(transaction).to include_custom_data("user" => { "id" => 456 })
end
end

describe "#add_breadcrumb" do
context "when over the limit" do
before do
Expand Down
47 changes: 42 additions & 5 deletions spec/lib/appsignal_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,12 @@
Appsignal.tag_request(:tag => "tag")
end
end

describe ".set_custom_data" do
it "does not raise an error" do
Appsignal.set_custom_data(:data => "value")
end
end
end

context "with config and started" do
Expand Down Expand Up @@ -480,15 +486,13 @@
end

describe ".tag_request" do
around do |example|
start_agent
with_current_transaction(transaction) { example.run }
end
before { start_agent }

context "with transaction" do
let(:transaction) { http_request_transaction }
before { set_current_transaction(transaction) }

it "calls set_tags on the current transaction" do
it "sets tags on the current transaction" do
Appsignal.tag_request("a" => "b")

transaction._sample
Expand Down Expand Up @@ -557,6 +561,39 @@
end
end

describe ".set_custom_data" do
before { start_agent }

context "with transaction" do
let(:transaction) { http_request_transaction }
before { set_current_transaction transaction }

it "sets custom data on the current transaction" do
Appsignal.set_custom_data(
:user => { :id => 123 },
:organization => { :slug => "appsignal" }
)

transaction._sample
expect(transaction).to include_custom_data(
"user" => { "id" => 123 },
"organization" => { "slug" => "appsignal" }
)
end
end

context "without transaction" do
it "does not set tags on the transaction" do
Appsignal.set_custom_data(
:user => { :id => 123 },
:organization => { :slug => "appsignal" }
)

expect_any_instance_of(Appsignal::Transaction).to_not receive(:set_custom_data)
end
end
end

describe ".add_breadcrumb" do
around do |example|
start_agent
Expand Down

0 comments on commit 875e443

Please sign in to comment.