> 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/triton-best-practices/templates-and-builder-pattern.md).

# Templates & the Builder Pattern

The one habit that keeps logging consistent at scale — set a template per unit of work, then log from Triton.t — plus when to override and the LWC equivalent.

If you adopt one habit from this entire section, make it this one: **set a template at the top of each unit of work, then build every log from `Triton.t`.** It's the difference between logs that line up perfectly across your whole org and logs that *almost* match — and "almost" is worthless when you're filtering.

The builder itself (every method you can chain) is documented in [Core Concepts → The Builder & Templates](/core-concepts/builder-and-templates.md). This page is about using it with discipline.

## The problem templates solve

Watch what happens without a template. Every log has to repeat the category and area, and the moment someone gets lazy or fat-fingers a value, your data quietly fragments:

{% code lineNumbers="true" %}

```apex
// Repetitive, and one typo away from an orphaned log
Triton.info(Triton.t.category(TritonTypes.Category.Apex).area(TritonTypes.Area.Accounts).summary('Fetched accounts'));
Triton.info(Triton.t.category(TritonTypes.Category.Apex).area(TritonTypes.Area.Accounts).summary('Recalculated rollups'));
Triton.error(Triton.t.category(TritonTypes.Category.Apex).area(TritonTypes.Area.Accounts).exception(e));
```

{% endcode %}

A template collapses the repetition to a single source of truth:

{% code lineNumbers="true" %}

```apex
// Set the shared shape once
Triton.setTemplate(Triton.makeBuilder()
    .category(TritonTypes.Category.Apex)
    .area(TritonTypes.Area.Accounts));

// ...then only say what's different about each log
Triton.info(Triton.t.summary('Fetched accounts'));
Triton.info(Triton.t.summary('Recalculated rollups'));
Triton.error(Triton.t.exception(e));
```

{% endcode %}

## How `Triton.t` actually works

`Triton.t` is shorthand for `Triton.fromTemplate()`. Each time you touch it, you get a **fresh clone** of the template with the current transaction id already stamped on. That cloning is the whole trick: the shared fields (category, area, any related object you baked in) come along, but per-log fields — summary, details, exception — never leak from one log into the next.

{% code lineNumbers="true" %}

```apex
Triton.info(Triton.t.summary('First'));    // fresh clone, summary = 'First'
Triton.info(Triton.t.summary('Second'));   // another fresh clone, summary = 'Second'
```

{% endcode %}

{% hint style="info" %}
**Set the template where the unit of work begins.** A trigger handler constructor, the top of a batch `execute`, the first line of an `@AuraEnabled` method. Because the template is static per-transaction, setting it once at the entry point means everything logged downstream inherits it — no passing anything around.
{% endhint %}

## `makeBuilder()` vs `t` — which do I use?

* **`Triton.t`** — your default. A clone of the template. Use it for essentially every log once a template is set.
* **`Triton.makeBuilder()`** — a plain builder pre-set to the `Apex` category, with *no* template fields. Use it to **build the template itself**, or for a genuine one-off that shouldn't inherit the current template.

{% code lineNumbers="true" %}

```apex
Triton.setTemplate(Triton.makeBuilder()      // makeBuilder to define the template
    .category(TritonTypes.Category.Apex)
    .area(TritonTypes.Area.OpportunityManagement));

Triton.info(Triton.t.summary('Using the template'));   // t for everything after
```

{% endcode %}

## Overriding — welcome, in moderation

A template is a starting point, not a straitjacket. Override any field on a per-log basis just by chaining it; the override wins:

{% code lineNumbers="true" %}

```apex
Triton.setTemplate(Triton.makeBuilder()
    .category(TritonTypes.Category.Apex)
    .area(TritonTypes.Area.Accounts)
    .relatedObject(accountId));

// This one log escalates to WARNING and points at a different record
Triton.warning(Triton.t
    .summary('Account flagged for manual review')
    .relatedObject(caseId));
```

{% endcode %}

{% hint style="warning" %}
**Don't override the classification fields casually.** Bumping the level or adding a related object per-log is normal. But if you find yourself overriding `category`/`type`/`area` all over a method, that's a sign the template is set at the wrong scope — split the method, or set a more specific template for the inner section.
{% endhint %}

## Do this / not that

{% hint style="success" %}
**Do**

* Set a template at the top of each unit of work.
* Build every log from `Triton.t`.
* Override only the fields that genuinely differ per log (summary, details, related objects, level).
* Use `.exception(e)` for errors instead of hand-setting type/summary/stack.
  {% endhint %}

{% hint style="danger" %}
**Don't**

* Rebuild the full classification on every call with `makeBuilder()` — that's what a template is for.
* Reach for `Triton.instance` — there is no instance in 2.0; the API is static.
* Reuse a builder across two logs — always start fresh from `Triton.t`.
  {% endhint %}

## The LWC equivalent

LWC has its own logger (a component-scoped instance you create with `new Triton().bindToComponent(...)`), but the template idea is identical: set it once in `connectedCallback`/`constructor`, then log from `fromTemplate()`.

{% code lineNumbers="true" %}

```javascript
import Triton, { TYPE, AREA, LEVEL } from 'c/triton';

export default class CaseConsole extends LightningElement {
    triton;

    connectedCallback() {
        this.triton = new Triton().bindToComponent('caseConsole');

        // Component-scoped template
        this.triton.setTemplate(
            this.triton.makeBuilder()
                .type(TYPE.FRONTEND)
                .area(AREA.COMMUNITY)
                .level(LEVEL.INFO)
                .relatedObjects([this.recordId])
        );
    }

    async handleCreateCase() {
        try {
            const result = await createCase({ caseData: this.caseForm });
            this.triton.log(
                this.triton.fromTemplate()
                    .summary('Case created')
                    .relatedObjects([result.Id])
            );
        } catch (error) {
            // Template fields are preserved even on the error path
            this.triton.logNow(
                this.triton.fromTemplate()
                    .exception(error)
                    .summary('Case creation failed')
            );
        }
    }
}
```

{% endcode %}

The full LWC logger surface — binding, enums, and lifecycle timing — is covered in [Beyond Apex → LWC](/beyond-apex/lwc.md).

## Related reading

* Every builder method, in detail: [Core Concepts → The Builder & Templates](/core-concepts/builder-and-templates.md).
* Adding your own typed builder methods for custom fields: same page, [Custom attributes](/core-concepts/builder-and-templates.md).
