Add monitoring
This commit is contained in:
parent
2299cae50c
commit
b5a8fc8e22
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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?
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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)"
|
||||
|
Loading…
Reference in New Issue
Block a user