Grouping with Action Names

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:

  1. 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.
  2. 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).
  3. 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:

Ruby
# 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.

Ruby
# 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:

Elixir
# 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:

JavaScript
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:

Python
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:

JavaScript
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:

JavaScript
// 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 }

Best Practices

When customizing action names, follow these guidelines:

  1. Be consistent: Use a consistent naming pattern across your application.
  2. Be specific: Include relevant information that helps identify the operation.
  3. Avoid high cardinality: Don't include unique IDs or values that would create unique action names per execution.
  4. Use static strings: Never interpolate variables or dynamic data into action names.
  5. Follow the codebase naming: Use naming patterns like Controller#action that match your application structure so the location in the code can be found.
  6. 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:

  1. Static action names: Use descriptive but static action names.
  2. Tags: Add tags with the variable information (e.g., provider: stripe, operation_type: refund).
  3. Custom attributes: Add additional metadata to the trace/transaction.

Example:

Ruby
# 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