Newer Version Available
Considerations for Custom Interactions
| Available in: both Salesforce Classic and Lightning Experience |
| Available in: Enterprise, Performance, Unlimited, and Developer Editions |
As an ISV partner, the complex features that you develop in your managed packages could involve multiple actions on different objects, callouts to Apex functions, and much more. It can be difficult to interpret how your subscribers interacted with specific packaged components via your downloaded App Analytics package usage logs and summaries.
To provide you with more clarity about your subscribers’ events in custom ways and at different granularity levels, create custom interactions in your managed packages using Apex.
With Apex custom interactions, you can discover:
- Which app feature a user interacted with
- How users flowed through a specific user journey
- Which UI components a user interacted with
Keep these considerations in mind:
- A custom interaction can appear for a given user request up to 50 times. This limit avoids flooding the logs due to large loops.
- We recommend that you don’t call IsvPartners.AppAnalytics.logCustomInteraction from inside a loop.
- If the IsvPartners.AppAnalytics.logCustomInteraction method is called from a running Apex test, no AppExchange App Analytics package usage log or package usage summary data is produced.
Log Custom Interactions
- In your packaged Apex code, include Apex enums that are associated with the events that you want to log as custom interactions.
- In your Apex code, invoke IsvPartners.AppAnalytics.logCustomInteraction, using the enums that you created.
- Test your code by running it in your development environment and checking your debug logs to be certain that the custom interactions you created are being logged. Ensure that your debug log levels for Apex Code are set to FINE.
- After you’re finished with your implementation, publish a new version of your managed package.
- After subscribers install your package, retrieve your package usage logs and package usage summaries. Filter your package usage log data on custom_entity_type by CustomInteractionLabel, and on log_record_type by CustomInteraction. Or filter your package usage summary data on custom_entity_type by CustomInteractionLabel.
- Analyze your custom interaction data.
Example
Let’s suppose you have a Lightning Web Component (LWC). Your LWC provides a list of related contacts for each Account record, uses a table layout, and is wired to an Apex class. You add a new card layout to your LWC. To track how well users are adopting this new layout, you log an interaction when a user switches between layouts.

In your code, include Apex enums and invoke IsvPartners.AppAnalytics.logCustomInteraction.
Your LWC HTML code:
1<template>
2 <div
3 class="slds-var-m-top_medium slds-var-m-bottom_x-large slds-box slds-theme_default"
4 >
5 <h2 class="slds-text-heading_medium slds-var-m-bottom_medium">
6 Change data view
7 </h2>
8 <!-- Button group: simple buttons -->
9 <lightning-button-group class="slds-var-m-bottom_medium">
10 <lightning-button
11 label="Table"
12 variant={tableVariant}
13 onclick={handleClick}
14 ></lightning-button>
15 <lightning-button
16 label="Card"
17 variant={cardVariant}
18 onclick={handleClick}
19 ></lightning-button>
20 </lightning-button-group>
21 <template lwc:if={displayTable}>
22 <lightning-datatable
23 key-field="id"
24 data={records}
25 columns={columns}
26 ></lightning-datatable>
27 </template>
28 <template lwc:if={displayCard}>
29 <div class="slds-grid slds-wrap slds-grid_pull-padded-small">
30 <template for:each={records} for:item="contact">
31 <div
32 class="slds-col slds-small-size_1-of-1 slds-large-size_1-of-2 slds-var-p_small"
33 key={contact.id}
34 >
35 <lightning-card
36 variant="Narrow"
37 title={contact.name}
38 icon-name="standard:contact"
39 >
40 <div class="slds-var-p-horizontal_small">
41 <p>{contact.name}</p>
42 <p>{contact.title}</p>
43 <p>
44 <lightning-formatted-phone
45 value={contact.phone}
46 ></lightning-formatted-phone>
47 </p>
48 <p>
49 <lightning-formatted-email
50 value={contact.email}
51 ></lightning-formatted-email>
52 </p>
53 </div>
54 </lightning-card>
55 </div>
56 </template>
57 </div>
58 </template>
59 </div>
60</template>Your LWC JavaScript code:
1import { LightningElement, wire, api } from "lwc";
2import { getRelatedListRecords } from "lightning/uiRelatedListApi";
3import logInteraction from "@salesforce/apex/LogContactListInteraction.log";
4
5export default class ContactList extends LightningElement {
6 @api recordId;
7 error;
8 records;
9 displayTable = true;
10 displayCard = false;
11 columns = [
12 { label: "Name", fieldName: "name" },
13 { label: "Title", fieldName: "title" },
14 { label: "Email", fieldName: "email", type: "email" },
15 { label: "Phone", fieldName: "phone", type: "phone" }
16 ];
17 @wire(getRelatedListRecords, {
18 parentRecordId: "$recordId",
19 relatedListId: "Contacts",
20 fields: [
21 "Contact.Name",
22 "Contact.Id",
23 "Contact.Phone",
24 "Contact.Email",
25 "Contact.Title"
26 ],
27 sortBy: ["Contact.Name"]
28 })
29 contactList({ error, data }) {
30 if (data) {
31 this.records = data.records.map((item) => {
32 return {
33 name: item.fields.Name.value,
34 id: item.fields.Id.value,
35 title: item.fields.Title.value,
36 email: item.fields.Email.value,
37 phone: item.fields.Phone.value
38 };
39 });
40 this.error = undefined;
41 } else if (error) {
42 this.error = error;
43 this.records = undefined;
44 }
45 }
46
47 handleClick(event) {
48 if (event.target.label.toLowerCase() === "table") {
49 this.displayTable = true;
50 this.displayCard = false;
51 logInteraction({ type: "table" });
52 } else if (event.target.label.toLowerCase() === "card") {
53 this.displayTable = false;
54 this.displayCard = true;
55 logInteraction({ type: "card" });
56 }
57 }
58 get cardVariant() {
59 return this.displayCard === true ? "brand" : "";
60 }
61 get tableVariant() {
62 return this.displayTable === true ? "brand" : "";
63 }
64}Your Apex class:
1public class LogContactListInteraction {
2 public Enum ContactListLayouts { TABLE, CARD }
3
4 @AuraEnabled
5 public static void log(String type) {
6 try {
7 IsvPartners.AppAnalytics.logCustomInteraction(getInteractionLabel(type));
8 } catch (Exception e) {
9 throw new AuraHandledException(e.getMessage());
10 }
11 }
12
13 private static ContactListLayouts getInteractionLabel(String type) {
14 if (type.toLowerCase() == 'table') {
15 return ContactListLayouts.TABLE;
16 } else if (type.toLowerCase() == 'card') {
17 return ContactListLayouts.CARD;
18 }
19 return null;
20 }
21}Next, you test your code. With your Apex code debug log level set to FINE, confirm that the custom interactions are logged by finding events in your debug logs called APP_ANALYTICS_FINE, APP_ANALYTICS_WARN, or APP_ANALYTICS_ERROR.
1APP_ANALYTICS_FINE [External]IsvPartners.AppAnalytics.logCustomInteraction was called, but not from an installed managed package.
2This means that the code is ready to be packaged.