> For the complete documentation index, see [llms.txt](https://triton.pharos.ai/pharos-triton/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/pharos-triton/triton-best-practices/implementation-patterns.md).

# Implementation Patterns

## Where to Insert Logs

**General pattern:**

* **Entry**: Start/resume transaction, log intent (INFO).
* **Milestones**: Key steps with metrics (INFO/DEBUG + duration, counts).
* **Decision points**: Branches affecting outcome (DEBUG).
* **External calls**: Before/after callouts (INFO, duration; attach sanitized request/response).
* **Errors/Recovery**: Catch, `exception(...)`, add full context, **immediately flush**.
* **Exit**: Summarize result and timing, **flush**.

## Triggers (Handler)

```apex
public with sharing class AccountTriggerHandler {
    public static void beforeUpdate(List<Account> newList, Map<Id, Account> oldMap) {
        Triton.instance.startTransaction();
        Triton.instance.setTemplate(
            Triton.makeBuilder()
                .category(TritonTypes.Category.Apex)
                .type(TritonTypes.Type.AccountTrigger)
                .area(TritonTypes.Area.Accounts)
                .createdTimestamp()
        );

        Set<Id> scopeIds = new Map<Id, Account>(newList).keySet();

        Triton.instance.log(
            Triton.instance.fromTemplate()
                .level(TritonTypes.Level.INFO)
                .summary('Account before update')
                .details('count=' + scopeIds.size() + ', op=' + Trigger.operationType)
                .relatedObjects(scopeIds)
        );
    }
}
```

## Queueable

```apex
public class PriceSyncQueueable implements Queueable, Database.AllowsCallouts {
    private final Set<Id> productIds;
    public PriceSyncQueueable(Set<Id> productIds) { this.productIds = productIds; }

    public void execute(QueueableContext qc) {
        Triton.instance.startTransaction();
        Triton.instance.setTemplate(
            Triton.makeBuilder()
                .category(TritonTypes.Category.Integration)
                .type(TritonTypes.Type.Backend)
                .area(TritonTypes.Area.ProductCatalog)
                .relatedObjects(productIds)
        );

        Long started = System.now().getTime();
        try {
            // ... callout(s) and DML
            Triton.instance.log(
                Triton.instance.fromTemplate()
                    .level(TritonTypes.Level.INFO)
                    .summary('Price sync complete')
                    .details('synced=' + productIds.size())
                    .duration(System.now().getTime() - started)
            );
        } catch (Exception e) {
            Triton.instance.log(
                Triton.instance.fromTemplate()
                    .exception(e)
            );
            throw e;
        } finally {
            Triton.instance.stopTransaction();
        }
    }
}
```

## Batchable

```apex
global class LeadQualifyBatch implements Database.Batchable<SObject>, Database.Stateful {
    global Database.QueryLocator start(Database.BatchableContext bc) {
        Triton.instance.startTransaction();
        Triton.instance.setTemplate(
            Triton.makeBuilder()
                .category(TritonTypes.Category.Apex)
                .type(TritonTypes.Type.Backend)
                .area(TritonTypes.Area.LeadConversion)
        );
        Triton.instance.log(Triton.instance.fromTemplate()
            .level(TritonTypes.Level.INFO)
            .summary('Lead qualify start')
            .details('scope=all open leads'));
        return Database.getQueryLocator([SELECT Id, Status FROM Lead WHERE IsConverted = FALSE]);
    }

    global void execute(Database.BatchableContext bc, List<Lead> scope) {
        Long t0 = System.now().getTime();
        try {
            // ... work
            Triton.instance.log(Triton.instance.fromTemplate()
                .level(TritonTypes.Level.INFO)
                .summary('Executed batch chunk')
                .details('size=' + scope.size())
                .duration(System.now().getTime() - t0)
                .relatedObjects(new Map<Id, Lead>(scope).keySet()));
        } catch (Exception e) {
            Triton.instance.log(
                Triton.instance.fromTemplate()
                    .exception(e)
                    .summary('Batch chunk failed')
                    .relatedObjects(new Map<Id, Lead>(scope).keySet())
            );
            // Continue or rethrow per design
        }
    }

    global void finish(Database.BatchableContext bc) {
        Triton.instance.log(Triton.instance.fromTemplate()
            .level(TritonTypes.Level.INFO)
            .summary('Lead qualify finished'));
        Triton.instance.stopTransaction();
    }
}
```

## LWC – With `@wire`

```js
import Triton, { LEVEL, TYPE, AREA } from 'c/triton';
import getCases from '@salesforce/apex/CaseController.getCases';
import { wire } from 'lwc';

const triton = new Triton().bindToComponent('c-case-list');
triton.setTemplate(triton.makeBuilder().type(TYPE.FRONTEND).area(AREA.COMMUNITY));

@wire(getCases, { contactId: '$recordId', txId: '$triton.transactionId' })
wiredCases({ data, error }) {
  if (data) {
    triton.log(
      triton.fromTemplate()
        .summary('Wire loaded')
        .details(`rows=${data.length}`)
        .relatedObjects([this.recordId])
    );
  } else if (error) {
    triton.logNow(
      triton.fromTemplate()
        .exception(error)
        .summary('Wire error')
        .relatedObjects([this.recordId])
    );
  }
}
```

## LWC – Imperative

```js
const triton = new Triton().bindToComponent('c-order-console');
const txId = triton.startTransaction();

// Set up template for order operations
triton.setTemplate(
    triton.makeBuilder()
        .type(TYPE.FRONTEND)
        .area(AREA.REST_API)
        .relatedObjects([this.recordId])
);

try {
  const data = await loadOrders({ accountId: this.recordId, txId });

  triton.log(
    triton.fromTemplate()
      .summary('Orders loaded')
      .details(`count=${data.length}`)
  );
} catch (e) {
  triton.logNow(
    triton.fromTemplate()
      .exception(e)
      .summary('Load orders failed')
  );
} finally {
  triton.stopTransaction();
}
```

## Flow

Use the Triton invocable action (`Add Log`) to log milestones and errors from flows. This declarative approach integrates seamlessly with Flow Builder and provides comprehensive logging capabilities.

**Key Flow Logging Strategies:**

* **Strategic Placement**: Add log actions at critical points:
  * **Flow Start**: Capture initiation and input parameters
  * **User Interactions**: Record form submissions and decisions
  * **Data Operations**: Log before/after DML operations and callouts
  * **Decision Points**: Document which logic branches are followed
  * **Error Paths**: Add detailed logging in fault connectors
* **Essential Parameters**:
  * **Interview GUID**: Always use `$Flow.InterviewGuid` for proper log grouping and error correlation
  * **Area**: Set to appropriate business domain (e.g., `Accounts`, `Opportunities`)
  * **Category**: Typically `Flow` for most logging scenarios
  * **Type**: Specify flow type (e.g., `Screen Flow`, `Autolaunched Flow`)
  * **Summary**: Brief description of the log entry
  * **Details**: Include record IDs, decision variables, and contextual data
* **Best Practices**:
  * **Use Constants**: Create constants for common parameters (Area, Category, Type) to maintain consistency
  * **Text Templates**: Use text templates for log details to combine static text with dynamic flow data
  * **Naming Conventions**: Prefix resources with "Log\_" for easy filtering
  * **Additional Fields**: Use JSON-formatted additional fields for extra contextual data when needed
* **Automatic Error Capture**: Triton automatically captures unhandled Flow exceptions and correlates them with your custom logs using the Interview GUID, providing enhanced debugging information including stack traces, user context, and related object details.
* **Log Grouping**: All logs from the same Flow instance are automatically grouped under a parent log using the Interview GUID, creating a complete execution trace for troubleshooting.

## Integration Patterns

### REST API Controllers

```apex
@RestResource(urlMapping='/api/accounts/*')
global with sharing class AccountRestAPI {
    
    @HttpGet
    global static void getAccount() {
        Triton.instance.startTransaction();
        Triton.instance.setTemplate(
            Triton.makeBuilder()
                .category(TritonTypes.Category.Integration)
                .type(TritonTypes.Type.Backend)
                .area(TritonTypes.Area.RestAPI)
        );
        
        try {
            RestRequest req = RestContext.request;
            RestResponse res = RestContext.response;
            
            // Process request
            String accountId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
            
            Triton.instance.log(
                Triton.instance.fromTemplate()
                    .summary('REST API call received')
                    .details('accountId=' + accountId + ', method=GET')
            );
            
            // ... process request ...
            
            Triton.instance.log(
                Triton.instance.fromTemplate()
                    .summary('REST API call completed')
                    .details('statusCode=200')
            );
            
        } catch (Exception e) {
            Triton.instance.log(
                Triton.instance.fromTemplate()
                    .exception(e)
                    .summary('REST API call failed')
            );
            throw e;
        } finally {
            Triton.instance.stopTransaction();
        }
    }
}
```

### Scheduled Jobs

```apex
global class DataSyncScheduler implements Schedulable {
    global void execute(SchedulableContext sc) {
        Triton.instance.startTransaction();
        Triton.instance.setTemplate(
            Triton.makeBuilder()
                .category(TritonTypes.Category.Apex)
                .type(TritonTypes.Type.Backend)
                .area(TritonTypes.Area.Integration)
        );
        
        try {
            Triton.instance.log(
                Triton.instance.fromTemplate()
                    .summary('Data sync scheduled job started')
            );
            
            // ... sync logic ...
            
            Triton.instance.log(
                Triton.instance.fromTemplate()
                    .summary('Data sync scheduled job completed')
            );
            
        } catch (Exception e) {
            Triton.instance.log(
                Triton.instance.fromTemplate()
                    .exception(e)
                    .summary('Data sync scheduled job failed')
            );
            throw e;
        } finally {
            Triton.instance.stopTransaction();
        }
    }
}
```

## Best Practices Summary

1. **Start transactions early**: Begin at the entry point of each context
2. **Set templates consistently**: Use templates for all logging within a scope
3. **Log at boundaries**: Entry, milestones, decision points, errors, exit
4. **Handle errors properly**: Always log exceptions and flush immediately
5. **Use appropriate levels**: INFO for milestones, DEBUG for details, ERROR for failures
6. **Include context**: Related objects, duration, operation details
7. **Stop transactions**: Always end transactions at natural boundaries

By following these implementation patterns, you'll create comprehensive logging coverage across all your Salesforce automation and code.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://triton.pharos.ai/pharos-triton/triton-best-practices/implementation-patterns.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
