> 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/beyond-apex/profiling/custom-marks.md).

# Custom Performance Marks

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:

```javascript
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 `timeBackendCall` produces.

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

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

{% hint style="info" %}
You can have multiple marks active at once. Names are independent: `startPerformanceMark('search')` and `startPerformanceMark('hydrate')` coexist happily and end independently.
{% endhint %}

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

```javascript
export default class ProductSearch extends LightningElement {
    triton;

    connectedCallback() {
        this.triton = new Triton().bindToComponent('ProductSearch');
        this.triton.trackComponentLifecycle('connected');
    }

    async search() {
        // Each instance's mark is isolated.
        this.triton.startPerformanceMark('search-operation');

        const results = await this.triton.timeBackendCall(
            'searchProducts',
            () => searchProducts({ term: this.searchTerm })
        ).execute();

        this.triton.endPerformanceMark('search-operation');
        this.products = results;
    }
}
```

## What Gets Logged

Each `endPerformanceMark` call emits one log entry:

| Field      | Value                                       |
| ---------- | ------------------------------------------- |
| `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](/pharos-triton/beyond-apex/profiling/component-lifecycle.md#locker-service-safe-timing): `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 `startPerformanceMark` with `endPerformanceMark`.** Wrap the end in `finally` so an exception in the middle doesn't leave a dangling mark.
* **Use descriptive, hyphenated names.** They show up directly in dashboards (`action` column).
* **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 `timeBackendCall` or `timeUserInteraction`, you usually don't need a custom mark on top — the helpers already record duration.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://triton.pharos.ai/pharos-triton/beyond-apex/profiling/custom-marks.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
