mirror of
https://github.com/launchdarkly/ai-tooling.git
synced 2026-09-14 20:00:40 +08:00
feat: [MTRX-1890] adding skills for metric choose, metric create, metric instrument (#22)
* feat: adding skills for metric choose, metric create, metric instrument * updated skills * generated * remove redundant skills
This commit is contained in:
+24
@@ -123,6 +123,30 @@
|
||||
"devops",
|
||||
"mcp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "launchdarkly-metric-choose",
|
||||
"description": "Choose the right metrics for a LaunchDarkly experiment, guarded rollout, or release policy. Use when the user wants to know which metrics to use, which is the primary metric for an experiment, what guardrails to add, or which events to monitor in a rollout. Surfaces what will auto-attach from existing release policies before making additional recommendations.",
|
||||
"path": "skills/metrics/launchdarkly-metric-choose",
|
||||
"version": "1.0.0-experimental",
|
||||
"license": "Apache-2.0",
|
||||
"compatibility": "Requires the remotely hosted LaunchDarkly MCP server"
|
||||
},
|
||||
{
|
||||
"name": "launchdarkly-metric-create",
|
||||
"description": "Create a LaunchDarkly metric that measures what matters for an experiment or rollout. Use when the user wants to create a metric, track an event, measure page views, button clicks, conversion, latency, error rate, or any custom numeric or binary outcome. Instruments the event first when needed (including SDK setup and .env), then creates and verifies the metric.",
|
||||
"path": "skills/metrics/launchdarkly-metric-create",
|
||||
"version": "1.4.0-experimental",
|
||||
"license": "Apache-2.0",
|
||||
"compatibility": "Requires the remotely hosted LaunchDarkly MCP server"
|
||||
},
|
||||
{
|
||||
"name": "launchdarkly-metric-instrument",
|
||||
"description": "Instrument a LaunchDarkly metric event in a codebase by adding a track() call. Use when the user wants to wire up an event, instrument an action for a metric, add tracking to a feature, or confirm that an event is flowing to LaunchDarkly.",
|
||||
"path": "skills/metrics/launchdarkly-metric-instrument",
|
||||
"version": "1.0.0-experimental",
|
||||
"license": "Apache-2.0",
|
||||
"compatibility": "Requires the remotely hosted LaunchDarkly MCP server"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
---
|
||||
name: launchdarkly-metric-choose
|
||||
description: "Choose the right metrics for a LaunchDarkly experiment, guarded rollout, or release policy. Use when the user wants to know which metrics to use, which is the primary metric for an experiment, what guardrails to add, or which events to monitor in a rollout. Surfaces what will auto-attach from existing release policies before making additional recommendations."
|
||||
license: Apache-2.0
|
||||
compatibility: Requires the remotely hosted LaunchDarkly MCP server
|
||||
metadata:
|
||||
author: launchdarkly
|
||||
version: "1.0.0-experimental"
|
||||
---
|
||||
|
||||
# LaunchDarkly Metric Choose
|
||||
|
||||
You're using a skill that helps users select the right metrics before setting up an experiment, guarded rollout, or release policy. Your job is to understand the feature context, surface what will auto-attach from existing project policies, inventory what's available and healthy, and produce a clear typed recommendation.
|
||||
|
||||
This skill is advisory. It does not create metrics, attach them to experiments, or configure rollouts. For those tasks, see the related skills at the end of this document.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This skill requires the remotely hosted LaunchDarkly MCP server to be configured in your environment.
|
||||
|
||||
**Required MCP tools:**
|
||||
- `list-metrics` — inventory available metrics with their types and event keys
|
||||
- `list-metric-events` — check which event keys have recent activity
|
||||
|
||||
**Optional MCP tools (enhance workflow):**
|
||||
- `list-release-policies` — fetch project-level policies that configure which metrics auto-attach to guarded rollouts. Use this for the guarded rollout and release policy paths.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Identify the Context
|
||||
|
||||
Ask two questions upfront:
|
||||
|
||||
1. **What is this for?**
|
||||
- **(a) Experiment** — testing a hypothesis with a flag variant
|
||||
- **(b) Guarded rollout** — progressively rolling out a change with automatic regression detection
|
||||
- **(c) Release policy** — creating or editing a project-wide policy that configures default metrics for all guarded rollouts matching certain conditions
|
||||
|
||||
2. **What is the change?**
|
||||
- Flag key (if applicable)
|
||||
- Plain-language description: "Rolling out a new checkout flow" / "Testing a new recommendation algorithm"
|
||||
|
||||
### Step 2: Fetch Existing Configuration (Guarded Rollout and Release Policy only)
|
||||
|
||||
**For experiments — skip this step.** There is no pre-existing configuration to surface.
|
||||
|
||||
**For guarded rollouts and release policy work**, call `list-release-policies` first:
|
||||
|
||||
```
|
||||
list-release-policies(projectKey)
|
||||
```
|
||||
|
||||
Surface the results before making any recommendations:
|
||||
|
||||
```
|
||||
Your project has 2 release policies:
|
||||
|
||||
Policy: "Production guardrails" (applies to: environment=production)
|
||||
Auto-attaches to guarded rollouts:
|
||||
✓ api-error-rate (count, LowerThanBaseline)
|
||||
✓ p95-latency (value, LowerThanBaseline)
|
||||
✓ [Metric group] Core Platform Health (3 metrics)
|
||||
|
||||
Policy: "Default" (applies to: all environments)
|
||||
No metrics configured.
|
||||
```
|
||||
|
||||
This tells the user what's already covered before they choose anything additional. For a guarded rollout, these metrics will appear automatically — the recommendation is about what to add on top, not rebuild from scratch.
|
||||
|
||||
If no policies exist or none have metrics configured, note that all metrics must be selected manually.
|
||||
|
||||
### Step 3: Inventory Available Metrics with Event Health
|
||||
|
||||
Call `list-metrics` to see all metrics in the project, then cross-reference with `list-metric-events`.
|
||||
|
||||
Organize into two groups:
|
||||
|
||||
| Group | Criteria | Note |
|
||||
|-------|----------|------|
|
||||
| **Healthy** | Event key appears in `list-metric-events` | Safe to recommend |
|
||||
| **At-risk** | Event key absent from `list-metric-events` | Warn: may not produce data |
|
||||
|
||||
Show this inventory before recommending — it may reveal that a metric the user has in mind has no events flowing.
|
||||
|
||||
### Step 4: Recommend
|
||||
|
||||
The reasoning differs meaningfully by context.
|
||||
|
||||
---
|
||||
|
||||
#### (a) Experiment
|
||||
|
||||
**Start with the hypothesis, not the metric list.**
|
||||
|
||||
Ask the user to complete this sentence before looking at available metrics:
|
||||
|
||||
> "If this change succeeds, [metric] will [increase / decrease]."
|
||||
|
||||
The primary metric must directly measure that hypothesis — not a proxy, not a correlation. If the user can't complete the sentence, help them get there first.
|
||||
|
||||
**Propose one primary metric.** It must:
|
||||
- Directly measure the hypothesis
|
||||
- Have events actively flowing
|
||||
- Have an unambiguous success direction (`HigherThanBaseline` or `LowerThanBaseline`)
|
||||
|
||||
**Propose typed secondary metrics.** Suggest at least one of each type that applies:
|
||||
|
||||
| Type | Purpose | Example |
|
||||
|------|---------|---------|
|
||||
| **Guardrail** | Did the change break anything? | Error rate, crash rate, latency p95 |
|
||||
| **Counter-metric** | Did A improve at the cost of B? | If primary is conversion, add support tickets or session length |
|
||||
| **Supporting signal** | Does correlated behavior confirm the hypothesis? | If primary is signup, add onboarding step 2 completion |
|
||||
|
||||
One of each type is usually the right amount. More secondary metrics add noise and interpretation burden.
|
||||
|
||||
---
|
||||
|
||||
#### (b) Guarded Rollout
|
||||
|
||||
Guarded rollouts are safety mechanisms, not experiments. Each metric you add is a potential automatic rollback trigger — if it regresses beyond its threshold before the rollout completes, LaunchDarkly can stop and revert the release.
|
||||
|
||||
**Start from what auto-attaches.** After surfacing the release policy results in Step 2, ask: "Are the auto-attached metrics enough, or do you want to add more for this specific rollout?"
|
||||
|
||||
**When recommending additional metrics:**
|
||||
- Bias toward reliability — engineering metrics (error rate, latency, crash rate) with stable, predictable baselines
|
||||
- Avoid exploratory product metrics that are noisy or hard to interpret under regression analysis
|
||||
- **Fewer is better.** Two or three high-signal metrics is the right size. More than five creates false positive rollback risk.
|
||||
- **Only recommend metrics with events actively flowing.** An at-risk metric in a guarded rollout either produces no signal or, worse, triggers a false rollback due to data quality issues, not a real regression.
|
||||
|
||||
Suggested starting point for any guarded rollout (if not already covered by a policy):
|
||||
1. Error rate — are we seeing more errors in the new variation?
|
||||
2. Latency / response time — is the new variation slower?
|
||||
3. One domain-specific metric tied to the core user action the change affects
|
||||
|
||||
---
|
||||
|
||||
#### (c) Release Policy
|
||||
|
||||
Release policies apply to every rollout in the project that matches their conditions. This is the highest bar.
|
||||
|
||||
**Start from the current state.** After surfacing existing policies in Step 2, ask: "Which policy are you editing, or do you want to create a new one? What environments or flag conditions will it apply to?"
|
||||
|
||||
**When recommending metrics for a policy:**
|
||||
- **2–3 metrics maximum.** More than that turns the policy into a burden on every rollout, including ones where the metrics don't apply well.
|
||||
- **Only recommend metrics with a long, stable event history.** If an event has been flowing reliably for months, it's a safe project-wide default. Occasional gaps will create problems at scale.
|
||||
- **Push back on additions.** If the user proposes more than 3, ask which ones they'd remove. The discipline of choosing is the point.
|
||||
- **Explain scope conditions.** A policy scoped to `environment=production` only applies to production rollouts. Help the user think through whether they want the same metrics in staging (where baselines may differ) or a separate policy.
|
||||
|
||||
Typical strong policy candidates: error rate, a core conversion or engagement metric, latency.
|
||||
|
||||
### Step 5: Deliver the Recommendation
|
||||
|
||||
Output a clear, named list. Be explicit about what each metric is for and what's already covered:
|
||||
|
||||
```
|
||||
Recommended metrics for: new checkout flow guarded rollout (environment: production)
|
||||
|
||||
AUTO-ATTACHED (from "Production guardrails" policy):
|
||||
✓ api-error-rate (count, LowerThanBaseline)
|
||||
✓ p95-latency (value, LowerThanBaseline)
|
||||
|
||||
ADDITIONAL — recommended for this rollout:
|
||||
✓ checkout-conversion (occurrence, HigherThanBaseline)
|
||||
→ Confirms the rollout isn't degrading the core conversion the feature targets
|
||||
|
||||
⚠ page-load-time — no recent events. Instrument the event before including it,
|
||||
or remove it from the list to avoid a false rollback trigger.
|
||||
```
|
||||
|
||||
Then close with next steps:
|
||||
- If a metric the user needs doesn't exist → use the **metric-create** skill
|
||||
- If an event isn't flowing → use the **metric-instrument** skill
|
||||
- Once the list is confirmed → configure the guarded rollout or experiment (via the LaunchDarkly UI or API)
|
||||
|
||||
## Important Context
|
||||
|
||||
- **Mid-experiment metric changes require a restart.** LaunchDarkly snapshots the metric configuration when an experiment starts. Adding, removing, or changing metrics after launch requires stopping the experiment and restarting it — historical data from before the change is not comparable. Raise this immediately if the user mentions they're mid-experiment.
|
||||
- **A primary metric with no events is worse than no primary metric.** The experiment produces no statistical output. Event health is a hard requirement for the primary metric.
|
||||
- **CUPED and percentile analysis are incompatible.** If the experiment uses CUPED variance reduction, percentile-based metrics (e.g. p95 latency) silently degrade to mean-based analysis. Flag this if the user selects a percentile metric in a CUPED-enabled experiment.
|
||||
- **Context kind mismatches cause missing data.** If the metric event is tracked with a `device` context but the experiment randomizes on `user`, the event won't be attributed correctly. Confirm that the context kind in `track()` calls matches the experiment's randomization unit.
|
||||
- **Release policy metrics must share the same context kind.** All metrics in a guarded rollout release policy must use the same randomization unit. If the user proposes metrics with mismatched context kinds, flag it before they try to configure the policy.
|
||||
|
||||
## Related Skills
|
||||
|
||||
- [`launchdarkly-metric-create`](../launchdarkly-metric-create/SKILL.md) — create a metric that doesn't exist yet
|
||||
- [`launchdarkly-metric-instrument`](../launchdarkly-metric-instrument/SKILL.md) — add a `track()` call so events start flowing
|
||||
@@ -0,0 +1,300 @@
|
||||
---
|
||||
name: launchdarkly-metric-create
|
||||
description: "Create a LaunchDarkly metric that measures what matters for an experiment or rollout. Use when the user wants to create a metric, track an event, measure page views, button clicks, conversion, latency, error rate, or any custom numeric or binary outcome. Instruments the event first when needed (including SDK setup and .env), then creates and verifies the metric."
|
||||
license: Apache-2.0
|
||||
compatibility: Requires the remotely hosted LaunchDarkly MCP server
|
||||
metadata:
|
||||
author: launchdarkly
|
||||
version: "1.4.0-experimental"
|
||||
---
|
||||
|
||||
# LaunchDarkly Metric Create
|
||||
|
||||
You're using a skill that will guide you through creating a LaunchDarkly metric. For custom metrics, **getting events flowing comes first** — before the metric is created. Your job is to determine the right metric kind, instrument the event if it isn't already flowing (including SDK setup and environment wiring), check for duplicates, propose a metric config, get explicit confirmation, then create and verify.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This skill requires the remotely hosted LaunchDarkly MCP server to be configured in your environment.
|
||||
|
||||
**Required MCP tools:**
|
||||
- `create-metric` — create the metric
|
||||
- `get-metric` — verify it after creation
|
||||
- `get-environment` — fetch the client-side SDK key when instrumenting
|
||||
|
||||
**Optional MCP tools (enhance workflow):**
|
||||
- `list-metrics` — check for existing metrics with the same event key and understand naming conventions
|
||||
- `list-metric-events` — discover which event keys have recent activity before committing to one (custom metrics only)
|
||||
|
||||
## Two Different "Projects" — Never Confuse Them
|
||||
|
||||
Users work with two completely separate things that both get called "project." You must keep these distinct at all times:
|
||||
|
||||
| | What it is | How the user refers to it | What you do with it |
|
||||
|---|---|---|---|
|
||||
| **LaunchDarkly project** | The project inside the user's LD account where the metric will be created | Usually sounds like an environment or team name: `my-app`, `anthony-agent-dev-5000`, `production` | Pass as `projectKey` to all MCP tool calls |
|
||||
| **Local codebase** | The developer's application on disk that you'll instrument with a `track()` call | Often a folder name, repo name, or app name: `checkout_proj`, `frontend`, `my-react-app` | Use to find and edit source files |
|
||||
|
||||
**Rules for resolving these from user input:**
|
||||
|
||||
- If the user says *"my application at X"* or *"my codebase"* or *"my repo"* → they mean the **local codebase**. `X` is a folder path or project name, not a LaunchDarkly key.
|
||||
- If the user says *"add it to X"* or *"in LaunchDarkly"* or *"my LD project"* → they mean the **LaunchDarkly project**. `X` is the `projectKey` for API calls.
|
||||
- A user can name their local codebase `checkout_proj` while their LaunchDarkly project is `anthony-agent-dev-5000`. These are unrelated.
|
||||
- **Never assume the local codebase name is a LaunchDarkly project key.** If you're unsure which is which, ask directly: *"Just to confirm — what's your LaunchDarkly project key? (This is different from your local app name — you can find it in the LD UI under Account Settings > Projects.)"*
|
||||
|
||||
When both are needed (e.g. for a custom metric with instrumentation), confirm each explicitly before proceeding.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Determine the Metric Kind
|
||||
|
||||
LaunchDarkly has three metric kinds. **Choose the right one before anything else.**
|
||||
|
||||
| Kind | How events are collected | Requires |
|
||||
|------|--------------------------|----------|
|
||||
| `custom` | Developer calls `ldClient.track(eventKey)` in code | `eventKey` |
|
||||
| `pageview` | Fires automatically when a user visits a matching URL — **no SDK call needed** | `urls` (URL match rules) |
|
||||
| `click` | Fires automatically when a user clicks a CSS selector on a matching URL — **no SDK call needed** | `urls` + `selector` |
|
||||
|
||||
**Decision rules:**
|
||||
- User says "track when someone views a page / visits a URL" → **`pageview`** (preferred — no instrumentation required)
|
||||
- User says "track when someone clicks a button / link" → **`click`**
|
||||
- User says "track a custom event" or references a `track()` call → **`custom`**
|
||||
|
||||
When `pageview` or `click` would work, suggest it over `custom` — it requires no code changes.
|
||||
|
||||
### Step 2: Resolve the Data Source
|
||||
|
||||
**For `pageview` and `click` metrics:**
|
||||
- Ask for the URL(s) to match. Confirm the `kind` of URL match rule:
|
||||
- `substring` — URL contains this string (most common)
|
||||
- `exact` — URL must match exactly
|
||||
- `canonical` — matches the canonical URL
|
||||
- `regex` — full regex pattern
|
||||
- For `click` metrics, also ask for the CSS selector (e.g. `.checkout-btn`, `#submit`).
|
||||
- Skip `list-metric-events` — these metrics don't use event keys.
|
||||
- Skip to Step 3.
|
||||
|
||||
**For `custom` metrics — check events first, instrument if needed:**
|
||||
|
||||
Call `list-metric-events` immediately to see which event keys are already flowing:
|
||||
|
||||
```
|
||||
list-metric-events(projectKey, environmentKey?)
|
||||
```
|
||||
|
||||
**Case A — the event key is already in the list:** Confirm the key with the user and proceed to Step 3. No instrumentation needed.
|
||||
|
||||
**Case B — the event key is NOT in the list:** The metric can't measure anything without events. **Instrument the event now before creating the metric.** Do not simply warn and ask whether to proceed — treat instrumentation as the default next action.
|
||||
|
||||
Follow the instrumentation sub-workflow below, then re-check `list-metric-events` to confirm events are flowing before moving to Step 3. Only skip instrumentation if the user explicitly says they want to create the metric first and wire the event up later — in that case, remind them at the end that the metric will produce no data until the event is tracked.
|
||||
|
||||
### Step 2b: Instrument the Event (when events aren't flowing)
|
||||
|
||||
This sub-workflow gets a `track()` call into the codebase and connects the app to the right LaunchDarkly environment. Complete all steps before returning to the main workflow.
|
||||
|
||||
**1. Find the right place in the codebase.**
|
||||
Locate the function or handler where the event naturally occurs (e.g. a checkout submit handler, a form submission callback). Read the relevant source files to understand the existing structure before making changes.
|
||||
|
||||
**2. Determine the event key.**
|
||||
If the user hasn't specified one, propose a descriptive kebab-case key that matches what the code is doing (e.g. `checkout-completed`, `signup-submitted`). Confirm with the user before using it.
|
||||
|
||||
**3. Fetch the client-side SDK key.**
|
||||
Ask the user which environment they want to connect to (e.g. "test", "production", "staging") — just the environment name. Then call:
|
||||
|
||||
```
|
||||
get-environment(projectKey, environmentKey)
|
||||
```
|
||||
|
||||
Use the `clientSideId` from the response.
|
||||
|
||||
**4. Write the environment file.**
|
||||
Check whether a `.env` file (or equivalent — `.env.local`, `.env.development`, etc.) already exists.
|
||||
|
||||
- If the file **does not exist**, create it.
|
||||
- If the file **exists and already contains the key** (e.g. `VITE_LD_CLIENT_SIDE_ID`), compare the stored value to the `clientSideId` returned by `get-environment`. If they differ, surface the discrepancy to the user:
|
||||
> "Your `.env` already has `VITE_LD_CLIENT_SIDE_ID=<old>`, but `get-environment` returned `<new>` for the `<env>` environment. Should I update it?"
|
||||
Do not silently keep the old value — a mismatched client-side ID means events will be sent to the wrong project or environment.
|
||||
- If the file exists but the key is absent, add it without touching other values.
|
||||
|
||||
Use the variable name appropriate to the project's build tool (e.g. `VITE_LD_CLIENT_SIDE_ID` for Vite, `REACT_APP_LD_CLIENT_SIDE_ID` for CRA, `NEXT_PUBLIC_LD_CLIENT_SIDE_ID` for Next.js).
|
||||
|
||||
**4b. Set the SDK base URL if the user is not on app.launchdarkly.com.**
|
||||
The SDK defaults to `app.launchdarkly.com` for all traffic. If the user is on a different LaunchDarkly deployment (e.g. an internal staging environment like catamorphic, or a dedicated instance), events and flag evaluations will silently go to the wrong host.
|
||||
|
||||
Detect this by inspecting any `_links` or UI URLs in MCP API responses — if they point to a host other than `app.launchdarkly.com`, you are on a non-production deployment. When in doubt, ask:
|
||||
> "Are you connecting to app.launchdarkly.com or a different LaunchDarkly instance? (e.g. an internal or staging environment)"
|
||||
|
||||
If they are on a non-standard host, add three additional variables to the `.env` file:
|
||||
|
||||
```
|
||||
VITE_LD_BASE_URL=https://<their-host>
|
||||
VITE_LD_STREAM_URL=https://clientstream.<their-host-domain>
|
||||
VITE_LD_EVENTS_URL=https://events.<their-host-domain>
|
||||
```
|
||||
|
||||
And pass them to the SDK `options` at init time:
|
||||
|
||||
```js
|
||||
asyncWithLDProvider({
|
||||
clientSideID,
|
||||
context: { kind: 'user', anonymous: true },
|
||||
options: {
|
||||
baseUrl: import.meta.env.VITE_LD_BASE_URL,
|
||||
streamUrl: import.meta.env.VITE_LD_STREAM_URL,
|
||||
eventsUrl: import.meta.env.VITE_LD_EVENTS_URL,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Omit the `options` block entirely if they are on `app.launchdarkly.com` — the defaults are correct and no extra config is needed.
|
||||
|
||||
**5. Install and initialize the SDK** if it isn't already present.
|
||||
Check `package.json` (or the equivalent dependency file) for an existing LD SDK. If none is found, install the right one for the project's stack:
|
||||
- React → `launchdarkly-react-client-sdk`
|
||||
- Browser JS → `launchdarkly-js-client-sdk`
|
||||
- Node.js server → `@launchdarkly/node-server-sdk`
|
||||
|
||||
Initialize the SDK at the app's entry point (e.g. wrap the React root with `LDProvider`, configure `LDClient.init()` in the server entry, etc.). Pass the client-side ID from the env file. Use an anonymous user/context as the default unless the app already manages user context.
|
||||
|
||||
**6. Add the `track()` call.**
|
||||
In the location identified in step 1, add the call immediately before or after the action completes:
|
||||
|
||||
- Count / occurrence metric: `ldClient.track('event-key')`
|
||||
- Value metric: `ldClient.track('event-key', null, numericValue)`
|
||||
|
||||
Use optional chaining (`ldClient?.track(...)`) in client-side code where the client may not yet be initialized.
|
||||
|
||||
**7. Verify events are flowing.**
|
||||
After the instrumentation changes are made, remind the user to run the app and trigger the event at least once. Then call `list-metric-events` again to confirm the key appears before proceeding to metric creation.
|
||||
|
||||
### Step 3: Check for Existing Metrics
|
||||
|
||||
Before creating anything, use `list-metrics` to scan the project:
|
||||
|
||||
1. **Check for duplicates.** Search for metrics with the same event key, URL pattern, or similar names. Avoid creating a second metric that measures the same thing — instead, flag the existing metric and ask the user if they want to reuse it.
|
||||
2. **Learn the naming convention.** Are metric keys `kebab-case` or `snake_case`? Are there common tag patterns? Match what already exists.
|
||||
3. **Understand the tag taxonomy.** Tags like `team:growth`, `area:checkout`, or `type:guardrail` may already exist. Suggest relevant tags based on what the user describes.
|
||||
|
||||
### Step 4: Propose the Metric Configuration
|
||||
|
||||
Before calling any API, surface a proposed configuration in plain language for the user to confirm or edit.
|
||||
|
||||
**Determine measure type.** The right choice depends on what the user is trying to learn and how they'll use the metric — in an experiment, a guarded rollout, or a release policy. **Do not assume.** When the event is something a user can do repeatedly (click, add to cart, view page, etc.), always ask before proposing:
|
||||
|
||||
> "Are you trying to measure **how many times** this event happens in total (`count`), or **what percentage of users** triggered it at least once (`occurrence`)?"
|
||||
|
||||
Tie the question to their context:
|
||||
- **Experiments** — occurrence is common for conversion goals (did the treatment cause more users to do X?); count is better for engagement or volume goals (did the treatment cause more total actions?)
|
||||
- **Guarded rollouts / release policies** — occurrence is typical for error rate guardrails (what fraction of users hit an error?); count suits absolute volume guardrails (total error events)
|
||||
- **If the user explicitly says "percent of users" or "conversion rate"** → `occurrence`
|
||||
- **If the user explicitly says "number of times" or "total events"** → `count`
|
||||
|
||||
Only skip asking if the intent is unambiguous from context (e.g. "API latency" → `value`, "error rate" → `count`, "signup conversion" → `occurrence`).
|
||||
|
||||
| What the user wants to measure | Measure type | Means |
|
||||
|-------------------------------|-------------|-------|
|
||||
| Total times the event occurred | `count` | Raw event count per analysis unit |
|
||||
| Whether each user triggered the event at all | `occurrence` | Conversion / binary (did it happen?) |
|
||||
| A numeric value attached to the event | `value` | Latency, revenue, score, etc. |
|
||||
|
||||
**Determine success criteria:**
|
||||
|
||||
- **Higher is better** → `HigherThanBaseline` (conversion rate, revenue, engagement)
|
||||
- **Lower is better** → `LowerThanBaseline` (latency, error rate, bounce rate)
|
||||
|
||||
**Use common templates as defaults** when the user's intent is clear:
|
||||
|
||||
| User intent | kind | measure type | success criteria | unit |
|
||||
|-------------|------|-------------|-----------------|------|
|
||||
| Page visit / view rate | `pageview` | `occurrence` | `HigherThanBaseline` | — |
|
||||
| Button / link click rate | `click` | `occurrence` | `HigherThanBaseline` | — |
|
||||
| API latency / page load time | `custom` | `value` (average) | `LowerThanBaseline` | `ms` |
|
||||
| Signup / conversion rate | `custom` | `occurrence` | `HigherThanBaseline` | — |
|
||||
| Error count / rate | `custom` | `count` | `LowerThanBaseline` | — |
|
||||
| Revenue per user | `custom` | `value` (sum) | `HigherThanBaseline` | `USD` |
|
||||
|
||||
**Present the proposed config** before creating — don't silently fire the API:
|
||||
|
||||
```
|
||||
Proposed metric:
|
||||
Key: checkout-page-viewed
|
||||
Name: Checkout Page Viewed
|
||||
Kind: pageview (fires automatically on URL visit — no code change needed)
|
||||
URLs: substring match on "/checkout"
|
||||
Measure type: occurrence (did each user visit the page?)
|
||||
Success criteria: HigherThanBaseline
|
||||
|
||||
Proceed, or would you like to change anything?
|
||||
```
|
||||
|
||||
**STOP HERE.** Do not call any API. Do not proceed to Step 5. Wait for the user to explicitly confirm before doing anything else. The user must respond with an approval (e.g. "yes", "looks good", "proceed") before you call `create-metric`. If there is any ambiguity in the proposed config — such as a choice between `sum` vs `average`, or the event key name — ask that question as part of the proposal and wait for the answer before continuing.
|
||||
|
||||
### Step 5: Create the Metric
|
||||
|
||||
**Only proceed once the user has explicitly confirmed the proposed config in Step 4.** If you have not yet received a confirmation, go back and wait.
|
||||
|
||||
Once the user confirms, call `create-metric`. The tool handles the translation from `measureType` to the underlying API fields — you never need to pass `isNumeric` or `unitAggregationType` directly.
|
||||
|
||||
```
|
||||
create-metric(
|
||||
projectKey,
|
||||
key,
|
||||
name,
|
||||
kind, // "custom" | "pageview" | "click"
|
||||
eventKey?, // only for kind="custom"
|
||||
urls?, // only for kind="pageview" or "click": [{ kind, url }]
|
||||
selector?, // only for kind="click": CSS selector string
|
||||
measureType, // "count" | "occurrence" | "value"
|
||||
successCriteria, // "HigherThanBaseline" | "LowerThanBaseline"
|
||||
valueAggregation?, // only for measureType="value": "average" (default) or "sum"
|
||||
unit?, // display label: "ms", "USD", etc.
|
||||
description?,
|
||||
tags?
|
||||
)
|
||||
```
|
||||
|
||||
### Step 6: Verify
|
||||
|
||||
Use `get-metric` to confirm the metric was created with the right configuration:
|
||||
|
||||
1. **Key and name match** what was requested.
|
||||
2. **kind is correct** — `custom`, `pageview`, or `click`.
|
||||
3. **measureType is correct** — double-check by reading back the `measureType` field, not just `isNumeric`.
|
||||
4. **eventKey / urls / selector** are set to the intended values.
|
||||
5. **successCriteria** is correct.
|
||||
|
||||
Surface a summary to the user:
|
||||
|
||||
```
|
||||
✓ Metric created: checkout-page-viewed
|
||||
Kind: pageview (auto-tracked on URL visit)
|
||||
URLs: substring "/checkout"
|
||||
Measures: occurrence (conversion rate)
|
||||
Goal: Higher is better
|
||||
|
||||
View in LaunchDarkly: {_links.ui from the create-metric response}
|
||||
```
|
||||
|
||||
The `create-metric` tool returns a `_links.ui` field with the correct URL for the environment being used. Always use that value — never hard-code `app.launchdarkly.com`.
|
||||
|
||||
## Measure Type Reference
|
||||
|
||||
The `create-metric` tool translates `measureType` to the LD API fields internally. You never need to set `isNumeric` or `unitAggregationType` directly.
|
||||
|
||||
| measureType | isNumeric | unitAggregationType | Use for |
|
||||
|-------------|-----------|---------------------|---------|
|
||||
| `count` | false | sum | Raw event counts — error rate, click count |
|
||||
| `occurrence` | false | average | Conversion — did the user do the thing? |
|
||||
| `value` (average) | true | average | Per-user mean — average latency, average session length |
|
||||
| `value` (sum) | true | sum | Per-user total — total revenue, total items purchased |
|
||||
|
||||
For `value` metrics, `valueAggregation` defaults to `"average"`. Pass `valueAggregation: "sum"` for revenue or cumulative totals.
|
||||
|
||||
## Important Context
|
||||
|
||||
- **Prefer `pageview` and `click` over `custom` when possible.** They require no SDK instrumentation and work automatically in browser environments.
|
||||
- **Event keys are case-sensitive.** `checkout-completed` and `Checkout-Completed` are different events. Match the key exactly as it appears in your `track()` calls.
|
||||
- **Custom metrics without events produce no data.** A custom metric is only useful once its event key is actively being tracked in production (or the relevant environment). If you created the metric before instrumenting the event, remind the user.
|
||||
- **Metric keys are immutable.** Once created, a metric's key cannot be changed. Choose carefully.
|
||||
- **Metrics are project-scoped.** A metric created in one project is not visible in another. Make sure `projectKey` matches where the experiment or flag lives.
|
||||
- **One primary metric per experiment.** When attaching this metric to an experiment, clarify whether it's the primary metric (the one that determines success or failure) or a secondary metric (a guardrail or supporting signal). See the LaunchDarkly docs for experiment setup.
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
name: launchdarkly-metric-instrument
|
||||
description: "Instrument a LaunchDarkly metric event in a codebase by adding a track() call. Use when the user wants to wire up an event, instrument an action for a metric, add tracking to a feature, or confirm that an event is flowing to LaunchDarkly."
|
||||
license: Apache-2.0
|
||||
compatibility: Requires the remotely hosted LaunchDarkly MCP server
|
||||
metadata:
|
||||
author: launchdarkly
|
||||
version: "1.0.0-experimental"
|
||||
---
|
||||
|
||||
# LaunchDarkly Metric Instrument
|
||||
|
||||
You're using a skill that will guide you through adding a `track()` call to a codebase so a LaunchDarkly metric can measure it. Your job is to detect the SDK in use, find the right place in code to add the call, write it correctly, and verify that events are reaching LaunchDarkly.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This skill requires the remotely hosted LaunchDarkly MCP server to be configured in your environment.
|
||||
|
||||
**Required MCP tools:**
|
||||
- `list-metric-events` — verify events are flowing after instrumentation
|
||||
|
||||
**Optional MCP tools (enhance workflow):**
|
||||
- `get-project` — retrieve the SDK key for the right environment when SDK initialization is needed
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Detect the SDK
|
||||
|
||||
Before writing any code, understand the LaunchDarkly setup already in this codebase.
|
||||
|
||||
1. **Search for existing `track()` calls.** This is the fastest signal:
|
||||
- Look for `ldClient.track(`, `.track(`, `ld.track(`
|
||||
- If any exist, they tell you the SDK type, call signature, and context pattern in one shot — mirror those exactly.
|
||||
|
||||
2. **Search for SDK imports and initialization** if no `track()` calls exist:
|
||||
- Check `package.json`, `requirements.txt`, `go.mod`, `Gemfile`, `*.csproj` for an LD SDK dependency
|
||||
- Look for `LDClient`, `ldclient`, `launchdarkly-server-sdk`, `launchdarkly-node-server-sdk`, `launchdarkly-react-client-sdk`, etc.
|
||||
- Find the initialization block to understand how the client is accessed across the codebase
|
||||
|
||||
3. **Determine client-side or server-side.** This is the most critical distinction — it determines the `track()` signature:
|
||||
|
||||
| SDK type | `track()` signature | Notes |
|
||||
|----------|---------------------|-------|
|
||||
| Server-side (Node, Python, Go, Java, Ruby, .NET) | `ldClient.track(eventKey, context, data?, metricValue?)` | Context required per call |
|
||||
| Client-side (React, browser JS) | `ldClient.track(eventKey, data?, metricValue?)` | Context set at init, not per call |
|
||||
|
||||
See [SDK Track Patterns](references/sdk-track-patterns.md) for full examples by language.
|
||||
|
||||
### Step 2: Install & Initialize (if SDK not present)
|
||||
|
||||
Skip this step if the SDK is already in the codebase.
|
||||
|
||||
1. **Detect the package manager** from lockfiles: `package-lock.json` / `yarn.lock` / `pnpm-lock.yaml` → npm/yarn/pnpm; `Pipfile.lock` / `poetry.lock` → pip/poetry; `go.sum` → go modules; `Gemfile.lock` → bundler.
|
||||
|
||||
2. **Install the appropriate SDK** using the detected package manager. See [SDK Track Patterns](references/sdk-track-patterns.md) for the right package name per language.
|
||||
|
||||
3. **Get the SDK key** using `get-project` — fetch the project and choose the key for the environment the user wants to instrument (typically `production` or `staging` for initial testing).
|
||||
|
||||
4. **Add SDK initialization** following the patterns already in this codebase. If there's a central config or service layer, add the LD client there. See [SDK Track Patterns](references/sdk-track-patterns.md) for initialization examples.
|
||||
|
||||
### Step 3: Find the Right Placement
|
||||
|
||||
Locate where in the code the user action or event occurs.
|
||||
|
||||
1. **Ask if you're not sure** where the action happens. Don't guess at placement — a `track()` call in the wrong location (e.g. a render method instead of a submit handler) produces misleading data.
|
||||
|
||||
2. **Look for signals of the right location:**
|
||||
- Form submissions, button click handlers, API route completions, mutation hooks
|
||||
- Existing analytics calls (`segment.track()`, `mixpanel.track()`, `gtag()`) — these are often co-located with where LD track calls should go
|
||||
- Comments like `// TODO: track this`
|
||||
|
||||
3. **Show the candidate location** to the user before writing anything:
|
||||
```
|
||||
I'll add the track() call here, in the checkout submit handler (src/checkout/CheckoutForm.tsx, line 47).
|
||||
Does that look right?
|
||||
```
|
||||
|
||||
4. **Proceed once confirmed** (or if you're confident enough from codebase signals).
|
||||
|
||||
### Step 4: Write the `track()` Call
|
||||
|
||||
Write the call following the patterns found in Step 1.
|
||||
|
||||
**Server-side SDKs** — context is required:
|
||||
```typescript
|
||||
ldClient.track('checkout-completed', context);
|
||||
```
|
||||
|
||||
**Client-side SDKs** — context is implicit:
|
||||
```typescript
|
||||
ldClient.track('checkout-completed');
|
||||
```
|
||||
|
||||
**For `value` metrics** — include `metricValue` with the numeric measurement:
|
||||
```typescript
|
||||
// Server-side: latency metric (ms)
|
||||
ldClient.track('api-response-time', context, null, responseTimeMs);
|
||||
|
||||
// Client-side: revenue metric
|
||||
ldClient.track('purchase-completed', { orderId }, purchaseAmountUSD);
|
||||
```
|
||||
|
||||
**Key rules:**
|
||||
- **Match the existing context.** Don't construct a new context inline. Find where the codebase already builds its context/user object (used for `variation()` calls) and use the same one. This is how LD correlates the event to the right experiment participant.
|
||||
- **`metricValue` only for `value` metrics.** For `count` and `occurrence` metrics, omit `metricValue` entirely.
|
||||
- **Respect wrapper patterns.** If the codebase wraps LD calls behind a utility (`featureFlags.track()`, `analytics.ldTrack()`), add the new call through that wrapper — not by calling `ldClient` directly.
|
||||
- **Match the event key exactly.** `track()` event keys are case-sensitive. Use the exact string that the metric was created with.
|
||||
|
||||
See [SDK Track Patterns](references/sdk-track-patterns.md) for full per-language examples.
|
||||
|
||||
### Step 5: Verify
|
||||
|
||||
**Guide the user to trigger the action** in their local or staging environment. Then use `list-metric-events` to confirm the event key appears:
|
||||
|
||||
```
|
||||
list-metric-events(projectKey, environmentKey)
|
||||
```
|
||||
|
||||
**If the event key appears:** confirm success and show a summary.
|
||||
|
||||
**If the event key is absent after triggering**, work through this checklist:
|
||||
|
||||
| Problem | Check |
|
||||
|---------|-------|
|
||||
| Wrong event key casing | Does the `track()` call match the metric's event key exactly? |
|
||||
| SDK not initialized | Is `ldClient` initialized before the `track()` call runs? |
|
||||
| Server-side: wrong context | Is the context passed to `track()` the same context used for `variation()` calls? |
|
||||
| Client-side: no flag evaluation first | Has the SDK initialized and identified the user before `track()` is called? |
|
||||
| Wrong environment | Is `list-metric-events` querying the same environment where the action was triggered? |
|
||||
| Data delay | `list-metric-events` shows the last 90 days with up to ~5 min delay — try again in a moment |
|
||||
|
||||
Surface a summary once verified:
|
||||
|
||||
```
|
||||
✓ Event flowing: checkout-completed
|
||||
Seen in: production
|
||||
|
||||
Next: this event is now ready to back a metric. Use the metric-create skill to set one up,
|
||||
or attach an existing metric to your experiment.
|
||||
```
|
||||
|
||||
## Important Context
|
||||
|
||||
- **`track()` calls only count in experiments when a flag is evaluated first.** The event is correlated to an experiment participant because LD saw a `variation()` call from that context. If the user triggers the action without evaluating any flag, the event may still be ingested but won't appear in experiment results.
|
||||
- **Client-side SDKs flush events on an interval** (default ~30 seconds) or on page unload. In tests, you may need to call `ldClient.flush()` explicitly to see events appear immediately.
|
||||
- **Server-side SDKs also buffer events.** Calling `ldClient.flush()` after `track()` in development ensures the event is sent before the process exits or the test ends.
|
||||
- **`metricValue` units must match the metric definition.** If the metric was created with unit `ms`, pass milliseconds. Passing seconds into a milliseconds metric will produce silently wrong results.
|
||||
- **The `data` parameter is for custom metadata, not the metric value.** Pass extra context (order ID, category, etc.) in `data`. Pass the numeric measurement in `metricValue`.
|
||||
|
||||
## References
|
||||
|
||||
- [SDK Track Patterns](references/sdk-track-patterns.md) — `track()` call syntax, initialization, and package names for every supported SDK
|
||||
@@ -0,0 +1,340 @@
|
||||
# SDK Track Patterns
|
||||
|
||||
How to call `track()` in each LaunchDarkly SDK. Use this reference to match the patterns already in use in the codebase — and to add the right call when starting fresh.
|
||||
|
||||
The key distinction across all SDKs: **server-side SDKs require a context per call; client-side SDKs do not.**
|
||||
|
||||
---
|
||||
|
||||
## JavaScript / TypeScript — Node.js (Server-side)
|
||||
|
||||
**Package:** `@launchdarkly/node-server-sdk` (v9+) or `launchdarkly-node-server-sdk` (v6–v8)
|
||||
|
||||
```bash
|
||||
npm install @launchdarkly/node-server-sdk
|
||||
```
|
||||
|
||||
```typescript
|
||||
import * as ld from '@launchdarkly/node-server-sdk';
|
||||
|
||||
const client = ld.init(process.env.LD_SDK_KEY!);
|
||||
await client.waitForInitialization();
|
||||
|
||||
// Count / occurrence metric (no metricValue)
|
||||
client.track('checkout-completed', context);
|
||||
|
||||
// Value metric — pass the measurement as metricValue
|
||||
client.track('api-response-time', context, null, responseTimeMs);
|
||||
|
||||
// With custom data payload
|
||||
client.track('item-purchased', context, { itemId: 'abc123', category: 'apparel' }, purchaseAmount);
|
||||
|
||||
// Flush explicitly in tests / short-lived processes
|
||||
await client.flush();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## JavaScript / TypeScript — Browser (Client-side)
|
||||
|
||||
**Package:** `launchdarkly-js-client-sdk`
|
||||
|
||||
```bash
|
||||
npm install launchdarkly-js-client-sdk
|
||||
```
|
||||
|
||||
```typescript
|
||||
import * as ld from 'launchdarkly-js-client-sdk';
|
||||
|
||||
const client = ld.initialize(clientSideId, context);
|
||||
await client.waitForInitialization();
|
||||
|
||||
// Count / occurrence metric — no context, no metricValue
|
||||
client.track('signup-completed');
|
||||
|
||||
// Value metric
|
||||
client.track('page-load-time', null, performanceMs);
|
||||
|
||||
// With custom data
|
||||
client.track('item-added-to-cart', { itemId: 'abc123' }, itemPrice);
|
||||
|
||||
// Flush (useful in tests or before navigating away)
|
||||
await client.flush();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## React (Client-side)
|
||||
|
||||
**Package:** `launchdarkly-react-client-sdk`
|
||||
|
||||
```bash
|
||||
npm install launchdarkly-react-client-sdk
|
||||
```
|
||||
|
||||
```tsx
|
||||
import { useLDClient } from 'launchdarkly-react-client-sdk';
|
||||
|
||||
function CheckoutButton() {
|
||||
const ldClient = useLDClient();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await processCheckout();
|
||||
|
||||
// Count / occurrence metric
|
||||
ldClient?.track('checkout-completed');
|
||||
|
||||
// Value metric
|
||||
ldClient?.track('checkout-revenue', null, orderTotal);
|
||||
};
|
||||
|
||||
return <button onClick={handleSubmit}>Complete Order</button>;
|
||||
}
|
||||
```
|
||||
|
||||
**Initialization** (typically in the app root via `LDProvider` or `asyncWithLDProvider`):
|
||||
|
||||
```tsx
|
||||
import { LDProvider } from 'launchdarkly-react-client-sdk';
|
||||
|
||||
// Wrap your app — context is set here, not in each track() call
|
||||
<LDProvider clientSideID={clientSideId} context={userContext}>
|
||||
<App />
|
||||
</LDProvider>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Python (Server-side)
|
||||
|
||||
**Package:** `launchdarkly-server-sdk`
|
||||
|
||||
```bash
|
||||
pip install launchdarkly-server-sdk
|
||||
```
|
||||
|
||||
```python
|
||||
import ldclient
|
||||
from ldclient.config import Config
|
||||
|
||||
ldclient.set_config(Config(sdk_key))
|
||||
client = ldclient.get()
|
||||
|
||||
# Count / occurrence metric
|
||||
client.track('checkout-completed', context)
|
||||
|
||||
# Value metric
|
||||
client.track('api-response-time', context, None, response_time_ms)
|
||||
|
||||
# With data payload
|
||||
client.track('item-purchased', context, {'item_id': 'abc123'}, purchase_amount)
|
||||
|
||||
# Flush
|
||||
client.flush()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Go (Server-side)
|
||||
|
||||
**Package:** `github.com/launchdarkly/go-server-sdk/v7`
|
||||
|
||||
```bash
|
||||
go get github.com/launchdarkly/go-server-sdk/v7
|
||||
```
|
||||
|
||||
```go
|
||||
import (
|
||||
ld "github.com/launchdarkly/go-server-sdk/v7"
|
||||
"github.com/launchdarkly/go-server-sdk/v7/ldcontext"
|
||||
)
|
||||
|
||||
client, _ := ld.MakeClient(sdkKey, 5*time.Second)
|
||||
defer client.Close()
|
||||
|
||||
// Count / occurrence metric
|
||||
client.TrackEvent("checkout-completed", context)
|
||||
|
||||
// Value metric
|
||||
client.TrackMetric("api-response-time", context, responseTimeMs, nil)
|
||||
|
||||
// With data payload
|
||||
data := ldvalue.BuildObject().Set("itemId", ldvalue.String("abc123")).Build()
|
||||
client.TrackData("item-purchased", context, data)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Java (Server-side)
|
||||
|
||||
**Package:** `com.launchdarkly:launchdarkly-java-server-sdk`
|
||||
|
||||
```xml
|
||||
<!-- Maven -->
|
||||
<dependency>
|
||||
<groupId>com.launchdarkly</groupId>
|
||||
<artifactId>launchdarkly-java-server-sdk</artifactId>
|
||||
<version>7.x.x</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
```java
|
||||
LDClient client = new LDClient(sdkKey);
|
||||
|
||||
// Count / occurrence metric
|
||||
client.track("checkout-completed", context);
|
||||
|
||||
// Value metric
|
||||
client.trackMetric("api-response-time", context, responseTimeMs);
|
||||
|
||||
// With data payload
|
||||
LDValue data = LDValue.buildObject().put("itemId", "abc123").build();
|
||||
client.trackData("item-purchased", context, data);
|
||||
client.trackMetric("item-purchased", context, purchaseAmount);
|
||||
|
||||
// Flush
|
||||
client.flush();
|
||||
client.close();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ruby (Server-side)
|
||||
|
||||
**Package:** `launchdarkly-server-sdk`
|
||||
|
||||
```bash
|
||||
gem install launchdarkly-server-sdk
|
||||
```
|
||||
|
||||
```ruby
|
||||
require 'ldclient-rb'
|
||||
|
||||
client = LaunchDarkly::LDClient.new(sdk_key)
|
||||
|
||||
# Count / occurrence metric
|
||||
client.track('checkout-completed', context)
|
||||
|
||||
# Value metric
|
||||
client.track('api-response-time', context, nil, response_time_ms)
|
||||
|
||||
# With data payload
|
||||
client.track('item-purchased', context, { item_id: 'abc123' }, purchase_amount)
|
||||
|
||||
# Flush
|
||||
client.flush
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## .NET / C# (Server-side)
|
||||
|
||||
**Package:** `LaunchDarkly.ServerSdk`
|
||||
|
||||
```bash
|
||||
dotnet add package LaunchDarkly.ServerSdk
|
||||
```
|
||||
|
||||
```csharp
|
||||
using LaunchDarkly.Sdk;
|
||||
using LaunchDarkly.Sdk.Server;
|
||||
|
||||
var client = new LdClient(sdkKey);
|
||||
|
||||
// Count / occurrence metric
|
||||
client.Track("checkout-completed", context);
|
||||
|
||||
// Value metric
|
||||
client.Track("api-response-time", context, LdValue.Null, responseTimeMs);
|
||||
|
||||
// With data payload
|
||||
var data = LdValue.BuildObject().Add("itemId", "abc123").Build();
|
||||
client.Track("item-purchased", context, data, purchaseAmount);
|
||||
|
||||
// Flush
|
||||
client.Flush();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## iOS / Swift (Client-side)
|
||||
|
||||
**Package:** `LaunchDarkly` via Swift Package Manager or CocoaPods
|
||||
|
||||
```swift
|
||||
import LaunchDarkly
|
||||
|
||||
// Context set during LDClient.start() — not required per track call
|
||||
|
||||
// Count / occurrence metric
|
||||
LDClient.get()!.trackEvent(key: "checkout-completed")
|
||||
|
||||
// Value metric
|
||||
LDClient.get()!.trackEvent(key: "api-response-time", metricValue: responseTimeMs)
|
||||
|
||||
// With data payload
|
||||
LDClient.get()!.trackEvent(key: "item-purchased", data: ["itemId": "abc123"], metricValue: purchaseAmount)
|
||||
|
||||
// Flush
|
||||
LDClient.get()!.flush()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Android / Kotlin (Client-side)
|
||||
|
||||
**Package:** `com.launchdarkly:launchdarkly-android-client-sdk`
|
||||
|
||||
```kotlin
|
||||
// Context set during LDClient.init() — not required per track call
|
||||
val client = LDClient.get()
|
||||
|
||||
// Count / occurrence metric
|
||||
client.trackEvent("checkout-completed")
|
||||
|
||||
// Value metric
|
||||
client.trackMetric("api-response-time", null, responseTimeMs)
|
||||
|
||||
// With data payload
|
||||
val data = LDValue.buildObject().put("itemId", "abc123").build()
|
||||
client.trackData("item-purchased", data, purchaseAmount)
|
||||
|
||||
// Flush
|
||||
client.flush()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Wrapper Patterns
|
||||
|
||||
Many teams abstract LaunchDarkly calls behind a service or utility. Search for these patterns before adding a raw `ldClient.track()` call:
|
||||
|
||||
```typescript
|
||||
// Service wrappers
|
||||
analyticsService.track('event-key');
|
||||
featureFlagService.trackEvent('event-key', context);
|
||||
tracking.record('event-key', metricValue);
|
||||
|
||||
// Module-level wrappers
|
||||
import { trackEvent } from '../lib/launchdarkly';
|
||||
trackEvent('event-key', context, metricValue);
|
||||
|
||||
// Class-based
|
||||
this.ldService.track('event-key');
|
||||
LDWrapper.getInstance().track('event-key');
|
||||
```
|
||||
|
||||
If any of these exist, add the new call through the same wrapper — don't bypass it.
|
||||
|
||||
---
|
||||
|
||||
## `metricValue` Quick Reference
|
||||
|
||||
| Metric type | Pass `metricValue`? | Example |
|
||||
|-------------|---------------------|---------|
|
||||
| `count` | No — omit it | `client.track('button-clicked', context)` |
|
||||
| `occurrence` | No — omit it | `client.track('signup-completed', context)` |
|
||||
| `value` (average) | Yes — the measurement | `client.track('page-load', context, null, 342)` |
|
||||
| `value` (sum) | Yes — the per-event amount | `client.track('revenue', context, null, 49.99)` |
|
||||
|
||||
The `data` parameter (before `metricValue`) is for arbitrary metadata — order IDs, category names, etc. It does not affect metric calculations.
|
||||
Reference in New Issue
Block a user