When AppSignal receives data from your application, traces/transactions are grouped by their namespace and action name combination.
By default, these action names are automatically determined based on your framework (e.g., Controller#action_name, BackgroundWorker#perform, etc.).
When the automatically determined action name is not good enough, you can customize the action name.
This guide explains how to customize action names in different language integrations.
Actions names are not meant to be unique!
Do not use variables to customize the action names, as this will create a new incident per occurrence.
This makes the incident overview difficult to use and prevents proper grouping and trend analysis.Always use static strings for action names. Never interpolate variables, user input, or dynamic data into action names.
Why Customize Action Names?
- Improved clarity: Provide more descriptive names for complex operations.
- Customized grouping: Control how application data is organized in the AppSignal.
When Automatic Action Names Fall Short
Here are some examples where automatically generated action names may not be good enough:
- Catch-all routes: In applications that use catch-all routes or dynamic routing (like
/api/:entity/:action), the default action name might be something generic like ApiController#dispatch.
- Multi-purpose tasks: Background workers and scripts that handle different types of work based on parameters that would all be grouped under the same action name (e.g.,
GenericWorker#perform).
- GraphQL resolvers: These might all be grouped under a single action name (e.g.
POST /graphql) despite handling many different types of operations.
Customizing Actions Per Language
Ruby
In Ruby applications, you can use the Appsignal.set_action helper to customize the action name:
# In a Rails controller
class PaymentsController < ApplicationController
def process_payment
provider = params[:payment][:provider]
# GOOD: Change the action name from "PaymentsController#process_payment"
# to something more specific about the operation
if provider == "creditcard"
Appsignal.set_action("PaymentsController#process_payment (creditcard)")
end
# BAD: Don't use a dynamic value, it creates too many action names
# Appsignal.set_action("PaymentsController#process_#{provider}")
# Rest of your code
end
end
For background jobs, tasks and scripts, you can set the action name in the same way with the Appsignal.set_action helper.
It’s also possible to configure the action name when creating a transaction using the Appsignal.monitor helper. It accepts the action name as a keyword argument.
# When creating a new transaction
Appsignal.monitor(action: "script/custom_script") do
# Your code to instrument
end
Elixir
In Elixir applications, you can use the Appsignal.Span.set_name/2 function to customize the action name on the root span:
# In a Phoenix controller
defmodule MyAppWeb.PaymentController do
use MyAppWeb, :controller
def process(conn, params) do
# GOOD: Set a custom static action name based on payment type
payment_type = Map.get(params, "type", "unknown")
if payment_type == "creditcard" do
Appsignal.Span.set_name(
Appsignal.Tracer.root_span(),
"PaymentController#process_payment (creditcard)"
)
end
# BAD: Don't use a dynamic value, it creates too many action names
# Appsignal.Span.set_name(
# Appsignal.Tracer.root_span(),
# "PaymentController#process_#{payment_type}"
# )
# Rest of your code
render(conn, "success.html")
end
end
For background jobs, tasks and scripts, you can set the action name in the same way with the Appsignal.Span.set_name/2 function.
Node.js
In Node.js applications, you can use the setRootName helper from the AppSignal package to customize action names:
import { setRootName } from "@appsignal/nodejs";
// In an Express.js route handler
app.post("/payments", (req, res) => {
const paymentType = req.body.type || "unknown";
// GOOD: Set a custom action name based on payment type
if (paymentType === "creditcard") {
setRootName("PaymentsController#process_payment (creditcard)");
}
// BAD: Don't use a dynamic value, it creates too many action names
// setRootName(`PaymentsController#process_${paymentType}`);
// Rest of your code
res.send("Payment processed");
});
For background jobs, tasks and scripts, you can set the action name in the same way with the setRootName helper.
Python
In Python applications, you can use the set_root_name helper from the AppSignal package to customize action names:
from appsignal import set_root_name
# In a Django view
def process_payment(request):
payment_type = request.POST.get('type', 'unknown')
# GOOD: Set a custom action name based on payment type
if payment_type == "creditcard":
set_root_name("PaymentsView#process_payment (creditcard)")
# BAD: Don't use a dynamic value, it creates too many action names
# set_root_name(f"PaymentsView#process_{payment_type}")
# Rest of your code
return HttpResponse("Payment processed")
For background jobs, tasks and scripts, you can set the action name in the same way with the set_root_name helper.
Front-end JavaScript
In Front-end JavaScript applications, you can use the setAction helper from the AppSignal package to customize action names:
const span = appsignal.createSpan((span) => {
const formType = getFormType();
// GOOD: Set a custom action name based on form type
if (formType === "checkout") {
span.setAction("CheckoutPage#submit_form (checkout)");
}
// BAD: Don't use a dynamic value, it creates too many action names
// span.setAction(`CheckoutPage#submit_${formType}_form`);
// Rest of your code
});
Alternatively, you can update an existing span’s action name:
// Get the current active span
const span = appsignal.getActiveSpan();
function handlePaymentMethodSelection(method) {
// GOOD: Set a custom action name based on payment method
if (method === "creditcard") {
span.setAction("PaymentForm#select_method (creditcard)");
}
// BAD: Don't do this - using a variable creates too many action names
// span.setAction(`PaymentForm#select_${method}`);
// Rest of your code
}
In Go applications, AppSignal works with OpenTelemetry, which uses spans to track metadata, such as the action name. On any span in the trace, set an appsignal.action_name attribute with a String value to customize the action name:
package main
import (
"context"
"net/http"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
func processPaymentHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
span := trace.SpanFromContext(ctx)
paymentType := r.FormValue("type")
// GOOD: Set a custom action name based on payment type
if paymentType == "creditcard" {
span.SetAttributes(attribute.String("appsignal.action_name", "PaymentHandler#process_payment (creditcard)"))
}
// BAD: Don't use a dynamic value, it creates too many action names
// span.SetAttributes(attribute.String("appsignal.action_name", fmt.Sprintf("PaymentHandler#process_%s", paymentType)))
// Rest of your code
w.WriteHeader(http.StatusOK)
w.Write([]byte("Payment processed"))
}
For background jobs, tasks and scripts, you can set the action name in the same way by setting the appsignal.action_name attribute on the active span.
Java
In Java applications, AppSignal works with OpenTelemetry, which uses spans to track metadata, such as the action name. On any span in the trace, set an appsignal.action_name attribute with a String value to customize the action name:
import io.opentelemetry.api.trace.Span;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PaymentController {
@PostMapping("/payments")
public String processPayment(@RequestParam String type) {
Span span = Span.current();
// GOOD: Set a custom action name based on payment type
if ("creditcard".equals(type)) {
span.setAttribute("appsignal.action_name", "PaymentController#processPayment (creditcard)");
}
// BAD: Don't use a dynamic value, it creates too many action names
// span.setAttribute("appsignal.action_name", "PaymentController#process_" + type);
// Rest of your code
return "Payment processed";
}
}
For background jobs, tasks and scripts, you can set the action name in the same way by setting the appsignal.action_name attribute on the active span.
PHP
In PHP applications, you can use the Appsignal::setAction() helper method from the AppSignal package to customize action names:
<?php
use Appsignal\Appsignal;
class PaymentController
{
public function processPayment()
{
$paymentType = $_POST['type'] ?? 'unknown';
// GOOD: Set a custom action name based on payment type
if ($paymentType === 'creditcard') {
Appsignal::setAction('PaymentController::processPayment (creditcard)');
}
// BAD: Don't use a dynamic value, it creates too many action names
// Appsignal::setAction("PaymentController::process_{$paymentType}");
// Rest of your code
return 'Payment processed';
}
}
For background jobs, tasks and scripts, you can set the action name in the same way by setting the appsignal.action_name attribute on the active span.
Best Practices
When customizing action names, follow these guidelines:
- Be consistent: Use a consistent naming pattern across your application.
- Be specific: Include relevant information that helps identify the operation.
- Avoid high cardinality: Don’t include unique IDs or values that would create unique action names per execution.
- Use static strings: Never interpolate variables or dynamic data into action names.
- Follow the codebase naming: Use naming patterns like
Controller#action that match your application structure so the location in the code can be found.
- Use tags or metadata instead: For tracking variable information like payment providers or user types, use tags and metadata rather than incorporating them into action names.
How to Handle Different Operations with the Same Action Name
Instead of creating dynamic action names, use a combination of:
- Static action names: Use descriptive but static action names.
- Tags: Add tags with the variable information (e.g.,
provider: stripe, operation_type: refund).
- Custom attributes: Add additional metadata to the trace/transaction.
Example:
# Instead of this:
# Appsignal.set_action("PaymentController#process_#{provider}")
# Do this:
Appsignal.set_action("PaymentController#process_payment")
Appsignal.add_tags(
provider: provider,
operation: operation
)
This approach allows you to:
- Group related operations under a single action name.
- Filter and search based on tags.
- Maintain a clean incident overview.
Deploy
After you’ve implemented custom action names, deploy your application. New traces/transactions will use your custom action names in the AppSignal dashboards.
Historical data will still use the original action names. Only newly reported actions will use the custom names you’ve defined.
Are your custom action names not appearing correctly? Don’t hesitate to contact our support team for help!
Further Reading