Pharos Triton
  • 🔱About Pharos Triton
  • 🏁Installing Pharos Triton
  • Apex Logging Basics
  • Common Apex Usage Patters
    • Batch Logging
    • Integration Logs
    • Apex Rest Logging
    • Full Control with TritonBuilder
  • Beyond Apex
    • LWC
    • 🔄LWC Transaction Management
    • ⚡LWC and Apex
    • 💾Platform Cache for Transactions
    • Flows
    • 〰️LWC, Apex and Flows
  • 📖Methods Reference
    • 📔Apex
      • Triton
      • TritonBuilder
      • TritonTypes
      • TritonLwc
        • ComponentLog
        • Component
        • Error
        • RuntimeInfo
      • TritonFlow
        • FlowLog
      • TritonHelper
        • PostProcessingControlsBuilder
      • LogBuilder
    • LWC
      • Triton
      • TritonBuilder
      • TritonUtils
  • Help and Support
Powered by GitBook
On this page
  • Implementation Approaches
  • Approach 1: Direct Parameter Passing
  • Approach 2: Using Platform Cache
  • Transaction ID Flow
  • Apex Controller
  • The Flow
  • Flow Configuration
  • Choosing Between Approaches
  • Direct Parameter Passing
  • Platform Cache
  • The Result
  • Handling Flow Failures
  • Example of an Unhandled Flow Error
  • Resulting Log Structure
  • Benefits of Automatic Error Correlation
  1. Beyond Apex

LWC, Apex and Flows

In this article we will take a look at a more complex logging scenario involving an LWC component, a corresponding Apex controller, and an autolaunched Flow.

In this article, we'll explore a more complex logging scenario involving three Salesforce technologies: an LWC component, its Apex controller, and an autolaunched Flow. Our goal is to produce a unified log trace across all three layers while demonstrating both direct parameter passing and cache-based approaches for transaction management.

The scenario follows this sequence:

  1. User clicks a button in LWC

  2. Apex controller performs a DML operation

  3. Autolaunched Flow triggers from the DML

  4. Flow performs some operations and logs its progress

Implementation Approaches

Let's examine two different approaches to managing transaction IDs across these contexts, with separate implementations for each.

Approach 1: Direct Parameter Passing

This approach explicitly passes the transaction ID through method parameters.

LWC Controller

// logDemoDirectParams.js
import apexActionThatTriggersFlow from '@salesforce/apex/LogDemoDirectController.apexActionThatTriggersFlow';
import Triton, { AREA, TYPE } from 'c/triton';

export default class LogDemoDirectParams extends LightningElement {
    triton;
    
    connectedCallback() {
        // Bind triton to this component
        this.triton = new Triton().bindToComponent('LogDemoDirectParams');
    }
    
    async executeScenario() {
        // Create initial LWC log and get transaction ID
        await this.triton.logNow(
            this.triton.debug(TYPE.FRONTEND, AREA.ACCOUNTS)
                .summary('Flow Demo - Start')
                .details('Starting execution with direct parameter passing')
        );

        try {
            // Pass transaction ID to Apex
            await apexActionThatTriggersFlow({
                transactionId: this.triton.transactionId
            });
        } catch (error) {
            await this.triton.logNow(this.triton.exception(error));
        }
    }
}

Apex Controller

// LogDemoDirectController.cls
public with sharing class LogDemoDirectController {
    @AuraEnabled
    public static void apexActionThatTriggersFlow(String transactionId) {
        // Resume the transaction started in LWC
        Triton.instance.resumeTransaction(transactionId);
        
        Triton.instance.debug(
            TritonTypes.Type.Backend,
            TritonTypes.Area.Community,
            'Flow Demo - Apex',
            'Performing DML to trigger flow'
        );
        
        try {
            // Update account to trigger flow
            List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 1];
            if (!accounts.isEmpty()) {
                accounts[0].Name = 'Flow Demo';
                update accounts;
            }
        } catch (Exception e) {
            Triton.instance.error(TritonTypes.Area.Community, e);
        }
    }
}

Approach 2: Using Platform Cache

This approach uses platform cache to automatically share the transaction ID.

LWC Controller

// logDemoCache.js
import apexActionThatTriggersFlow from '@salesforce/apex/LogDemoCacheController.apexActionThatTriggersFlow';
import Triton, { AREA, TYPE } from 'c/triton';

export default class LogDemoCache extends LightningElement {
    triton;
    
    connectedCallback() {
        // Bind triton to this component
        this.triton = new Triton().bindToComponent('LogDemoCache');
    }
    
    async executeScenario() {
        // Initial log will be cached automatically
        await this.triton.logNow(
            this.triton.debug(TYPE.FRONTEND, AREA.ACCOUNTS)
                .summary('Flow Demo - Start')
                .details('Starting execution with cache enabled')
        );

        try {
            // No need to pass transaction ID
            await apexActionThatTriggersFlow();
        } catch (error) {
            await this.triton.logNow(this.triton.exception(error));
        }
    }
}

Apex Controller

// LogDemoCacheController.cls
public with sharing class LogDemoCacheController {
    @AuraEnabled
    public static void apexActionThatTriggersFlow() {
        // Enable cache to automatically retrieve transaction ID
        Triton.instance.withCache();
        
        Triton.instance.debug(
            TritonTypes.Type.Backend,
            TritonTypes.Area.Community,
            'Flow Demo - Apex',
            'Performing DML to trigger flow'
        );
        
        try {
            // Update account to trigger flow
            List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 1];
            if (!accounts.isEmpty()) {
                accounts[0].Name = 'Flow Demo';
                update accounts;
            }
        } catch (Exception e) {
            Triton.instance.error(TritonTypes.Area.Community, e);
        }
    }
}

