Add monitoring

This commit is contained in:
Christoph Hagen 2023-01-11 18:29:32 +01:00
parent 2299cae50c
commit b5a8fc8e22
4 changed files with 112 additions and 77 deletions

View File

@ -1,7 +1,8 @@
import Foundation
import Vapor
import Clairvoyant
final class CapServer {
final class CapServer: ServerOwner {
// MARK: Paths
@ -26,6 +27,7 @@ final class CapServer {
var classifierVersion: Int = 0 {
didSet {
writeClassifierVersion()
updateMonitoredClassifierVersionProperty()
}
}
@ -44,7 +46,10 @@ final class CapServer {
private var nextSaveTime: Date?
private var caps = [Int: Cap]() {
didSet { scheduleSave() }
didSet {
scheduleSave()
updateMonitoredPropertiesOnCapChange()
}
}
var nextClassifierVersion: Int {
@ -310,4 +315,81 @@ final class CapServer {
classifierVersion = version
log("Updated classifier to version \(version)")
}
// MARK: ServerOwner
let authenticationMethod: PropertyAuthenticationMethod = .accessToken
func hasReadPermission(for property: UInt32, accessData: Data) -> Bool {
guard let key = String(data: accessData, encoding: .utf8) else {
return false
}
return writers.contains(key)
}
func hasWritePermission(for property: UInt32, accessData: Data) -> Bool {
guard let key = String(data: accessData, encoding: .utf8) else {
return false
}
return writers.contains(key)
}
func hasListAccessPermission(_ accessData: Data) -> Bool {
guard let key = String(data: accessData, encoding: .utf8) else {
return false
}
return writers.contains(key)
}
// MARK: Monitoring
private let capCountPropertyId = PropertyId(name: "caps", uniqueId: 1)
private let imageCountPropertyId = PropertyId(name: "images", uniqueId: 2)
private let classifierVersionPropertyId = PropertyId(name: "classifier", uniqueId: 3)
func registerProperties(with monitor: PropertyManager) {
let capCountProperty = PropertyRegistration(
uniqueId: capCountPropertyId.uniqueId,
name: capCountPropertyId.name,
updates: .continuous,
isLogged: true,
allowsManualUpdate: false,
read: { [weak self] in
return (self?.capCount ?? 0).timestamped()
})
monitor.register(capCountProperty, for: self)
let imageCountProperty = PropertyRegistration(
uniqueId: imageCountPropertyId.uniqueId,
name: imageCountPropertyId.name,
updates: .continuous,
isLogged: true,
allowsManualUpdate: false,
read: { [weak self] in
return (self?.imageCount ?? 0).timestamped()
})
monitor.register(imageCountProperty, for: self)
let classifierVersionProperty = PropertyRegistration(
uniqueId: classifierVersionPropertyId.uniqueId,
name: classifierVersionPropertyId.name,
updates: .continuous,
isLogged: true,
allowsManualUpdate: false,
read: { [weak self] in
return (self?.classifierVersion ?? 0).timestamped()
})
monitor.register(classifierVersionProperty, for: self)
}
private func updateMonitoredPropertiesOnCapChange() {
try? monitor.logChanged(property: capCountPropertyId, value: capCount.timestamped())
try? monitor.logChanged(property: imageCountPropertyId, value: imageCount.timestamped())
}
private func updateMonitoredClassifierVersionProperty() {
try? monitor.logChanged(property: classifierVersionPropertyId, value: classifierVersion.timestamped())
}
}

View File

@ -1,50 +0,0 @@
//
// Log.swift
// App
//
// Created by Christoph on 05.05.20.
//
import Foundation
private let df: DateFormatter = {
let df = DateFormatter()
df.dateStyle = .short
df.timeStyle = .short
df.locale = Locale(identifier: "de")
return df
}()
func log(_ message: String, file: String = #file, line: Int = #line) {
let date = df.string(from: Date())
let m = "[\(date)][\(file.components(separatedBy: "/").last ?? file):\(line)] \(message)"
print(m)
Log.write(m + "\n")
}
enum Log {
static func set(logFile: String) throws {
let url = URL(fileURLWithPath: logFile)
let date = df.string(from: Date())
if !FileManager.default.fileExists(atPath: logFile) {
try "[\(date)] New log created.\n".write(to: url, atomically: false, encoding: .utf8)
}
guard let f = FileHandle(forWritingAtPath: logFile) else {
try "[\(date)] Failed to start log.\n".write(to: url, atomically: false, encoding: .utf8)
return
}
f.seekToEndOfFile()
f.write("[\(date)] Logging started.\n".data(using: .utf8)!)
file = f
}
static func write(_ message: String) {
guard let f = file else {
return
}
f.write(message.data(using: .utf8)!)
}
private static var file: FileHandle?
}

View File

@ -1,46 +1,49 @@
import Vapor
import Foundation
import Clairvoyant
private(set) var server: CapServer!
private(set) var monitor: PropertyManager!
public func configure(_ app: Application) throws {
try Log.set(logFile: config.logPath)
let resourceDirectory = URL(fileURLWithPath: app.directory.resourcesDirectory)
let publicDirectory = app.directory.publicDirectory
let config = Config(loadFrom: resourceDirectory)
server = CapServer(in: URL(fileURLWithPath: publicDirectory),
writers: config.writers)
monitor = .init(logFolder: config.logURL, serverOwner: server)
monitor.update(status: .initializing)
server.registerProperties(with: monitor)
monitor.registerRoutes(app)
app.http.server.configuration.port = config.port
app.routes.defaultMaxBodySize = .init(stringLiteral: config.maxBodySize)
if config.serveFiles {
let middleware = FileMiddleware(publicDirectory: publicDirectory)
app.middleware.use(middleware)
}
do {
try server.loadData()
} catch {
monitor.update(status: .initializationFailure)
return
}
// Register routes to the router
try routes(app)
routes(app)
monitor.update(status: .nominal)
}
private func writeDefaultCofig(to path: URL) throws -> Config {
do {
let configData = try JSONEncoder().encode(Config.default)
try configData.write(to: path)
print("Wrote default configuration to \(path.path)")
return .default
} catch {
print("Failed to write default config file at \(path.path): \(error)")
throw error
}
}
private func loadConfig(at path: URL) throws -> Config {
do {
let configData = try Data(contentsOf: path)
return try JSONDecoder().decode(Config.self, from: configData)
} catch {
print("Failed to load config file at \(path.path): \(error)")
throw error
}
func log(_ message: String) {
monitor.log(message)
print(message)
}

View File

@ -13,7 +13,7 @@ private func authorize(_ request: Request) throws {
}
}
func routes(_ app: Application) throws {
func routes(_ app: Application) {
app.get("version") { _ in
"\(server.classifierVersion)"