Skip to content

Commit 8e6b382

Browse files
committed
Add open-telemetry example showcasing Distributed Tracing support
1 parent 42128b0 commit 8e6b382

File tree

4 files changed

+146
-2
lines changed

4 files changed

+146
-2
lines changed

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.DS_Store
2-
/.build
2+
.build
3+
.swiftpm
34
/Packages
45
xcuserdata/
56
DerivedData/
@@ -10,4 +11,4 @@ DerivedData/
1011
Package.resolved
1112
.benchmarkBaselines/
1213
.swift-version
13-
.docc-build
14+
.docc-build

Examples/open-telemetry/Package.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// swift-tools-version:6.1
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "open-telemetry",
6+
platforms: [.macOS(.v15)],
7+
products: [
8+
.executable(name: "example", targets: ["Example"])
9+
],
10+
dependencies: [
11+
// TODO: Change to remote once Distributed Tracing support was merged into main and/or tagged
12+
.package(path: "../../"),
13+
.package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"),
14+
.package(url: "https://github.com/apple/swift-distributed-tracing.git", from: "1.0.0"),
15+
.package(url: "https://github.com/swift-otel/swift-otel.git", exact: "1.0.0-alpha.1"),
16+
],
17+
targets: [
18+
.executableTarget(
19+
name: "Example",
20+
dependencies: [
21+
.product(name: "Valkey", package: "valkey-swift"),
22+
.product(name: "Hummingbird", package: "hummingbird"),
23+
.product(name: "Tracing", package: "swift-distributed-tracing"),
24+
.product(name: "OTel", package: "swift-otel"),
25+
]
26+
)
27+
],
28+
swiftLanguageModes: [.v6]
29+
)
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import Hummingbird
2+
import Logging
3+
import OTel
4+
import ServiceLifecycle
5+
import Tracing
6+
import Valkey
7+
8+
@main
9+
struct Example {
10+
static func main() async throws {
11+
let observability = try bootstrapObservability()
12+
let logger = Logger(label: "example")
13+
14+
let valkeyClient = ValkeyClient(
15+
.hostname("localhost"),
16+
logger: logger
17+
)
18+
19+
let router = Router()
20+
router.add(middleware: TracingMiddleware())
21+
router.add(middleware: LogRequestsMiddleware(.info))
22+
23+
router.get("/:x") { _, context in
24+
/*
25+
This demonstrates the span created for pipelined commands where all commands are of the same type.
26+
The `db.operation.name` indicates that it's multiple `EVAL` commands,
27+
and `db.operation.batch.size` indicates the number of commands.
28+
*/
29+
_ = await valkeyClient.execute(
30+
EVAL(script: "return '1'"),
31+
EVAL(script: "return '2'"),
32+
EVAL(script: "return '3'")
33+
)
34+
35+
/*
36+
This demonstrates the span created for pipelined commands where the commands are of different types.
37+
The `db.operation.name` resorts to `MULTI`, and `db.operation.batch.size` indicates the number of commands.
38+
*/
39+
_ = await valkeyClient.execute(
40+
EVAL(script: "return '1'"),
41+
ACL.WHOAMI()
42+
)
43+
44+
// This demonstrates the span created for a failed command.
45+
_ = try? await valkeyClient.execute(EVAL(script: "💩"))
46+
47+
// This demonstrates the span created for a failed pipelined command.
48+
_ = await valkeyClient.execute(
49+
EVAL(script: "return 'ok'"),
50+
EVAL(script: "💩")
51+
)
52+
53+
let x = try context.parameters.require("x", as: Int.self)
54+
55+
func expensiveAlgorithm(_ x: Int) async throws -> Int {
56+
try await withSpan("compute") { span in
57+
span.attributes["input"] = x
58+
try await Task.sleep(for: .seconds(3))
59+
return x * 2
60+
}
61+
}
62+
63+
if let cachedResult = try await valkeyClient.hget("values", field: "\(x)") {
64+
return cachedResult
65+
}
66+
67+
let result = try await expensiveAlgorithm(x)
68+
69+
try await valkeyClient.hset("values", data: [.init(field: "\(x)", value: "\(result)")])
70+
71+
return ByteBuffer(string: "\(result)")
72+
}
73+
74+
var app = Application(router: router)
75+
app.addServices(observability)
76+
app.addServices(valkeyClient)
77+
78+
try await app.runService()
79+
}
80+
81+
private static func bootstrapObservability() throws -> some Service {
82+
LoggingSystem.bootstrap(
83+
StreamLogHandler.standardOutput(label:metadataProvider:),
84+
metadataProvider: OTel.makeLoggingMetadataProvider()
85+
)
86+
87+
var configuration = OTel.Configuration.default
88+
configuration.serviceName = "example"
89+
90+
// For now, valkey-swift only supports Distributed Tracing so we disable the other signals.
91+
configuration.logs.enabled = false
92+
configuration.metrics.enabled = false
93+
94+
return try OTel.bootstrap(configuration: configuration)
95+
}
96+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
services:
2+
valkey:
3+
image: valkey/valkey:8.0
4+
ports:
5+
- 6379:6379
6+
healthcheck:
7+
test: ["CMD", "valkey-cli", "--raw", "incr", "ping"]
8+
volumes:
9+
- valkey_data:/data
10+
11+
jaeger:
12+
image: jaegertracing/all-in-one:latest
13+
ports:
14+
- 4318:4318 # OTLP/HTTP receiver
15+
- 16686:16686 # Jaeger Web UI
16+
17+
volumes:
18+
valkey_data:

0 commit comments

Comments
 (0)