> ## Documentation Index
> Fetch the complete documentation index at: https://docs.squasher.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Design Queryable Logs

> Emit one canonical operation event with stable fields, trace correlation, typed attributes, and outcome-aware sampling.

Good logs answer a question without a text parser. Emit one final event for each important operation. Put the result, duration, and business context on that event.

## Use one canonical operation event

Do not emit `started`, `processing`, and `completed` logs for routine work. Keep child work as spans. Keep counters and durations as metrics. Emit one final operation event in a `finally` block.

```typescript theme={null}
await captureTelemetry({
  event_name: "checkout.completed",
  kind: "log",
  level: status >= 500 ? "error" : "info",
  message: "checkout completed",
  attributes: {
    "checkout.currency": "USD",
    "checkout.item_count": 3,
    "http.response.status_code": status,
    "squasher.operation": "checkout",
    "squasher.outcome": status >= 400 ? "failure" : "success",
  },
  measurements: [
    { name: "checkout.count", value: 1, unit: "1" },
    { name: "checkout.duration_ms", value: durationMs, unit: "ms" },
  ],
  trace: {
    trace_id: traceId,
    span_id: spanId,
    span_name: "checkout",
    span_kind: "server",
    status: status >= 500 ? "error" : "ok",
    duration_ms: durationMs,
  },
});
```

The Squasher SDK converts this event to a correlated log, span, and measurement set. You do not need three application calls for the same operation.

## Use stable field names

| Field                       | Purpose                                         |
| --------------------------- | ----------------------------------------------- |
| `event_name`                | Stable name, such as `checkout.completed`       |
| `service.name`              | Service that owns the work                      |
| `deployment.environment`    | Production, development, or another environment |
| `service.version`           | Release or commit identifier                    |
| `squasher.operation`        | Stable operation or route name                  |
| `squasher.outcome`          | `success` or `failure`                          |
| `http.route`                | Route template, not a raw URL with IDs          |
| `http.response.status_code` | Numeric status code                             |
| `trace_id` and `span_id`    | Correlation through the `trace` object          |

Use low-cardinality fields such as route, region, provider, result, and release for breakdowns. Use high-cardinality fields such as request, user, order, or monitor IDs only for direct filtering. Do not group large time windows by a unique ID.

## Keep attribute types

Send numbers and booleans as numbers and booleans. The JavaScript SDKs flatten safe nested objects to dotted keys.

```typescript theme={null}
log.info("cache lookup completed", {
  cache: { hit: true, age_ms: 42 },
  result_count: 8,
});
```

This produces `cache.hit=true`, `cache.age_ms=42`, and `result_count=8`. Sensitive attribute names, such as passwords, tokens, cookies, and authorization values, are redacted. Attribute depth, count, and string length are bounded.

## Sample by outcome

Use deterministic outcome-aware sampling. Keep failures and slow operations. Sample routine success. Squasher keeps operation measurements when it samples out the related log and trace, so counts and latency data stay exact.

```typescript theme={null}
init({
  apiKey: process.env.SQUASHER_API_KEY!,
  projectId: process.env.SQUASHER_PROJECT_ID!,
  sampling: {
    successRate: 0.1,
    errorRate: 1,
    slowRate: 1,
    slowThresholdMs: 1000,
    alwaysSampleReleases: [process.env.GIT_SHA!],
  },
});
```

The SDK uses a stable trace, session, user, or event key. Rate-based decisions with the same key and outcome class are identical; a user or release allowlist can override the rate. Outcome classes use separate rates, so the SDK can keep a failure or slow event after it drops routine work from the same trace. This is event sampling, not whole-trace tail sampling. Use [Collector tail sampling](/integrations/tail-sampling) when every span in a trace must receive one decision. Debug mode keeps all events so you can verify instrumentation.

## Protect private data

* Do not record credentials, session cookies, raw authorization headers, prompts, or payment data.
* Record route templates instead of raw URLs with customer identifiers.
* Put an internal ID in an attribute only when an operator must find one exact operation.
* Use `beforeSend` for product-specific redaction and allowlists.

## Verify quality

For each service, check that operation events have service, environment, release, route or operation, outcome, status, duration, and trace correlation. Also check for orphan logs, duplicate final events, and attribute type changes.

Use the [Logs API](/api-reference/logs) for narrow searches and the Logs **Patterns** and **Changes** views for bounded comparisons.
Run `squasher observe quality --project <project_id> --service-name <service>`
or use the [Telemetry Quality API](/api-reference/telemetry-quality) for a
content-free, bounded report of these checks.
