Owlmetry
SDKsSwift SDK

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?] = [:]
) -> OwlOperation

Optional 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:

AttributeDescription
tracking_idUUID linking this phase to the corresponding start event.
duration_msMilliseconds elapsed since startOperation() was called.
errorError 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 caseApproach
You want aggregated stats (p50, p95, error rates)Structured metrics
You want a timeline of individual occurrencesPlain events (Owl.info(), Owl.error())
You need to measure how long something takesLifecycle operation (startOperation)
You want to record a single value at a point in timeSingle-shot (recordMetric)

Ready to get started?

Connect your agent via MCP or CLI and start tracking.