Custom Performance Marks
Drop browser-style performance marks anywhere in your LWC code with startPerformanceMark / endPerformanceMark — fully per-instance and Locker Service-safe.
The high-level helpers (timeBackendCall, timeUserInteraction, trackComponentLifecycle, trackComponentRender) cover the common cases. When you need to time something that doesn't fit those moulds — a chunk of client-side data shaping, an animation, a debounce window, a delay between async steps — reach for raw performance marks.
The API mirrors the browser's performance.mark() pattern:
this.triton.startPerformanceMark('product-search');
// ... do work ...
this.triton.endPerformanceMark('product-search');startPerformanceMark stores a starting timestamp keyed by mark name and component instance. endPerformanceMark computes the duration, emits a TYPE.PERFORMANCE log entry, and clears the mark.
When to Reach for Marks
Marks are the right tool when:
You're measuring purely client-side work that has no backend round trip — e.g. parsing a CSV, computing a derived data structure, an animation timeline.
You want to wrap both sides of an async boundary that the higher-level helpers don't model — e.g. starting a timer when a debounce fires, stopping it when the next user event arrives.
You want a single duration record without the auto-generated start/complete pair that
timeBackendCallproduces.
If your work is an Apex call or a user-triggered flow, the higher-level helpers are almost always a better fit — they give you start/complete records and standard error handling for free.
Example: Wrapping a Search Flow
async handleSearch() {
const searchTerm = this.searchInput.value;
this.triton.startPerformanceMark('product-search');
try {
const results = await this.triton.timeBackendCall(
'searchProducts',
() => searchProducts({ searchTerm })
).execute();
this.products = results;
} finally {
// Always pair the start with an end — even on failure.
this.triton.endPerformanceMark('product-search');
}
}This pattern is handy when you want a single "user-perceived search latency" record (the mark) plus the more granular backend call timing inside it. The mark captures the whole stretch — including the time between the backend response and the UI being ready — while the backend call captures just the round trip.
Per-Instance Isolation
Marks are stored per component instance, just like lifecycle and render counts. Two instances of the same component can each have an active product-search mark and neither will interfere with the other.
What Gets Logged
Each endPerformanceMark call emits one log entry:
type
TYPE.PERFORMANCE
summary
Performance: <componentName> - <markName>
details
Duration: <ms>ms
duration
the measured milliseconds
action
the mark name
No log is produced on startPerformanceMark — marks are silent until they're closed. If you call endPerformanceMark for a name that was never started, Triton emits a console warning and skips the log.
Locker Service Compatibility
Mark timing uses the same getCurrentTime() helper described in Component Lifecycle: performance.now() when available, Date.now() otherwise. You get sub-millisecond precision where allowed, and you never need a fallback path in your code.
Best Practices
Always pair
startPerformanceMarkwithendPerformanceMark. Wrap the end infinallyso an exception in the middle doesn't leave a dangling mark.Use descriptive, hyphenated names. They show up directly in dashboards (
actioncolumn).Bind first. Custom marks require
bindToComponent()— without it you'll just get a console warning and no log.Don't double up. If you're already wrapping with
timeBackendCallortimeUserInteraction, you usually don't need a custom mark on top — the helpers already record duration.
Last updated