> For the complete documentation index, see [llms.txt](https://triton.pharos.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://triton.pharos.ai/common-apex-usage-patters.md).

# Logging in Apex

The everyday Apex logging patterns — a first log, grouping logs under a transaction, and the try/catch/finally shape you'll reach for again and again.

You've got the [mental model](/core-concepts.md) down: describe a log with a builder, hand it to `Triton`, flush when you're ready. This section is where that model meets real Apex — triggers, services, batches, callouts, REST endpoints. We'll start with the shape you'll use most and build up from there.

## A first Apex log

Here's error logging in Triton 2.0, start to finish:

{% code lineNumbers="true" %}

```apex
public void updateAccounts() {
    Triton.startTransaction();
    try {
        List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 1];
        if (!accounts.isEmpty()) {
            Triton.debug(Triton.t
                .area(TritonTypes.Area.Accounts)
                .summary('Account found, applying update')
                .details('Processing ' + accounts[0].Id));
            accounts[0].Name = 'Logging rules!';
            update accounts;
        }
        Triton.flush();
    } catch (Exception e) {
        Triton.logNow(Triton.t.area(TritonTypes.Area.Accounts).exception(e));
    } finally {
        Triton.stopTransaction();
    }
}
```

{% endcode %}

Everything you need is in that snippet. Let's unpack the three ideas that make it tick.

## Group related logs with a transaction

`Triton.startTransaction()` opens a **correlation scope**: every log you create until `stopTransaction()` shares one transaction id, and Pharos groups them under a single parent log. Instead of a scattered pile of records, you get a tidy tree you can navigate top-down.

{% code lineNumbers="true" %}

```apex
Triton.startTransaction();
Triton.info(Triton.t.area(TritonTypes.Area.Accounts).summary('Job started'));
Triton.info(Triton.t.area(TritonTypes.Area.Accounts).summary('Records processed'));
Triton.flush();
Triton.stopTransaction();
```

{% endcode %}

The first log created becomes the parent; everything after nests beneath it.

{% hint style="info" %}
Need to correlate logs across a boundary — into an async job, a `@future` call, or a callout? Pass `Triton.TRANSACTION_ID` along and call `Triton.resumeTransaction(id)` on the other side. That's the whole basis of end-to-end tracing — see [Transactions & Cache](/end-to-end/transactions-and-cache.md).
{% endhint %}

If you skip `startTransaction()` entirely, Triton still logs everything — the records just won't be grouped under a shared parent. Grouping is a convenience, not a requirement.

## `.exception(e)` — the error one-liner

When you're catching an exception, don't hand-assemble the log. `.exception(e)` sets the type from the exception (falling back to `Backend`), flips the level to `ERROR`, copies the message into the summary, captures the stack trace and details, and flags the log for issue creation — all at once:

{% code lineNumbers="true" %}

```apex
try {
    chargeCard(payment);
} catch (Exception e) {
    Triton.logNow(Triton.t.area(TritonTypes.Area.OpportunityManagement).exception(e));
}
```

{% endcode %}

Set the area, pass the exception, done. Issues are created automatically for error logs; routine `debug`/`info` logs don't generate issues. Full builder details live in [The Builder & Templates](/core-concepts/builder-and-templates.md).

## Buffer the routine, publish the critical

Notice the mix above: `Triton.debug(...)` and `Triton.info(...)` **buffer** (cheap, published together on `flush()`), while the exception uses `Triton.logNow(...)` to **publish immediately**. That's the discipline for every Apex context:

* **Buffer** progress and info logs, then `Triton.flush()` at a safe checkpoint.
* **Publish critical failures now** with `Triton.logNow(...)` so you never lose them if the transaction dies.

{% hint style="warning" %}
**Mind the governor limits.** If you blow a limit mid-transaction, there's no guarantee your `finally` block (and its `flush()`/`stopTransaction()`) runs. A buffered log that never flushes is a log you'll wish you had — so publish anything you truly can't lose with `logNow`, and treat `flush()` like DML: do it at boundaries, out of loops. More in [Buffering, Flushing & Deferred Publishing](/core-concepts/buffering-flushing.md).
{% endhint %}

## Where to go next

Each page here takes one Apex context and goes deep:

* [Batch Logging](/common-apex-usage-patters/batch-logging.md) — buffering across `execute()` chunks and grouping every iteration under one transaction.
* [Performance Marks](/common-apex-usage-patters/performance-marks.md) — timing a stretch of Apex with `startMark` / `endMark`, and the span tree they build.
* [Integration Logs](/common-apex-usage-patters/integration-logs.md) — capturing request/response payloads on outbound callouts.
* [Apex REST Logging](/common-apex-usage-patters/apex-rest-logging.md) — logging inbound REST requests and threading the transaction id downstream.
* [Full Control with TritonBuilder](/common-apex-usage-patters/full-control-with-tritonbuilder.md) — the complete builder surface, custom attributes, and templates.
* [Log Levels](/common-apex-usage-patters/log-levels.md) — choosing severity and tuning verbosity per environment.
