Instrumenting an app · 5 min read · Updated 2026-09-22

Which call to make

The chapter you keep open while you write. A failure, an intent, a duration, or context — pick one call, or pick none.

Part 2 of 8 · Next: Errors in application code

Engineers say “should I log this?” and mean five different jobs. They mean “record that it failed,” “record that someone did it,” “record how long it took,” “leave a trail in case it fails later,” or “I am debugging locally and I want stdout.” Talaria has a call for the first four. The fifth belongs in your process logs.

The table below is a checkout — storefront plus API, SKU TR-900. The same rule applies if your core action is signup, upgrade, or publish.

Which call?

Checkout

production

What just happened in the code?

A failure?

captureException

Becomes an issue

Someone did a thing?

analytics.track

Intent, not breakage

Work took time?

startSpan

Shows up on the waterfall

Context before a failure?

addBreadcrumb

Cheap trail, no meter

If none of the four fit, leave it in stdout. Do not invent a fifth Talaria call.

Four questions. If none match, the line stays in your process logs.

The four calls

  • captureException — a throwable you want grouped into an issue. Default level error. This is the one that pages people.
  • analytics.track (or page / identify) — a named thing a person did, including successes. Most analytics events are not failures.
  • startTransaction / startSpan — timed work. The root is what Talaria meters; children ride along. Auto instrumentation already covers the HTTP request and many DB / fetch calls.
  • addBreadcrumb — a cheap trail (nav, click, “opened checkout”) attached to the next event. Breadcrumbs are not a meter. They are also not a product analytics stream.

captureMessage and logger helpers such as Talaria::info() exist. They create events, they share the event meter with crashes, and they are the wrong default. Use them when something is operationally important and is not an exception — a webhook signature that failed closed, a job you dead-lettered on purpose. Do not use them as printf.

Call sites on a checkout

These are the lines that actually appear in a checkout. Read the “Use” column as the primary call. You can attach more than one signal to the same moment — a failed charge is an exception and a checkout_failed track — but you should never substitute one for another.

In the codeUseDo not
Stripe throws on chargecaptureExceptiontrack('Error') or a span-only catch
Charge succeedsanalytics.track('order_completed') on the serverA success captureMessage
Customer clicks Add to cartanalytics.track('add_to_cart')captureMessage('added to cart')
Checkout form mountsaddBreadcrumb and track('checkout_started')An info event for every mount
User signs inidentify(userId) + setUserPutting the email in a property named email
Landing page loadspage() + the pageload transactionA custom span named homepage
PDO runs INSERT ordersAuto DB span (or a short manual span around a batch)track of the SQL
N queries in a loopFix the query; the N+1 view will show itA span per row with the primary key in the name
Outbound fetch to the APIAuto HTTP span + W3C traceparentWrapping Talaria’s own ingest client
Payment HTTP to StripestartSpan if the client is not auto-wrappedCard numbers in span attributes
Feature flag checkedTag or breadcrumb if it will explain a failureAn event per flag per request
404 on a missing SKUUsually nothing — or a breadcrumbAn error issue for every bot
Expected validation (“size required”)track with a reason, or a breadcrumbcaptureException on a user mistake
Debug dump of the cart objectstdout / your log productTalaria extra, or a 16 KB analytics property map
Job retried, then dead-letteredcaptureMessage at error, onceAn info event on every retry

Fifteen moments on a checkout. The “Do not” column is how a dashboard fills with noise.

Anti-patterns

Tracking exceptions

analytics.track('checkout_error', { message: String(e) }) does not create an issue. It does not fingerprint. It does not get a stack. It will not reopen when the same crash comes back. The catch block calls captureException. If you also want funnel drop-off for “people who failed to pay,” fire checkout_failed as well — a named intent that the charge did not complete, not a dump of the throwable.

A message on every request

Talaria::info('Checkout opened') at the top of the controller is how you discover the event meter. Ten thousand successful checkouts become ten thousand events, grouped badly, sitting next to real crashes. The request is already a transaction once tracing is on. The “opened” moment is a breadcrumb or checkout_started.

Spans for business facts

A span named user.upgraded_plan with no meaningful duration is an analytics event wearing a stopwatch. Spans exist so you can ask “where did the time go?” If the answer is “there was no time, only a fact,” use track.

Stacks and carts in properties

Analytics properties are a flat map: 64 keys, 1024-character strings, 16 KB for the whole object or the map is dropped. Extra on an error event is for cart_id, not the serialized cart. Card numbers, emails, and tokens are stripped or should never have been passed — see privacy and consent.

IDs in the name

POST /checkout/cart_01H… as a transaction name, or product_TR900_viewed as an event name, explodes the group-by. Name the route and the intent. Put the id in an attribute or a property.

The rule you can memorize

  • It threw and you did not expect it — captureException.
  • A person finished (or abandoned) a step you care about — track.
  • A unit of work has a start and an end you might need to explain — span.
  • It will only matter if something fails later — breadcrumb.
  • You are staring at a local bug — stdout.

Related guides

  • Instrumenting an app · 5 min

    Instrumenting an app

    Four signals, one checkout, and a rule for every line of application code: capture, span, track, breadcrumb — or leave it alone.

  • Instrumenting an app · 5 min

    Errors in application code

    Talaria events are exceptions and the rare message that should become an issue. Everything else is a breadcrumb, a span, or stdout.

  • Instrumenting an app · 4 min

    Tracing in application code

    A transaction is the request. A span is a timed child. You name the route, you opt in, and you let auto instrumentation do the dull work.

  • Instrumenting an app · 4 min

    Analytics in application code

    Name the intent, fire it once, identify the person, and put the charge on the server — the browser never saw the card succeed.

Instrument the first three events

Install an official SDK, opt into tracing and analytics, and name the start, the middle, and the finish of your core action.