Add monitoring
This commit is contained in:
parent
2299cae50c
commit
b5a8fc8e22
@ -1,7 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Vapor
|
import Vapor
|
||||||
|
import Clairvoyant
|
||||||
|
|
||||||
final class CapServer {
|
final class CapServer: ServerOwner {
|
||||||
|
|
||||||
// MARK: Paths
|
// MARK: Paths
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ final class CapServer {
|
|||||||
var classifierVersion: Int = 0 {
|
var classifierVersion: Int = 0 {
|
||||||
didSet {
|
didSet {
|
||||||
writeClassifierVersion()
|
writeClassifierVersion()
|
||||||
|
updateMonitoredClassifierVersionProperty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +46,10 @@ final class CapServer {
|
|||||||
private var nextSaveTime: Date?
|
private var nextSaveTime: Date?
|
||||||
|
|
||||||
private var caps = [Int: Cap]() {
|
private var caps = [Int: Cap]() {
|
||||||
didSet { scheduleSave() }
|
didSet {
|
||||||
|
scheduleSave()
|
||||||
|
updateMonitoredPropertiesOnCapChange()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var nextClassifierVersion: Int {
|
var nextClassifierVersion: Int {
|
||||||
@ -310,4 +315,81 @@ final class CapServer {
|
|||||||
classifierVersion = version
|
classifierVersion = version
|
||||||
log("Updated classifier to version \(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 Vapor
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Clairvoyant
|
||||||
|
|
||||||
|
|
||||||
private(set) var server: CapServer!
|
private(set) var server: CapServer!
|
||||||
|
private(set) var monitor: PropertyManager!
|
||||||
|
|
||||||
public func configure(_ app: Application) throws {
|
public func configure(_ app: Application) throws {
|
||||||
|
|
||||||
try Log.set(logFile: config.logPath)
|
|
||||||
|
|
||||||
let resourceDirectory = URL(fileURLWithPath: app.directory.resourcesDirectory)
|
let resourceDirectory = URL(fileURLWithPath: app.directory.resourcesDirectory)
|
||||||
let publicDirectory = app.directory.publicDirectory
|
let publicDirectory = app.directory.publicDirectory
|
||||||
|
|
||||||
let config = Config(loadFrom: resourceDirectory)
|
let config = Config(loadFrom: resourceDirectory)
|
||||||
|
|
||||||
server = CapServer(in: URL(fileURLWithPath: publicDirectory),
|
server = CapServer(in: URL(fileURLWithPath: publicDirectory),
|
||||||
writers: config.writers)
|
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 {
|
if config.serveFiles {
|
||||||
let middleware = FileMiddleware(publicDirectory: publicDirectory)
|
let middleware = FileMiddleware(publicDirectory: publicDirectory)
|
||||||
app.middleware.use(middleware)
|
app.middleware.use(middleware)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
try server.loadData()
|
||||||
|
} catch {
|
||||||
|
monitor.update(status: .initializationFailure)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Register routes to the router
|
// Register routes to the router
|
||||||
try routes(app)
|
routes(app)
|
||||||
|
|
||||||
|
monitor.update(status: .nominal)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func writeDefaultCofig(to path: URL) throws -> Config {
|
func log(_ message: String) {
|
||||||
do {
|
monitor.log(message)
|
||||||
let configData = try JSONEncoder().encode(Config.default)
|
print(message)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ private func authorize(_ request: Request) throws {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func routes(_ app: Application) throws {
|
func routes(_ app: Application) {
|
||||||
|
|
||||||
app.get("version") { _ in
|
app.get("version") { _ in
|
||||||
"\(server.classifierVersion)"
|
"\(server.classifierVersion)"
|
||||||
|
Loading…
Reference in New Issue
Block a user