66import io .micrometer .core .instrument .Gauge ;
77import io .micrometer .core .instrument .MeterRegistry ;
88import io .micrometer .core .instrument .Metrics ;
9+ import io .micrometer .core .instrument .binder .MeterBinder ;
910import io .micrometer .core .instrument .binder .jvm .ClassLoaderMetrics ;
1011import io .micrometer .core .instrument .binder .jvm .JvmGcMetrics ;
1112import io .micrometer .core .instrument .binder .jvm .JvmMemoryMetrics ;
1213import io .micrometer .core .instrument .binder .jvm .JvmThreadMetrics ;
1314import io .micrometer .core .instrument .binder .system .ProcessorMetrics ;
1415import io .micrometer .jmx .JmxConfig ;
1516import io .micrometer .jmx .JmxMeterRegistry ;
17+ import org .slf4j .Logger ;
18+ import org .slf4j .LoggerFactory ;
1619import org .terasology .engine .config .SystemConfig ;
1720import org .terasology .engine .context .Context ;
1821import org .terasology .engine .core .GameEngine ;
1922import org .terasology .engine .core .Time ;
2023import org .terasology .engine .core .subsystem .EngineSubsystem ;
2124import org .terasology .engine .monitoring .gui .AdvancedMonitor ;
25+ import reactor .core .publisher .Flux ;
26+ import reactor .function .TupleUtils ;
27+ import reactor .util .function .Tuple2 ;
28+ import reactor .util .function .Tuples ;
2229
2330import java .time .Duration ;
31+ import java .util .List ;
32+ import java .util .Set ;
2433
2534public class MonitoringSubsystem implements EngineSubsystem {
2635
2736 public static final Duration JMX_INTERVAL = Duration .ofSeconds (5 );
2837
38+ private static final Logger logger = LoggerFactory .getLogger (MonitoringSubsystem .class );
39+
40+ protected MeterRegistry meterRegistry ;
2941 private AdvancedMonitor advancedMonitor ;
3042
3143 @ Override
@@ -39,29 +51,23 @@ public void initialise(GameEngine engine, Context rootContext) {
3951 advancedMonitor = new AdvancedMonitor ();
4052 advancedMonitor .setVisible (true );
4153 }
54+ meterRegistry = initMeterRegistries ();
55+ }
4256
43- initMicrometerMetrics (rootContext .get (Time .class ));
57+ @ Override
58+ public void postInitialise (Context context ) {
59+ initMeters (context );
4460 }
4561
4662 /**
4763 * Initialize Micrometer metrics and publishers.
4864 *
4965 * @see org.terasology.engine.core.GameScheduler GameScheduler for global Reactor metrics
50- * @param time provides statistics
51- * <p>
52- * Note {@link org.terasology.engine.core.EngineTime EngineTime}
53- * does not serve the same role as a {@link Clock micrometer Clock}.
54- * (Not yet. Maybe it should?)
5566 */
56- private void initMicrometerMetrics ( Time time ) {
67+ protected MeterRegistry initMeterRegistries ( ) {
5768 // Register metrics with the built-in global composite registry.
5869 // This makes them available to any agent(s) we add to it.
59- MeterRegistry meterRegistry = Metrics .globalRegistry ;
60-
61- Gauge .builder ("terasology.fps" , time ::getFps )
62- .description ("framerate" )
63- .baseUnit ("Hz" )
64- .register (meterRegistry );
70+ MeterRegistry registry = Metrics .globalRegistry ;
6571
6672 // Publish the global metrics registry on a JMX server.
6773 MeterRegistry jmxMeterRegistry = new JmxMeterRegistry (new JmxConfig () {
@@ -77,14 +83,58 @@ public Duration step() {
7783 }, Clock .SYSTEM );
7884 Metrics .addRegistry (jmxMeterRegistry );
7985
86+ return registry ;
8087 // If we want to make global metrics available to our custom view,
8188 // we add our custom registry to the global composite:
8289 //
8390 // Metrics.addRegistry(DebugOverlay.meterRegistry);
8491 //
8592 // If we want to see JVM metrics there as well:
8693 //
87- // initAllJvmMetrics(DebugOverlay.meterRegistry);
94+ // allJvmMetrics().forEach(m -> m.bindTo(DebugOverlay.meterRegistry));
95+ }
96+
97+ /** Initialize meters for all the things in this Context. */
98+ protected void initMeters (Context context ) {
99+ // We can build meters individually like this:
100+ var time = context .get (Time .class );
101+ Gauge .builder ("terasology.fps" , time ::getFps )
102+ .description ("framerate" )
103+ .baseUnit ("Hz" )
104+ .register (meterRegistry );
105+
106+ // But we'd like the code for meters to live closer to the implementation
107+ // of the thing they're monitoring.
108+ //
109+ // Somewhere we get a list of all the things that provide meters.
110+ // Maybe hardcoded, maybe a registry of some kind? Modules will want
111+ // to contribute as well.
112+ var meterMaps = List .of (
113+ org .terasology .engine .rendering .world .Meters .GAUGE_MAP
114+ );
115+
116+ meterMaps .forEach (gaugeMap -> registerForContext (context , gaugeMap ));
117+ }
118+
119+ protected void registerForContext (Context context , Iterable <GaugeMapEntry > gaugeMap ) {
120+ Flux .fromIterable (gaugeMap )
121+ .map (entry -> Tuples .of (context .get (entry .iface ), entry .gaugeSpecs ))
122+ .filter (TupleUtils .predicate ((subject , specs ) -> subject != null ))
123+ .doOnDiscard (Tuple2 .class , TupleUtils .consumer ((iface , gaugeSpecs ) ->
124+ logger .debug ("Not building gauges for {}, none was in {}" , iface , context )))
125+ .subscribe (TupleUtils .consumer (this ::registerAll ));
126+ }
127+
128+ protected <T > void registerAll (T subject , Set <GaugeSpec <? extends T >> gaugeSpecs ) {
129+ Flux .fromIterable (gaugeSpecs )
130+ .filter (spec -> spec .isInstanceOfType (subject ))
131+ // Make sure the gauge is right for the specific type.
132+ .map (spec -> spec .binderAfterCasting (subject ))
133+ .subscribe (this ::registerMeter );
134+ }
135+
136+ public void registerMeter (MeterBinder meterBinder ) {
137+ meterBinder .bindTo (meterRegistry );
88138 }
89139
90140 /**
@@ -95,12 +145,14 @@ public Duration step() {
95145 * have a different agent you want them published through.
96146 */
97147 @ SuppressWarnings ("unused" )
98- void initAllJvmMetrics (MeterRegistry registry ) {
99- new ClassLoaderMetrics ().bindTo (registry );
100- new JvmMemoryMetrics ().bindTo (registry );
101- new JvmGcMetrics ().bindTo (registry );
102- new JvmThreadMetrics ().bindTo (registry );
103- new ProcessorMetrics ().bindTo (registry );
148+ List <MeterBinder > allJvmMetrics () {
149+ return List .of (
150+ new ClassLoaderMetrics (),
151+ new JvmMemoryMetrics (),
152+ new JvmGcMetrics (),
153+ new JvmThreadMetrics (),
154+ new ProcessorMetrics ()
155+ );
104156 }
105157
106158 @ Override
0 commit comments