SigNoz Cloud - This page is relevant for SigNoz Cloud editions.
Self-Host - This page is relevant for self-hosted SigNoz editions.

Swift OpenTelemetry Instrumentation

This document contains instructions on how to set up OpenTelemetry instrumentation in your Swift applications and view your application traces in SigNoz.

Prerequisites

Setup

Step 1: Install dependencies

To configure your Swift application to send traces directly to SigNoz Cloud, install opentelemetry-swift and grpc-swift as dependencies.

Add the following to your Package.swift file:

platforms: [
    .macOS(.v12)
],
dependencies: [
    .package(url: "https://github.com/open-telemetry/opentelemetry-swift-core.git", .upToNextMajor(from: "2.3.0")),
    .package(url: "https://github.com/open-telemetry/opentelemetry-swift.git", .upToNextMajor(from: "2.3.0")),
    .package(url: "https://github.com/grpc/grpc-swift", from: "1.15.0"),
],
targets: [
    .executableTarget(
        name: "<service_name>",
        dependencies: [
            .product(name: "OpenTelemetryApi", package: "opentelemetry-swift-core"),
            .product(name: "OpenTelemetrySdk", package: "opentelemetry-swift-core"),
            .product(name: "OpenTelemetryProtocolExporter", package: "opentelemetry-swift"),
            .product(name: "GRPC", package: "grpc-swift")
        ],
        path: "."
    )
]

Verify these values:

  • <service_name>: The name of your service.

Step 2: Initialize the Tracer

Import the necessary OpenTelemetry modules and initialize the tracer. Insert the following code snippet into your main.swift file:

import Foundation
import GRPC
import NIO
import NIOSSL
@preconcurrency import OpenTelemetryApi
import OpenTelemetryProtocolExporterCommon
import OpenTelemetryProtocolExporterGrpc
import OpenTelemetrySdk