Transaction ID Flow

Understanding how the transaction ID connects logs across contexts is crucial:

  1. LWC Initialization

    • Direct Params: Transaction ID is generated when first log is created

    • Cache: Transaction ID is generated and automatically cached

  2. Apex Execution

    • Direct Params: Transaction ID is passed via method parameter and resumed

    • Cache: Transaction ID is automatically retrieved from cache

  3. Flow Execution

    • The Flow's Interview GUID is automatically associated with the transaction

    • All Flow logs are grouped under the same transaction

Here's what the resulting log structure looks like in Pharos:

Transaction: 7fb8c320-e8f9-4f1b-9742-1a5768b9e0d2
├── LWC Debug Log (Start)
├── Apex Debug Log (DML Operation)
└── Flow Execution
    ├── Flow Start Log
    ├── Flow Operation Log
    └── Flow Completion Log

Apex Controller

The Apex controller can handle both approaches:

LogDemoController.cls
public with sharing class LogDemoController {
    @AuraEnabled
    public static void apexActionThatTriggersFlow(String transactionId, Boolean cacheEnabled) {
        if (cacheEnabled) {
            Triton.instance.withCache();
        } else if (String.isNotBlank(transactionId)) {
            Triton.instance.resumeTransaction(transactionId);
        }
        
        Triton.instance.debug(
            TritonTypes.Type.Backend,
            TritonTypes.Area.Community,
            'Flow Demo - Apex',
            'Performing DML to trigger flow'
        );
        
        try {
            // Update account to trigger flow
            List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 1];
            if (!accounts.isEmpty()) {
                accounts[0].Name = 'Flow Demo';
                update accounts;
            }
        } catch (Exception e) {
            Triton.instance.error(TritonTypes.Area.Community, e);
        }
    }
}

The Flow

Flow Configuration

  1. Add the Triton Apex action as the first element

  2. Pass the Interview GUID using $Flow.InterviewGuid

Choosing Between Approaches

Direct Parameter Passing

  • Best for simple, linear execution paths

  • More explicit and easier to debug

  • Recommended for initial implementation

Platform Cache

  • Better for complex scenarios with multiple components

  • Handles asynchronous operations more elegantly

  • Useful when direct parameter passing becomes cumbersome

Both approaches will result in the same unified log structure, showing the complete execution path from LWC through Apex and into the Flow.

The Result

Regardless of the approach chosen, you'll see this log structure:

  • Parent LWC Debug Log

    1. Child Apex Debug Log

    2. Child Flow Debug Log

    3. Additional Flow logs as configured

    4. Any error logs if exceptions occur

This unified view provides complete visibility into your execution path across all three technologies.

Handling Flow Failures

When an unhandled error occurs in your Flow, Pharos automatically captures and correlates this failure with your existing transaction logs. This is possible because:

  1. The Flow's Interview GUID is passed to each Triton log action

  2. Pharos automatically captures unhandled Flow errors

  3. The transaction ID links everything together

Example of an Unhandled Flow Error

Let's modify our example to trigger an error in the Flow:

// LogDemoCacheController.cls
public with sharing class LogDemoCacheController {
    @AuraEnabled
    public static void apexActionThatTriggersFlow() {
        Triton.instance.withCache();
        
        Triton.instance.debug(
            TritonTypes.Type.Backend,
            TritonTypes.Area.Community,
            'Flow Demo - Apex',
            'Triggering flow that will fail'
        );
        
        try {
            // Update account with invalid data to cause Flow failure
            List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 1];
            if (!accounts.isEmpty()) {
                accounts[0].Name = null; // This will cause Flow validation to fail
                update accounts;
            }
        } catch (Exception e) {
            Triton.instance.error(TritonTypes.Area.Community, e);
        }
    }
}

Resulting Log Structure

When the Flow fails, you'll see this structure in Pharos:

Transaction: 7fb8c320-e8f9-4f1b-9742-1a5768b9e0d2
├── LWC Debug Log (Start)
├── Apex Debug Log (DML Operation)
└── Flow Execution
    ├── Flow Start Log
    ├── Flow Operation Log
    └── Unhandled Flow Error
        └── Flow Debug Details
            ├── Interview GUID
            ├── Current Element
            ├── Error Message
            └── Stack Trace

The automatic correlation of unhandled Flow errors is a key feature that helps maintain visibility across your entire execution path, even when things go wrong. This is particularly valuable in production environments where direct Flow debugging isn't available.

Benefits of Automatic Error Correlation

  1. Complete Context: See the entire execution path that led to the failure

  2. No Extra Code: No additional error handling required to capture Flow failures

  3. Production Debugging: Valuable insight into production issues without debug logs

  4. Time Savings: Quickly identify where in the process the failure occurred

PreviousFlowsNextApex

Last updated 1 month ago

The Flow setup remains similar to what's detailed in , with one key difference - we need to ensure the Flow can access the same transaction context regardless of which approach was used to manage the transaction ID.

Configure logging parameters as described in the documentation

For more details on Flow logging and error handling, see the documentation.

〰️
Flows
Flows
Flows
Basic Flow