Templates & the Builder Pattern

Leveraging templates and the builder pattern for consistent, maintainable Triton logging across Apex and LWC.

Why Templates Matter

  • Always prefer TritonBuilder + template cloning over convenience helpers.

  • Why: Templates standardize category, type, area, base details, and reduce copy/paste drift.

Apex – Set & Reuse a Template

// Early in the transaction (e.g., controller entry, batch start)
Triton.instance.setTemplate(
    Triton.makeBuilder()
        .category(TritonTypes.Category.Apex)
        .level(TritonTypes.Level.DEBUG)
        .type(TritonTypes.Type.Backend)
        .area(TritonTypes.Area.OpportunityManagement) // agreed taxonomy
);

// Later: clone and specialize
Triton.instance.log(
    Triton.instance.fromTemplate()
        .level(TritonTypes.Level.INFO)
        .summary('Opportunity sync started')
        .details('seed=nightly, source=ERP')
);

LWC – Component-Scoped Template

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

const triton = new Triton().bindToComponent('c-opp-sync'); // scopes component details

triton.setTemplate(
  triton.makeBuilder()
    .type(TYPE.FRONTEND)
    .area(AREA.OPPORTUNITY_MANAGEMENT)
);

// Usage later:
triton.log(
  triton.fromTemplate()
    .summary('Button clicked')
    .details('action=sync-all')
);

Real-World Example: Customer Service Portal

// Customer service LWC component with comprehensive template
export default class CustomerServicePortal extends LightningElement {
    triton;
    
    connectedCallback() {
        this.triton = new Triton().bindToComponent('c-customer-service-portal');
        
        // Set comprehensive template for all customer service operations
        this.triton.setTemplate(
            this.triton.makeBuilder()
                .type(TYPE.FRONTEND)
                .area(AREA.COMMUNITY)
                .level(LEVEL.INFO)
                .relatedObjects([this.recordId])
        );
    }
    
    async handleCaseCreation() {
        try {
            const result = await createCase({ caseData: this.caseForm });
            
            // Uses template, adds specific details
            this.triton.log(
                this.triton.fromTemplate()
                    .summary('Case created successfully')
                    .details(JSON.stringify({ 
                        caseId: result.Id, 
                        priority: this.caseForm.priority,
                        category: this.caseForm.category 
                    }))
                    .relatedObjects([result.Id])
            );
            
        } catch (error) {
            // Template properties preserved in error logs
            this.triton.logNow(
                this.triton.fromTemplate()
                    .exception(error)
                    .summary('Case creation failed')
            );
        }
    }
}

Template Benefits

Consistency

  • All logs in a method/component share the same base properties

  • Reduces the risk of inconsistent categorization

  • Ensures proper context across all log entries

Maintainability

  • Changes to common properties only need to be made in one place

  • Reduces code duplication and drift

  • Makes updates and refactoring easier

Reduced Errors

  • No risk of forgetting to set important properties

  • Template ensures all required fields are populated

  • Consistent structure across all logging calls

Performance

  • Template cloning is efficient

  • Reduces object creation overhead

  • Maintains singleton pattern benefits

Template Override Pattern

Show how to override template properties when needed:

// Template sets common properties
Triton.instance.setTemplate(
    Triton.makeBuilder()
        .category(TritonTypes.Category.Apex)
        .type(TritonTypes.Type.Backend)
        .area(TritonTypes.Area.Accounts)
        .relatedObject(accountId)
);

// Override specific properties as needed
Triton.instance.log(
    Triton.instance.fromTemplate()
        .level(TritonTypes.Level.INFO)        // Override level
        .summary('Account processed')         // Add specific summary
        .details('status=completed')          // Add specific details
        .duration(processingTime)             // Add duration
        .relatedObjects(new Set<Id>{contactId}) // Override related objects
);

Best Practices

Do's

  • βœ… Clone from fromTemplate() to get a pre-filled builder and add specifics

  • βœ… Set templates early in methods/components

  • βœ… Use templates for all logging calls within a scope

  • βœ… Override only what's specific to each log entry

Don'ts

  • ❌ Call addEvent/addDebug directly in new code

  • ❌ Use makeBuilder() for every log entry

  • ❌ Forget to set templates in new methods

  • ❌ Override template properties unnecessarily

Template Scoping

Method-Level Templates

public void processAccount(Id accountId) {
    // Set template for this method
    Triton.instance.setTemplate(
        Triton.makeBuilder()
            .category(TritonTypes.Category.Apex)
            .type(TritonTypes.Type.Backend)
            .area(TritonTypes.Area.Accounts)
            .relatedObject(accountId)
    );
    
    // All logs in this method use the template
    Triton.instance.log(Triton.instance.fromTemplate().summary('Processing started'));
    // ... processing logic ...
    Triton.instance.log(Triton.instance.fromTemplate().summary('Processing completed'));
}

Component-Level Templates

export default class AccountManager extends LightningElement {
    triton;
    
    connectedCallback() {
        this.triton = new Triton().bindToComponent('c-account-manager');
        
        // Set template for entire component
        this.triton.setTemplate(
            this.triton.makeBuilder()
                .type(TYPE.FRONTEND)
                .area(AREA.ACCOUNTS)
                .relatedObjects([this.recordId])
        );
    }
    
    // All methods in this component can use the template
    async handleSave() {
        this.triton.log(this.triton.fromTemplate().summary('Save initiated'));
        // ... save logic ...
    }
    
    async handleDelete() {
        this.triton.log(this.triton.fromTemplate().summary('Delete initiated'));
        // ... delete logic ...
    }
}

Advanced Template Patterns

Conditional Template Properties

// Set base template
Triton.instance.setTemplate(
    Triton.makeBuilder()
        .category(TritonTypes.Category.Apex)
        .type(TritonTypes.Type.Backend)
        .area(TritonTypes.Area.Accounts)
);

// Add conditional properties
if (isHighPriority) {
    Triton.instance.log(
        Triton.instance.fromTemplate()
            .level(TritonTypes.Level.WARNING)
            .summary('High priority account processed')
    );
} else {
    Triton.instance.log(
        Triton.instance.fromTemplate()
            .level(TritonTypes.Level.INFO)
            .summary('Standard account processed')
    );
}

Template Inheritance

// Base template for all account operations
Triton.instance.setTemplate(
    Triton.makeBuilder()
        .category(TritonTypes.Category.Apex)
        .type(TritonTypes.Type.Backend)
        .area(TritonTypes.Area.Accounts)
);

// Specialize for specific operations
public void processHighValueAccount(Id accountId) {
    // Override template for this specific operation
    Triton.instance.setTemplate(
        Triton.instance.fromTemplate()
            .level(TritonTypes.Level.WARNING)
            .relatedObject(accountId)
    );
    
    // All logs in this method inherit the specialized template
    Triton.instance.log(Triton.instance.fromTemplate().summary('High value processing started'));
}

By mastering the template pattern, you'll create logging code that is consistent, maintainable, and efficient while providing rich context for all your log entries.

Last updated