Add (inofficial) routes to get system information

This commit is contained in:
Eike Kettner
2021-01-05 20:54:53 +01:00
parent 30df887934
commit b08e88cd69
7 changed files with 210 additions and 16 deletions

View File

@ -0,0 +1,69 @@
package docspell.common
import io.circe.Decoder
import io.circe.Encoder
final case class ByteSize(bytes: Long) {
def toHuman: String =
ByteSize.bytesToHuman(bytes)
def <=(other: ByteSize) =
bytes <= other.bytes
def >=(other: ByteSize) =
bytes >= other.bytes
def >(other: ByteSize) =
bytes > other.bytes
def -(other: ByteSize) =
ByteSize(bytes - other.bytes)
def +(other: ByteSize) =
ByteSize(bytes + other.bytes)
}
object ByteSize {
val zero = ByteSize(0L)
def bytesToHuman(bytes: Long): String =
if (math.abs(bytes) < 1024 && bytes != Long.MinValue) s"${bytes}B"
else {
val k = bytes / 1024.0
if (math.abs(k) < 1024) f"$k%.02fK"
else {
val m = k / 1024.0
if (math.abs(m) < 1024) f"$m%.02fM"
else f"${m / 1024.0}%.02fG"
}
}
def parse(str: String): Either[String, ByteSize] =
str.toLongOption
.map(ByteSize.apply)
.toRight(s"Not a valid size string: $str")
.orElse(span(str.toLowerCase) match {
case (num, "k") =>
Right(ByteSize(math.round(num.toDouble * 1024)))
case (num, "m") =>
Right(ByteSize(math.round(num.toDouble * 1024 * 1024)))
case (num, "g") =>
Right(ByteSize(math.round(num.toDouble * 1024 * 1024 * 1024)))
case _ =>
Left(s"Invalid byte string: $str")
})
private def span(str: String): (String, String) =
if (str.isEmpty) ("", "")
else (str.init, str.last.toString)
def unsafe(str: String): ByteSize =
parse(str).fold(sys.error, identity)
implicit val jsonDecoder: Decoder[ByteSize] =
Decoder.decodeLong.map(ByteSize.apply)
implicit val jsonEncoder: Encoder[ByteSize] =
Encoder.encodeLong.contramap(_.bytes)
}

View File

@ -0,0 +1,103 @@
package docspell.common
import java.time.Instant
import scala.jdk.CollectionConverters._
import cats.effect._
import cats.implicits._
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.circe.{Decoder, Encoder}
case class JvmInfo(
id: Ident,
pidHost: String,
ncpu: Int,
inputArgs: List[String],
libraryPath: String,
specVendor: String,
specVersion: String,
startTime: Timestamp,
uptime: Duration,
vmName: String,
vmVendor: String,
vmVersion: String,
heapUsage: JvmInfo.MemoryUsage,
props: Map[String, String]
)
object JvmInfo {
def create[F[_]: Sync](id: Ident): F[JvmInfo] =
MemoryUsage.createHeap[F].flatMap { mu =>
Sync[F].delay {
val rmb = management.ManagementFactory.getRuntimeMXBean()
val rt = Runtime.getRuntime()
JvmInfo(
id,
pidHost = rmb.getName(),
ncpu = rt.availableProcessors(),
inputArgs = rmb.getInputArguments().asScala.toList,
libraryPath = rmb.getLibraryPath(),
specVendor = rmb.getSpecVendor(),
specVersion = rmb.getSpecVersion(),
startTime = Timestamp(Instant.ofEpochMilli(rmb.getStartTime())),
uptime = Duration.millis(rmb.getUptime()),
vmName = rmb.getVmName(),
vmVendor = rmb.getVmVendor(),
vmVersion = rmb.getVmVersion(),
heapUsage = mu,
props = rmb.getSystemProperties().asScala.toMap
)
}
}
case class MemoryUsage(
init: Long,
used: Long,
comitted: Long,
max: Long,
free: Long,
description: String
)
object MemoryUsage {
def apply(init: Long, used: Long, comitted: Long, max: Long): MemoryUsage = {
def str(n: Long) = ByteSize(n).toHuman
val free = max - used
val descr =
s"init=${str(init)}, used=${str(used)}, comitted=${str(comitted)}, max=${str(max)}, free=${str(free)}"
MemoryUsage(init, used, comitted, max, free, descr)
}
val empty = MemoryUsage(0, 0, 0, 0)
def createHeap[F[_]: Sync]: F[MemoryUsage] =
Sync[F].delay {
val mxb = management.ManagementFactory.getMemoryMXBean()
val heap = mxb.getHeapMemoryUsage()
MemoryUsage(
init = math.max(0, heap.getInit()),
used = math.max(0, heap.getUsed()),
comitted = math.max(0, heap.getCommitted()),
max = math.max(0, heap.getMax())
)
}
implicit val jsonEncoder: Encoder[MemoryUsage] =
deriveEncoder[MemoryUsage]
implicit val jsonDecoder: Decoder[MemoryUsage] =
deriveDecoder[MemoryUsage]
}
implicit val jsonEncoder: Encoder[JvmInfo] =
deriveEncoder[JvmInfo]
implicit val jsonDecoder: Decoder[JvmInfo] =
deriveDecoder[JvmInfo]
}