Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions Sources/Prometheus/PrometheusMetrics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,21 @@ public struct PrometheusLabelSanitizer: LabelSanitizer {
}
}

/// Defines the base for a bridge between PrometheusClient and swift-metrics.
/// Used by `SwiftMetrics.prometheus()` to get an instance of `PromtheusClient` from `MetricsSystem`
///
/// Any custom implementation of `MetricsFactory` using `PrometheusClient` should conform to this implementation.
public protocol PrometheusWrappedMetricsFactory: MetricsFactory {
var client: PrometheusClient { get }
}

/// A bridge between PrometheusClient and swift-metrics. Prometheus types don't map perfectly on swift-metrics API,
/// which makes bridge implementation non trivial. This class defines how exactly swift-metrics types should be backed
/// with Prometheus types, e.g. how to sanitize labels, what buckets/quantiles to use for recorder/timer, etc.
public struct PrometheusMetricsFactory: MetricsFactory {
public struct PrometheusMetricsFactory: PrometheusWrappedMetricsFactory {

/// Prometheus client to bridge swift-metrics API to.
private let client: PrometheusClient
public let client: PrometheusClient

/// Bridge configuration.
private let configuration: Configuration
Expand Down Expand Up @@ -262,10 +270,10 @@ public extension MetricsSystem {
/// - Throws: `PrometheusError.PrometheusFactoryNotBootstrapped`
/// if no `PrometheusClient` was used to bootstrap `MetricsSystem`
static func prometheus() throws -> PrometheusClient {
guard let prom = self.factory as? PrometheusClient else {
guard let prom = self.factory as? PrometheusWrappedMetricsFactory else {
throw PrometheusError.prometheusFactoryNotBootstrapped(bootstrappedWith: "\(self.factory)")
}
return prom
return prom.client
}
}

Expand Down
7 changes: 7 additions & 0 deletions Tests/SwiftPrometheusTests/PrometheusMetricsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ final class PrometheusMetricsTests: XCTestCase {
self.prom = nil
try! self.group.syncShutdownGracefully()
}

func testGetPrometheus() {
MetricsSystem.bootstrapInternal(NOOPMetricsHandler.instance)
XCTAssertThrowsError(try MetricsSystem.prometheus())
MetricsSystem.bootstrapInternal(PrometheusMetricsFactory(client: self.prom))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I love that style but I guess this was introduced some time recently for some reason?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, see #46 and #44 for full context.

Using PrometheusClient directly limited some options :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, so it's for the configs etc. Thanks for the pointer; perhaps we can sugar the configuration-less case to just bootstrap with prometheus client as parameter but that's minor...

Thanks for the pointer :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem. And definitely open to add sugar here 👍🏻

XCTAssertNoThrow(try MetricsSystem.prometheus())
}

func testCounter() {
let counter = Counter(label: "my_counter")
Expand Down