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