Skip to content

Commit 11d73cc

Browse files
committed
Make AppMetrics dependency based
1 parent 12177f4 commit 11d73cc

File tree

6 files changed

+71
-23
lines changed

6 files changed

+71
-23
lines changed

Sources/App/Core/AppMetrics.swift

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,6 @@ import Vapor
2121

2222
enum AppMetrics {
2323

24-
static let initialized = Mutex(false)
25-
26-
static func bootstrap() {
27-
// prevent tests from boostrapping multiple times
28-
guard !initialized.withLock({ $0 }) else { return }
29-
initialized.withLock {
30-
let client = PrometheusClient()
31-
MetricsSystem.bootstrap(PrometheusMetricsFactory(client: client))
32-
$0 = true
33-
}
34-
}
35-
36-
// metrics
37-
3824
static var analyzeCandidatesCount: PromGauge<Int>? {
3925
gauge("spi_analyze_candidates_count")
4026
}
@@ -155,13 +141,13 @@ enum AppMetrics {
155141
extension AppMetrics {
156142

157143
static func counter<V: Numeric>(_ name: String) -> PromCounter<V>? {
158-
try? MetricsSystem.prometheus()
159-
.createCounter(forType: V.self, named: name)
144+
@Dependency(\.prometheus) var prometheus
145+
return prometheus?.createCounter(forType: V.self, named: name)
160146
}
161147

162148
static func gauge<V: DoubleRepresentable>(_ name: String) -> PromGauge<V>? {
163-
try? MetricsSystem.prometheus()
164-
.createGauge(forType: V.self, named: name)
149+
@Dependency(\.prometheus) var prometheus
150+
return prometheus?.createGauge(forType: V.self, named: name)
165151
}
166152

167153
}
@@ -176,14 +162,19 @@ extension AppMetrics {
176162
static func push(client: Client, jobName: String) async throws {
177163
@Dependency(\.environment) var environment
178164
@Dependency(\.logger) var logger
165+
@Dependency(\.prometheus) var prometheus
166+
167+
guard let prometheus else {
168+
throw AppError.genericError(nil, "Prometheus client unavailable (nil)")
169+
}
179170

180171
guard let pushGatewayUrl = environment.metricsPushGatewayUrl() else {
181172
throw AppError.envVariableNotSet("METRICS_PUSHGATEWAY_URL")
182173
}
183174
let url = URI(string: "\(pushGatewayUrl)/metrics/job/\(jobName)")
184175

185176
do {
186-
let metrics: String = try await MetricsSystem.prometheus().collect()
177+
let metrics: String = await prometheus.collect()
187178
_ = try await client.post(url) { req in
188179
// append "\n" to avoid
189180
// text format parsing error in line 4: unexpected end of input stream
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright Dave Verwer, Sven A. Schmidt, and other contributors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Dependencies
16+
import Metrics
17+
import Synchronization
18+
@preconcurrency import Prometheus
19+
20+
21+
enum AppMetricsClient {
22+
private static let initialized = Mutex(false)
23+
24+
static func bootstrap() {
25+
guard !initialized.withLock({ $0 }) else { return }
26+
initialized.withLock {
27+
let client = PrometheusClient()
28+
MetricsSystem.bootstrap(PrometheusMetricsFactory(client: client))
29+
$0 = true
30+
}
31+
}
32+
}
33+
34+
35+
extension AppMetricsClient: DependencyKey {
36+
static var liveValue: PrometheusClient? {
37+
try? MetricsSystem.prometheus()
38+
}
39+
}
40+
41+
42+
extension AppMetricsClient: TestDependencyKey {
43+
static var testValue: PrometheusClient? {
44+
unimplemented("testValue"); return nil
45+
}
46+
}
47+
48+
49+
extension DependencyValues {
50+
public var prometheus: PrometheusClient? {
51+
get { self[AppMetricsClient.self] }
52+
set { self[AppMetricsClient.self] = newValue }
53+
}
54+
}

Sources/App/configure.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ public func configure(_ app: Application, databasePort: Int? = nil) async throws
365365
try routes(app)
366366

367367
// bootstrap app metrics
368-
AppMetrics.bootstrap()
368+
AppMetricsClient.bootstrap()
369369

370370
return host
371371
}

Sources/App/routes.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,8 @@ func routes(_ app: Application) throws {
211211

212212
do { // Metrics
213213
app.get("metrics") { req -> String in
214-
try await MetricsSystem.prometheus().collect()
214+
@Dependency(\.prometheus) var prometheus
215+
return await prometheus?.collect() ?? ""
215216
}
216217
}
217218
}

Tests/AppTests/AllTests.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ import Testing
1616
import Dependencies
1717

1818

19-
@Suite(.dependency(\.date.now, .t0)) struct AllTests { }
19+
@Suite(
20+
.dependency(\.date.now, .t0),
21+
.dependency(\.prometheus, .init())
22+
) struct AllTests { }
2023

2124

2225
extension AllTests {

Tests/AppTests/MetricsTests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ extension AllTests.MetricsTests {
164164

165165
// validation
166166
#expect((AppMetrics.buildTriggerDurationSeconds?.get()) ?? 0 > 0)
167-
print(AppMetrics.buildTriggerDurationSeconds!.get())
168167
}
169168
}
170169
}

0 commit comments

Comments
 (0)