-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add helper to create custom markers (#1378)
Add a helper class that calls our Public Endpoint API to create custom markers. This helper is added to give people a method to report custom markers from the Ruby gem. We have chosen to do this through the public endpoint and not the Push API, because it's better fits the data flow we want. It does not support reporting deploy markers, because we only want to report those through the `revision` config option. This request is authenticated with the Push API key, app name and app environment as query parameters, which is supported by the Public Endpoint API, similar to how we report check ins. The 'normal' API requires a Personal Access Token, which gives a lot of access to the reported data. This method uses a write only authentication method. Related work: - Public Endpoint PR: appsignal/appsignal-processor-rs#1679 - Docs PR: appsignal/appsignal-docs#2419 Closes #1375
- Loading branch information
Showing
5 changed files
with
280 additions
and
0 deletions.
There are no files selected for viewing
31 changes: 31 additions & 0 deletions
31
.changesets/add-helper-to-create-custom-markers-from-the-ruby-gem.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
--- | ||
bump: minor | ||
type: add | ||
--- | ||
|
||
Add a helper to create custom markers from the Ruby gem. | ||
|
||
Create a custom marker (a little icon shown in the graph timeline on AppSignal.com) to mark events on the timeline. | ||
|
||
Create a marker with all the available options: | ||
|
||
```ruby | ||
Appsignal::CustomMarker.report( | ||
# The icon shown on the timeline | ||
:icon => "🎉", | ||
# The message shown on hover | ||
:message => "Migration completed", | ||
# Any time object or a string with a ISO8601 valid time is accepted | ||
:created_at => Time.now | ||
) | ||
``` | ||
|
||
Create a marker with just a message: | ||
|
||
```ruby | ||
Appsignal::CustomMarker.report( | ||
:message => "Migration completed", | ||
) | ||
``` | ||
|
||
_The default icon is the 🚀 icon. The default time is the time the request is received by our servers._ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# frozen_string_literal: true | ||
|
||
module Appsignal | ||
# Custom markers are used on AppSignal.com to indicate events in an | ||
# application, to give additional context on graph timelines. | ||
# | ||
# This helper class will send a request to the AppSignal public endpoint to | ||
# create a Custom marker for the application on AppSignal.com. | ||
# | ||
# @see https://docs.appsignal.com/api/public-endpoint/custom-markers.html | ||
# Public Endpoint API markers endpoint documentation | ||
# @see https://docs.appsignal.com/appsignal/terminology.html#markers | ||
# Terminology: Markers | ||
class CustomMarker | ||
# @param icon [String] icon to use for the marker, like an emoji. | ||
# @param message [String] name of the user that is creating the | ||
# marker. | ||
# @param created_at [Time/String] A Ruby time object or a valid ISO8601 | ||
# timestamp. | ||
# @return [Boolean] | ||
def self.report( | ||
icon: nil, | ||
message: nil, | ||
created_at: nil | ||
) | ||
new( | ||
{ | ||
:icon => icon, | ||
:message => message, | ||
:created_at => created_at.respond_to?(:iso8601) ? created_at.iso8601 : created_at | ||
}.compact | ||
).transmit | ||
end | ||
|
||
# @api private | ||
def initialize(marker_data) | ||
@marker_data = marker_data | ||
end | ||
|
||
# @api private | ||
def transmit | ||
unless Appsignal.config | ||
Appsignal.internal_logger.warn( | ||
"Did not transmit custom marker: no AppSignal config loaded" | ||
) | ||
return false | ||
end | ||
|
||
transmitter = Transmitter.new( | ||
"#{Appsignal.config[:logging_endpoint]}/markers", | ||
Appsignal.config | ||
) | ||
response = transmitter.transmit(@marker_data) | ||
|
||
if (200...300).include?(response.code.to_i) | ||
Appsignal.internal_logger.info("Transmitted custom marker") | ||
true | ||
else | ||
Appsignal.internal_logger.error( | ||
"Failed to transmit custom marker: #{response.code} status code" | ||
) | ||
false | ||
end | ||
rescue => e | ||
Appsignal.internal_logger.error( | ||
"Failed to transmit custom marker: #{e.class}: #{e.message}\n" \ | ||
"#{e.backtrace}" | ||
) | ||
false | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
describe Appsignal::CustomMarker do | ||
let(:config) { build_config } | ||
|
||
describe "#transmit" do | ||
# def stub_marker_request | ||
# stub_api_request config, "markers", marker.marker_data | ||
# end | ||
|
||
def create_marker( | ||
icon: nil, | ||
message: nil, | ||
created_at: nil | ||
) | ||
described_class.report( | ||
:icon => icon, | ||
:message => message, | ||
:created_at => created_at | ||
) | ||
end | ||
|
||
context "without Appsignal.config" do | ||
it "logs a warning" do | ||
logs = | ||
capture_logs do | ||
expect(create_marker( | ||
:icon => "🎉", | ||
:message => "Migration completed", | ||
:created_at => Time.now | ||
)).to be(false) | ||
end | ||
expect(logs) | ||
.to contains_log(:warn, "Did not transmit custom marker: no AppSignal config loaded") | ||
end | ||
end | ||
|
||
context "with Appsignal.config" do | ||
before { configure } | ||
|
||
context "when request is valid" do | ||
it "sends request with all parameters and logs success" do | ||
time = "2025-02-21T11:03:48+01:00" | ||
stub_public_endpoint_markers_request( | ||
:marker_data => { | ||
"icon" => "🎉", | ||
"message" => "Migration completed", | ||
"created_at" => time | ||
} | ||
).to_return(:status => 200) | ||
|
||
logs = | ||
capture_logs do | ||
expect(create_marker( | ||
:icon => "🎉", | ||
:message => "Migration completed", | ||
:created_at => time | ||
)).to be(true) | ||
end | ||
|
||
expect(logs).to contains_log(:info, "Transmitted custom marker") | ||
expect(logs).to_not contains_log(:error, "Failed to transmit custom marker") | ||
end | ||
|
||
it "sends request with time object as parameter and logs success" do | ||
time = Time.now.utc | ||
stub_public_endpoint_markers_request( | ||
:marker_data => { | ||
"icon" => "🎉", | ||
"message" => "Migration completed", | ||
"created_at" => time.iso8601 | ||
} | ||
).to_return(:status => 200) | ||
|
||
logs = | ||
capture_logs do | ||
expect(create_marker( | ||
:icon => "🎉", | ||
:message => "Migration completed", | ||
:created_at => time | ||
)).to be(true) | ||
end | ||
|
||
expect(logs).to contains_log(:info, "Transmitted custom marker") | ||
expect(logs).to_not contains_log(:error, "Failed to transmit custom marker") | ||
end | ||
|
||
it "sends request with some parameters and logs success" do | ||
stub_public_endpoint_markers_request( | ||
:marker_data => { | ||
"message" => "Migration completed" | ||
} | ||
).to_return(:status => 200) | ||
|
||
logs = | ||
capture_logs do | ||
expect(create_marker(:message => "Migration completed")).to be(true) | ||
end | ||
|
||
expect(logs).to contains_log(:info, "Transmitted custom marker") | ||
expect(logs).to_not contains_log(:error, "Failed to transmit custom marker") | ||
end | ||
end | ||
|
||
context "when request failed" do | ||
it "logs error" do | ||
time = Time.now.utc | ||
stub_public_endpoint_markers_request( | ||
:marker_data => { | ||
"icon" => "🎉", | ||
"message" => "Migration completed", | ||
"created_at" => time.iso8601 | ||
} | ||
).to_return(:status => 500) | ||
|
||
logs = | ||
capture_logs do | ||
expect(create_marker( | ||
:icon => "🎉", | ||
:message => "Migration completed", | ||
:created_at => time | ||
)).to be(false) | ||
end | ||
|
||
expect(logs).to_not contains_log(:info, "Transmitted custom marker") | ||
expect(logs).to contains_log(:error, "Failed to transmit custom marker: 500 status code") | ||
end | ||
end | ||
|
||
context "when request raised an error" do | ||
it "logs error" do | ||
time = Time.now.utc | ||
stub_public_endpoint_markers_request( | ||
:marker_data => { | ||
"icon" => "🎉", | ||
"message" => "Migration completed", | ||
"created_at" => time.iso8601 | ||
} | ||
).to_raise(RuntimeError.new("uh oh")) | ||
|
||
logs = | ||
capture_logs do | ||
expect(create_marker( | ||
:icon => "🎉", | ||
:message => "Migration completed", | ||
:created_at => time | ||
)).to be(false) | ||
end | ||
|
||
expect(logs).to_not contains_log(:info, "Transmitted custom marker") | ||
expect(logs) | ||
.to contains_log(:error, "Failed to transmit custom marker: RuntimeError: uh oh") | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters