Metrics
Track performance with startOperation() and recordMetric() in the Swift SDK.
Structured metrics give you aggregated statistics -- p50, p95, p99 latencies, success/failure rates, and trends over time. Use them when you need more than a list of individual events. For a broader overview, see Metrics.
The SDK supports two patterns: lifecycle operations for things with a duration, and single-shot measurements for point-in-time values.
Lifecycle Operations
Use Owl.startOperation() when you are measuring something that has a start and an end, such as a network request, file upload, or data processing task. The SDK tracks duration automatically.
let op = Owl.startOperation("photo-upload", attributes: ["format": "heic"])
do {
let result = try await uploadPhoto(image)
op.complete(attributes: ["size_bytes": "\(result.byteCount)"])
} catch {
op.fail(error: error.localizedDescription)
}Starting an Operation
Owl.startOperation() returns an OwlOperation object and immediately emits a metric:<slug>:start event. The operation records its start time and a unique tracking_id (UUID) that correlates all phases together.
public static func startOperation(
_ metric: String,
attributes: [String: String?] = [:]
) -> OwlOperationOptional values are accepted directly — nil-valued keys are dropped before the event ships, so you can pass String? from your domain code without unwrapping first.
Completing an Operation
Call one of three methods on the OwlOperation to finish it:
// Success -- emits metric:<slug>:complete (info level)
op.complete(attributes: ["output_format": "jpeg"])
// Failure -- emits metric:<slug>:fail (error level)
op.fail(error: "timeout", attributes: ["retry_count": "3"])
// Cancellation -- emits metric:<slug>:cancel (info level)
op.cancel(attributes: ["reason": "user_cancelled"])Each completion method automatically includes:
| Attribute | Description |
|---|---|
tracking_id | UUID linking this phase to the corresponding start event. |
duration_ms | Milliseconds elapsed since startOperation() was called. |
error | Error description (only on fail()). |
Full Example
func syncUserData() async {
let op = Owl.startOperation("data-sync", attributes: ["source": "cloud"])
do {
let records = try await fetchRecords()
try await mergeRecords(records)
op.complete(attributes: ["record_count": "\(records.count)"])
} catch is CancellationError {
op.cancel()
} catch {
op.fail(error: error.localizedDescription)
}
}Single-Shot Measurements
Use Owl.recordMetric() for values that do not have a duration -- a point-in-time measurement or a count.
Owl.recordMetric("items-in-cart", attributes: ["count": "\(cart.items.count)"])
Owl.recordMetric("app-cold-start", attributes: ["screen": "home"])This emits a single metric:<slug>:record event at info level.
public static func recordMetric(
_ metric: String,
attributes: [String: String?] = [:]
)Slug Rules
Metric slugs must contain only lowercase letters, numbers, and hyphens (matching the pattern ^[a-z0-9-]+$). Examples: photo-upload, data-sync, api-request.
If you pass an invalid slug, the SDK auto-corrects it by lowercasing, replacing invalid characters with hyphens, and collapsing consecutive hyphens. A warning is logged to the console when this happens.
// "Photo Upload" is auto-corrected to "photo-upload" with a console warning
let op = Owl.startOperation("Photo Upload")Server-Side Metric Definitions
You do not need to create a metric definition before emitting events — the server stores metric events by slug regardless, and they appear in metric queries immediately. Defining a metric in the dashboard or via the CLI gives you a human-readable name, description, and documentation alongside the aggregated data.
When to Use Metrics vs Events
| Use case | Approach |
|---|---|
| You want aggregated stats (p50, p95, error rates) | Structured metrics |
| You want a timeline of individual occurrences | Plain events (Owl.info(), Owl.error()) |
| You need to measure how long something takes | Lifecycle operation (startOperation) |
| You want to record a single value at a point in time | Single-shot (recordMetric) |