@main
struct SwiftExample {
    static func main() async throws {
        let instrumentationScopeName = "SwiftExample"
        let instrumentationScopeVersion = "semver:0.1.0"

        // Replace <your-ingestion-key> with your SigNoz Ingestion Key
        let otlpConfiguration = OtlpConfiguration(timeout: TimeInterval(10), headers: [("signoz-ingestion-key", "<your-ingestion-key>")])

        // Replace <region> with the region of your SigNoz Cloud instance
        let grpcChannel = ClientConnection.usingPlatformAppropriateTLS(
            for: MultiThreadedEventLoopGroup(numberOfThreads: 1)
        ).connect(host: "ingest.<region>.signoz.cloud", port: 443)

        let otlpTraceExporter = OtlpTraceExporter(channel: grpcChannel, config: otlpConfiguration)
        let spanProcessor = BatchSpanProcessor(spanExporter: otlpTraceExporter)

        // Replace <service_name> with the name of your service
        let resource = Resource(attributes: [
            "service.name": AttributeValue.string("<service_name>")
        ])

        let tracerProvider = TracerProviderBuilder()
            .add(spanProcessor: spanProcessor)
            .with(resource: resource)
            .build()
        OpenTelemetry.registerTracerProvider(tracerProvider: tracerProvider)

        let tracer = OpenTelemetry.instance.tracerProvider.get(instrumentationName: instrumentationScopeName, instrumentationVersion: instrumentationScopeVersion)

Verify these values:

  • <region>: Your SigNoz Cloud region
  • <your-ingestion-key>: Your SigNoz ingestion key
  • <service_name>: A name to identify your service in SigNoz (e.g., my-swift-app)

Step 3: Instrument your application

OpenTelemetry Swift does not support automatic instrumentation. You must manually create spans for every operation you want to trace.

To send telemetry data to SigNoz, wrap operations in spans. Each span represents a unit of work and can carry attributes (key-value metadata) for context.

Create a basic span

Use the tracer's spanBuilder to start a span, optionally set its kind and attributes, then call .end() when the work is done:

        func doWork() {
            let span = tracer.spanBuilder(spanName: "doWork")
                .setSpanKind(spanKind: .client)
                .startSpan()
            // Add key-value attributes to provide context
            span.setAttribute(key: "operation.type", value: "example")
            span.setAttribute(key: "operation.id", value: 42)

            // ... perform work here ...

            span.end()
        }

Nest spans for detailed traces

Set a parent span as the active span using OpenTelemetry.instance.contextProvider so that any child spans created within its scope are automatically linked:

        func handleRequest() {
            let parentSpan = tracer.spanBuilder(spanName: "handleRequest")
                .setSpanKind(spanKind: .server)
                .startSpan()
            OpenTelemetry.instance.contextProvider.setActiveSpan(parentSpan)

            // This child span is automatically linked to parentSpan
            fetchData()

            parentSpan.end()
        }

        func fetchData() {
            let childSpan = tracer.spanBuilder(spanName: "fetchData")
                .setSpanKind(spanKind: .client)
                .startSpan()
            childSpan.setAttribute(key: "db.system", value: "sqlite")

            // ... fetch data ...

            childSpan.end()
        }

Record errors

Mark a span's status as .error and record the exception when something fails:

        func riskyOperation() {
            let span = tracer.spanBuilder(spanName: "riskyOperation").startSpan()
            do {
                // ... work that might throw ...
            } catch {
                span.status = .error(description: error.localizedDescription)
            }
            span.end()
        }

Once your spans are in place, flush and run the application:

        // Call the function to generate a trace
        handleRequest()

        // Flush all pending spans before the process exits
        tracerProvider.forceFlush(timeout: 15)

Step 4: Run the application

Execute your application by running the following command in your terminal:

swift run

If the application exits without errors, the traces have been sent to SigNoz. Navigate to the Traces tab in SigNoz to verify.

Validate

To ensure everything is working:

  1. Perform an action in your application that triggers the traced code.
  2. In SigNoz, navigate to the Traces tab.
  3. Filter by your <service_name> (e.g. service.name IN my-swift-app) to find your traces.
  4. If you see your spans appearing in the list, the instrumentation is successfully sending data to SigNoz.

Troubleshooting

No data appearing in SigNoz

  • Invalid Credentials: Ensure that <your-ingestion-key> and ingest.<region>.signoz.cloud are correctly populated without the angle brackets.
  • Network Issues: Ensure your application environment has outbound network access to the SigNoz ingestion endpoint on port 443.
  • Initialization Order: Make sure the OpenTelemetry.registerTracerProvider() gets called before any spans are created.

Setup OpenTelemetry Collector (Optional)

What is the OpenTelemetry Collector?

Think of the OTel Collector as a middleman between your app and SigNoz. Instead of your application sending data directly to SigNoz, it sends everything to the Collector first, which then forwards it along.

Why use it?

  • Cleaning up data — Filter out noisy traces you don't care about, or remove sensitive info before it leaves your servers.
  • Keeping your app lightweight — Let the Collector handle batching, retries, and compression instead of your application code.
  • Adding context automatically — The Collector can tag your data with useful info like which Kubernetes pod or cloud region it came from.
  • Future flexibility — Want to send data to multiple backends later? The Collector makes that easy without changing your app.

See Switch from direct export to Collector for step-by-step instructions to convert your setup.

For more details, see Why use the OpenTelemetry Collector? and the Collector configuration guide.

Update connection logic

Change your tracer initialization to send data without TLS to localhost (or your collector host) on port 4317:

Use the same imports from Step 2, adding import NIOSSL. Only the connection and provider setup changes:

import Foundation
import GRPC
import NIO
import NIOSSL
@preconcurrency import OpenTelemetryApi
import OpenTelemetryProtocolExporterCommon
import OpenTelemetryProtocolExporterGrpc
import OpenTelemetrySdk

let otlpConfiguration = OtlpConfiguration(timeout: TimeInterval(10))

let configuration = ClientConnection.Configuration.default(
    target: .hostAndPort("localhost", 4317),
    eventLoopGroup: MultiThreadedEventLoopGroup(numberOfThreads: 1)
)
let grpcChannel = ClientConnection(configuration: configuration)

let otlpTraceExporter = OtlpTraceExporter(channel: grpcChannel, config: otlpConfiguration)
let spanProcessor = BatchSpanProcessor(spanExporter: otlpTraceExporter)

// Replace <service_name> with the name of your service
let resource = Resource(attributes: [
    "service.name": AttributeValue.string("<service_name>")
])

let tracerProvider = TracerProviderBuilder()
    .add(spanProcessor: spanProcessor)
    .with(resource: resource)
    .build()
OpenTelemetry.registerTracerProvider(tracerProvider: tracerProvider)

let tracer = OpenTelemetry.instance.tracerProvider.get(instrumentationName: instrumentationScopeName, instrumentationVersion: instrumentationScopeVersion)

Next Steps

  • View your Traces to find performance bottlenecks.
  • Create Custom Dashboards to monitor application health.
  • Set up Alerts to get notified when things go wrong.

Sample Swift Application:

Get Help

If you need help with the steps in this topic, please reach out to us on SigNoz Community Slack.

If you are a SigNoz Cloud user, please use in product chat support located at the bottom right corner of your SigNoz instance or contact us at cloud-support@signoz.io.

Last updated: March 16, 2026

Edit on GitHub