-
Notifications
You must be signed in to change notification settings - Fork 119
Expand file tree
/
Copy pathUptimeMonitor.scala
More file actions
74 lines (64 loc) · 2.66 KB
/
Copy pathUptimeMonitor.scala
File metadata and controls
74 lines (64 loc) · 2.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package demo
import kyo.*
/** Uptime monitor with SSE dashboard.
*
* Periodically pings real websites and streams health check results as SSE events. Demonstrates Async.foreach for concurrent health
* checks, SSE streaming, and periodic polling with Stream.
*/
object UptimeMonitor extends KyoApp:
case class HealthCheck(url: String, status: Int, healthy: Boolean, latencyMs: Long) derives Schema
case class CheckRound(timestamp: String, checks: List[HealthCheck]) derives Schema
val targets = Seq(
"https://www.google.com",
"https://github.com",
"https://www.scala-lang.org",
"https://docs.scala-lang.org",
"https://httpbin.org/status/200"
)
def checkOne(url: String): HealthCheck < Async =
Clock.stopwatch.map { sw =>
HttpClient.withConfig(_.timeout(10.seconds).followRedirects(true)) {
Abort.run[HttpException](HttpClient.getText(url)).map { result =>
sw.elapsed.map { dur =>
result match
case kyo.Result.Success(_) =>
HealthCheck(url, 200, true, dur.toMillis)
case _ =>
HealthCheck(url, 0, false, dur.toMillis)
}
}
}
}
def checkAll: CheckRound < Async =
for
checks <- Async.foreach(targets, targets.size)(checkOne).map(_.toList)
now <- Clock.now
yield CheckRound(now.toString, checks)
val statusStream = HttpHandler.getSseJson[CheckRound]("status") { _ =>
Stream[HttpSseEvent[CheckRound], Async] {
Loop.foreach {
for
_ <- Async.delay(30.seconds)(())
round <- checkAll
yield Emit.valueWith(Chunk(HttpSseEvent(data = round)))(Loop.continue)
}
}
}
val checkRoute = HttpHandler.getJson[CheckRound]("check") { _ =>
checkAll
}
val health = HttpHandler.health()
run {
val port = args.headOption.flatMap(_.toIntOption).getOrElse(0)
HttpServer.init(
HttpServerConfig.default.port(port).openApi("/openapi.json", "Uptime Monitor")
)(statusStream, checkRoute, health).map { server =>
for
_ <- Console.printLine(s"UptimeMonitor running on http://localhost:${server.port}")
_ <- Console.printLine(s" curl http://localhost:${server.port}/check")
_ <- Console.printLine(s" curl -N http://localhost:${server.port}/status")
_ <- server.await
yield ()
}
}
end UptimeMonitor