Skip to main content
This page covers instrumenting a Swift application with the OpenTelemetry SDK to send logs and traces to Bronto over OTLP/HTTP via a local OTel Collector.
If you don’t have a Collector and want to export directly from your application to Bronto, see Direct export to Bronto at the bottom of this page.

Prerequisites

Install dependencies

Add the opentelemetry-swift package to your Package.swift:
Package.swift
// swift-tools-version: 5.7
import PackageDescription

let package = Package(
    name: "MyApp",
    dependencies: [
        .package(
            url: "https://github.com/open-telemetry/opentelemetry-swift",
            from: "1.10.0"
        ),
    ],
    targets: [
        .executableTarget(
            name: "MyApp",
            dependencies: [
                .product(name: "OpenTelemetryApi",  package: "opentelemetry-swift"),
                .product(name: "OpenTelemetrySdk",  package: "opentelemetry-swift"),
                .product(name: "OtlpHttpExporter",  package: "opentelemetry-swift"),
                .product(name: "ResourceExtension", package: "opentelemetry-swift"),
            ]
        ),
    ]
)
ProductPurpose
OpenTelemetryApiCore OTel API
OpenTelemetrySdkSDK — LoggerProvider, processors
OtlpHttpExporterOTLP/HTTP exporter
ResourceExtensionHelpers for building Resource

Configure the log bridge

Set up a LoggerProvider with an OTLP exporter and register it as the global OTel logs provider.
OtelLogging.swift
import Foundation
import OpenTelemetryApi
import OpenTelemetrySdk
import OtlpHttpExporter
import ResourceExtension

func configureOtelLogging() {
    let resource = DefaultResources().get()
        .merging(other: Resource(attributes: [
            ResourceAttributes.serviceName.rawValue:
                AttributeValue.string("my-service"),
            ResourceAttributes.serviceNamespace.rawValue:
                AttributeValue.string("my-team"),
            "deployment.environment":
                AttributeValue.string("production"),
        ]))

    let exporterEndpoint = URL(string: "http://localhost:4318/v1/logs")!
    let exporter = OtlpHttpLogExporter(endpoint: exporterEndpoint)

    let processor = BatchLogRecordProcessor(
        logRecordExporter: exporter,
        scheduleDelay: 5.0
    )

    let loggerProvider = LoggerProviderBuilder()
        .with(resource: resource)
        .with(processors: [processor])
        .build()

    OpenTelemetry.registerLoggerProvider(loggerProvider: loggerProvider)
}
Call configureOtelLogging() once at application startup, before the first log emission.

Configure the OTLP exporter

The exporterEndpoint in the snippet above connects to the OTel Collector on localhost:4318. If your Collector runs on a different host or port, update the URL accordingly.

Set resource attributes

Resource attributes are attached to every log record exported from this process. Two attributes drive how Bronto organises incoming logs:
OTel attributeBronto conceptDescription
service.nameDatasetGroups logs from one service
service.namespaceCollectionGroups related services or a team’s services
These are set via Resource(attributes:) in the setup above.

Complete example

main.swift
import OpenTelemetryApi

configureOtelLogging()

let logger = OpenTelemetry.instance.loggerProvider
    .loggerBuilder(instrumentationScopeName: "my-service")
    .build()

var record = ReadableLogRecord()
record.body = AttributeValue.string("Application started")
record.severity = .info
logger.emit(logRecord: record)

var warnRecord = ReadableLogRecord()
warnRecord.body = AttributeValue.string("Low disk space")
warnRecord.severity = .warn
warnRecord.attributes = ["disk_free_gb": AttributeValue.double(2.1)]
logger.emit(logRecord: warnRecord)
A direct bridge to Apple’s os.Logger (Unified Logging) is not yet available in the official SDK. The example above emits log records via the OTel Logs API directly. For apps targeting Apple platforms, you can emit to both os.Logger and OTel from a thin wrapper function.

Verify log collection

After running your application, open the Search page in Bronto. Filter by the dataset name you set in service.name — your log records should appear within a few seconds. If no logs appear, check:
  • The OTel Collector is running and reachable at the configured endpoint.
  • The Collector’s pipeline includes a logs pipeline with an otlp receiver and the Bronto exporter — see Connect Open Telemetry to Bronto.
  • configureOtelLogging() is called before the first log emission.
  • The BatchLogRecordProcessor exports on a background timer — ensure your process does not exit before the first flush interval.

Traces

Auto-instrumentation for Swift is currently limited. Check the OpenTelemetry Swift registry for available community instrumentation packages. For URLSession and networking, community wrappers exist that create spans automatically.

Install tracing dependencies

Add StdoutExporter or OtlpHttpTraceExporter from the same opentelemetry-swift package — no additional Swift Package Manager entries are needed. Add OpenTracingShim if you need OpenTracing compatibility:
Package.swift
.product(name: "OpenTelemetrySdk",   package: "opentelemetry-swift"),
.product(name: "OtlpHttpExporter",   package: "opentelemetry-swift"),

Configure the tracer provider

OtelTracing.swift
import OpenTelemetryApi
import OpenTelemetrySdk
import OtlpHttpExporter

func configureOtelTracing(resource: Resource) {
    let traceEndpoint = URL(string: "http://localhost:4318/v1/traces")!
    let traceExporter = OtlpHttpTraceExporter(endpoint: traceEndpoint)

    let spanProcessor = BatchSpanProcessor(spanExporter: traceExporter)

    let tracerProvider = TracerProviderBuilder()
        .with(resource: resource)
        .with(processors: [spanProcessor])
        .build()

    OpenTelemetry.registerTracerProvider(tracerProvider: tracerProvider)
}
Pass the same resource used for logging so both signals share service.name and service.namespace.

Creating spans

let tracer = OpenTelemetry.instance.tracerProvider
    .get(instrumentationName: "my-service", instrumentationVersion: nil)

let span = tracer.spanBuilder(spanName: "process-payment").startSpan()
span.setAttribute(key: "payment.amount", value: AttributeValue.double(99.99))
// your code here
span.end()

Direct export to Bronto

If you are not using an OTel Collector, export directly to Bronto by replacing the exporter configurations with the Bronto OTLP endpoints and your API key:
let config = OtlpConfiguration(
    timeout: TimeInterval(10),
    headers: [("x-bronto-api-key", "<YOUR_API_KEY>")]
)

let logExporter = OtlpHttpLogExporter(
    endpoint: URL(string: "https://ingestion.eu.bronto.io/v1/logs")!, // or ingestion.us.bronto.io
    config: config
)

let traceExporter = OtlpHttpTraceExporter(
    endpoint: URL(string: "https://ingestion.eu.bronto.io/v1/traces")!, // or ingestion.us.bronto.io
    config: config
)
RegionLogs endpointTraces endpoint
EUhttps://ingestion.eu.bronto.io/v1/logshttps://ingestion.eu.bronto.io/v1/traces
UShttps://ingestion.us.bronto.io/v1/logshttps://ingestion.us.bronto.io/v1/traces
See API Keys for how to create a key with ingestion permissions. No other changes to the rest of the setup are required.