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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
package kamon.system.custom
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.nio.file.{ Paths, Files }
import akka.actor.{ Props, Actor, ActorSystem }
import akka.event.{ Logging, LoggingAdapter }
import kamon.Kamon
import kamon.metric._
import kamon.metric.instrument.InstrumentFactory
import kamon.system.custom.ContextSwitchesUpdater.UpdateContextSwitches
import org.hyperic.sigar.Sigar
import scala.collection.JavaConverters.iterableAsScalaIterableConverter
import scala.concurrent.duration.FiniteDuration
class ContextSwitchesMetrics(pid: Long, log: LoggingAdapter, instrumentFactory: InstrumentFactory) extends GenericEntityRecorder(instrumentFactory) {
val perProcessVoluntary = histogram("context-switches-process-voluntary")
val perProcessNonVoluntary = histogram("context-switches-process-non-voluntary")
val global = histogram("context-switches-global")
def update(): Unit = {
def contextSwitchesByProcess(pid: Long): (Long, Long) = {
val filename = s"/proc/$pid/status"
var voluntaryContextSwitches = 0L
var nonVoluntaryContextSwitches = 0L
try {
for (line ← Files.readAllLines(Paths.get(filename), StandardCharsets.US_ASCII).asScala.toList) {
if (line.startsWith("voluntary_ctxt_switches")) {
voluntaryContextSwitches = line.substring(line.indexOf(":") + 1).trim.toLong
}
if (line.startsWith("nonvoluntary_ctxt_switches")) {
nonVoluntaryContextSwitches = line.substring(line.indexOf(":") + 1).trim.toLong
}
}
} catch {
case ex: IOException ⇒ log.error("Error trying to read [{}]", filename)
}
(voluntaryContextSwitches, nonVoluntaryContextSwitches)
}
def contextSwitches: Long = {
val filename = "/proc/stat"
var contextSwitches = 0L
try {
for (line ← Files.readAllLines(Paths.get(filename), StandardCharsets.US_ASCII).asScala.toList) {
if (line.startsWith("rcs")) {
contextSwitches = line.substring(line.indexOf(" ") + 1).toLong
}
}
} catch {
case ex: IOException ⇒ log.error("Error trying to read [{}]", filename)
}
contextSwitches
}
val (voluntary, nonVoluntary) = contextSwitchesByProcess(pid)
perProcessVoluntary.record(voluntary)
perProcessNonVoluntary.record(nonVoluntary)
global.record(contextSwitches)
}
}
object ContextSwitchesMetrics {
def register(system: ActorSystem, refreshInterval: FiniteDuration): ContextSwitchesMetrics = {
val metricsExtension = Kamon(Metrics)(system)
val log = Logging(system, "ContextSwitchesMetrics")
val pid = (new Sigar).getPid
val instrumentFactory = metricsExtension.instrumentFactory("system-metric")
metricsExtension.register(Entity("context-switches", "system-metric"), new ContextSwitchesMetrics(pid, log, instrumentFactory)).recorder
}
}
class ContextSwitchesUpdater(csm: ContextSwitchesMetrics, refreshInterval: FiniteDuration) extends Actor {
val schedule = context.system.scheduler.schedule(refreshInterval, refreshInterval, self, UpdateContextSwitches)(context.dispatcher)
def receive = {
case UpdateContextSwitches ⇒ csm.update()
}
override def postStop(): Unit = {
schedule.cancel()
super.postStop()
}
}
object ContextSwitchesUpdater {
case object UpdateContextSwitches
def props(csm: ContextSwitchesMetrics, refreshInterval: FiniteDuration): Props =
Props(new ContextSwitchesUpdater(csm, refreshInterval))
}
|