diff --git a/Package.swift b/Package.swift index f07c131..17bad5b 100755 --- a/Package.swift +++ b/Package.swift @@ -8,7 +8,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/vapor/vapor", from: "4.0.0"), - .package(url: "https://github.com/christophhagen/Clairvoyant", from: "0.4.0"), + .package(url: "https://github.com/christophhagen/Clairvoyant", from: "0.5.0"), ], targets: [ .target(name: "App", diff --git a/Sources/App/CapServer.swift b/Sources/App/CapServer.swift index 7733070..e8bb13c 100644 --- a/Sources/App/CapServer.swift +++ b/Sources/App/CapServer.swift @@ -41,7 +41,9 @@ final class CapServer { var classifierVersion: Int = 0 { didSet { writeClassifierVersion() - classifierMetric.update(classifierVersion) + Task { + try? await classifierMetric.update(classifierVersion) + } } } @@ -62,8 +64,10 @@ final class CapServer { private var caps = [Int: Cap]() { didSet { scheduleSave() - capCountMetric.update(caps.count) - imageCountMetric.update(imageCount) + Task { + try? await capCountMetric.update(caps.count) + try? await imageCountMetric.update(imageCount) + } } } @@ -79,7 +83,7 @@ final class CapServer { caps.reduce(0) { $0 + $1.value.count } } - init(in folder: URL) { + init(in folder: URL) async { self.imageFolder = folder.appendingPathComponent("images") self.thumbnailFolder = folder.appendingPathComponent("thumbnails") self.gridCountFile = folder.appendingPathComponent("count.js") @@ -90,6 +94,19 @@ final class CapServer { self.changedImagesFile = folder.appendingPathComponent("changes.txt") self.changedImageEntryDateFormatter = DateFormatter() changedImageEntryDateFormatter.dateFormat = "yy-MM-dd-HH-mm-ss" + + // Metric initializers only fail if observer is missing or ID is duplicate + self.capCountMetric = try! await .init("caps.count", + name: "Number of caps", + description: "The total number of caps in the database") + + self.imageCountMetric = try! await .init("caps.images", + name: "Total images", + description: "The total number of images for all caps") + + self.classifierMetric = try! await .init("caps.classifier", + name: "Classifier Version", + description: "The current version of the image classifier") } func loadData() throws { @@ -460,15 +477,9 @@ final class CapServer { // MARK: Monitoring - private let capCountMetric = Metric("caps.count", - name: "Number of caps", - description: "The total number of caps in the database") + private let capCountMetric: Metric - private let imageCountMetric = Metric("caps.images", - name: "Total images", - description: "The total number of images for all caps") + private let imageCountMetric: Metric - private let classifierMetric = Metric("caps.classifier", - name: "Classifier Version", - description: "The current version of the image classifier") + private let classifierMetric: Metric } diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift index 38b453f..55a91a1 100755 --- a/Sources/App/configure.swift +++ b/Sources/App/configure.swift @@ -2,7 +2,7 @@ import Vapor import Foundation import Clairvoyant -public func configure(_ app: Application) throws { +public func configure(_ app: Application) async throws { let resourceDirectory = URL(fileURLWithPath: app.directory.resourcesDirectory) let publicDirectory = app.directory.publicDirectory @@ -10,7 +10,7 @@ public func configure(_ app: Application) throws { let config = Config(loadFrom: resourceDirectory) let authenticator = Authenticator(writers: config.writers) - let monitor = MetricObserver( + let monitor = await MetricObserver( logFolder: config.logURL, accessManager: authenticator, logMetricId: "caps.log") @@ -18,18 +18,18 @@ public func configure(_ app: Application) throws { // All new metrics are automatically registered with the standard observer MetricObserver.standard = monitor - let status = Metric("caps.status", + let status = try await Metric("caps.status", name: "Status", description: "The general status of the service") - status.update(.initializing) - - let server = CapServer(in: URL(fileURLWithPath: publicDirectory)) - - monitor.registerRoutes(app) + try await status.update(.initializing) app.http.server.configuration.port = config.port app.routes.defaultMaxBodySize = .init(stringLiteral: config.maxBodySize) + let server = await CapServer(in: URL(fileURLWithPath: publicDirectory)) + + await monitor.registerRoutes(app) + if config.serveFiles { let middleware = FileMiddleware(publicDirectory: publicDirectory) app.middleware.use(middleware) @@ -41,13 +41,18 @@ public func configure(_ app: Application) throws { // Initialize the server data do { try server.loadData() - status.update(.nominal) } catch { - status.update(.initializationFailure) + try await status.update(.initializationFailure) } + try await status.update(.nominal) } func log(_ message: String) { - MetricObserver.standard?.log(message) - print(message) + guard let observer = MetricObserver.standard else { + print(message) + return + } + Task { + await observer.log(message) + } } diff --git a/Sources/Run/main.swift b/Sources/Run/main.swift index e715f81..7996f26 100755 --- a/Sources/Run/main.swift +++ b/Sources/Run/main.swift @@ -5,5 +5,11 @@ var env = Environment.production try LoggingSystem.bootstrap(from: &env) let app = Application(env) defer { app.shutdown() } -try configure(app) + +private let semaphore = DispatchSemaphore(value: 1) +Task { + try await configure(app) + semaphore.signal() +} +semaphore.wait() try app.run()