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

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]? = nil
)

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

Metric definitions must exist on the server before the SDK emits events for a given slug. Create them through the CLI or the dashboard. Events for undefined slugs will still be ingested but will not appear in metric queries until the definition is created.

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?

Install the CLI and let your agent handle the rest.