Skip to content

Commit

Permalink
Adds first pass on metrics. Didn't test CloudWatch
Browse files Browse the repository at this point in the history
  • Loading branch information
jrddunbr committed Jan 25, 2021
1 parent 4561471 commit 28821cd
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 22 deletions.
14 changes: 14 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
buildscript {
ext.prometheus_version = "0.1.0"
repositories {
mavenCentral()
maven {
Expand All @@ -15,6 +16,7 @@ buildscript {
classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
classpath 'de.undercouch:gradle-download-task:1.2'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6'
classpath "io.spring.gradle:dependency-management-plugin:1.0.9.RELEASE"
}
}

Expand All @@ -28,9 +30,17 @@ apply plugin: 'de.undercouch.download'
apply plugin: 'java'
apply plugin: 'com.jfrog.bintray'
apply plugin: 'maven-publish'
apply plugin: "io.spring.dependency-management"

apply from: 'gradle.properties'

dependencyManagement {
imports {
mavenBom 'com.amazonaws:aws-java-sdk-bom:1.11.942'
}
}


group = GROUP
archivesBaseName = ARCHIVE_NAME
version = MAJORVERSION + "." + MINORVERSION + "." + REVISION
Expand Down Expand Up @@ -91,6 +101,10 @@ dependencies {
shade "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
compile "mcp.mobius.waila:Waila:1.5.11-RC2-NONEI_1.7.10:dev"
compile "com.fazecast:jSerialComm:2.6.2"
shade 'com.amazonaws:aws-java-sdk-cloudwatch'
shade group: 'io.prometheus', name: 'simpleclient', version: prometheus_version
shade group: 'io.prometheus', name: 'simpleclient_httpserver', version: prometheus_version
shade group: 'io.prometheus', name: 'simpleclient_hotspot', version: prometheus_version
}

jar {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MAJORVERSION = 1
MINORVERSION = 18
REVISION = 3
REVISION = 4

GROUP = 'net.electricalage.eln'
ARCHIVE_NAME = 'ElectricalAge-jrddunbr'
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/mods/eln/Eln.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import mods.eln.item.regulator.RegulatorAnalogDescriptor;
import mods.eln.item.regulator.RegulatorOnOffDescriptor;
import mods.eln.mechanical.*;
import mods.eln.metrics.MetricsSubsystem;
import mods.eln.misc.*;
import mods.eln.misc.SeriesFunction;
import mods.eln.node.NodeBlockEntity;
Expand Down Expand Up @@ -365,6 +366,11 @@ public class Eln {
public static String dictNibbleMemory;
public static String dictALU;

public static String metricsDimension;
public static int prometheusPort;
public static boolean cloudwatchEnable;
public static boolean prometheusEnable;

public static Configuration config;

@EventHandler
Expand Down Expand Up @@ -529,6 +535,19 @@ public void preInit(FMLPreInitializationEvent event) {

Eln.flywheelMass = Math.min(Math.max(config.get("balancing", "flywheelMass", 50.0, "How heavy is *your* flywheel?").getDouble(), 1.0), 1000.0);

/*
*
* Metrics configuration
*
*
*/

Eln.metricsDimension = config.get("metrics", "config_dimension", "Server").getString();
Eln.prometheusPort = config.get("metrics", "prometheus_port", 3424).getInt();

Eln.cloudwatchEnable = config.get("metrics", "cloudwatch_enable", false, "Enable logging to AWS Cloudwatch. Account requried.").getBoolean();
Eln.prometheusEnable = config.get("metrics", "prometheus_enable", false, "Enable Prometheus Logging Node").getBoolean();

config.save();

eventChannel = NetworkRegistry.INSTANCE.newEventDrivenChannel(channelName);
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/mods/eln/metrics/CloudwatchMetricsImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package mods.eln.metrics

import com.amazonaws.services.cloudwatch.AmazonCloudWatchClientBuilder
import com.amazonaws.services.cloudwatch.model.Dimension
import com.amazonaws.services.cloudwatch.model.MetricDatum
import com.amazonaws.services.cloudwatch.model.PutMetricDataRequest
import mods.eln.Eln


object CloudwatchMetricsImpl: IMetricsAbstraction {
val cw = AmazonCloudWatchClientBuilder.defaultClient()

/**
* Put a metric to CloudWatch.
*
* @param value The value of the metric in question
* @param unit The unit type
* @param metricName The name of the metric (for example, time to complete X)
* @param namespace The part of the code we are in. For example, "MNA"
* @param description The description (unused for CloudWatch)
*/
override fun putMetric(value: Double, unit: UnitTypes, metricName: String, namespace: String, description: String) {
val dimension = Dimension().withName("Server").withValue(Eln.metricsDimension)
val datum = MetricDatum().withMetricName(metricName).withUnit(unit.symbol).withValue(value).withDimensions(dimension)
val request = PutMetricDataRequest().withNamespace("ELN/$namespace").withMetricData(datum)
cw.putMetricData(request)
}
}
28 changes: 28 additions & 0 deletions src/main/java/mods/eln/metrics/IMetricsAbstraction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package mods.eln.metrics

interface IMetricsAbstraction {
/**
* Put a metric to a service.
*
* @param value The value of the metric in question (a double)
* @param unit The unit type (seconds, volts, watts, etc.)
* @param metricName The name of the metric (for example, time to complete X)
* @param namespace The part of the code we are in. For example, "MNA"
*/
fun putMetric(value: Double, unit: UnitTypes, metricName: String, namespace: String, description: String)
}

enum class UnitTypes(val symbol: String) {
SECONDS("s"),
MINUTES("m"),
HOURS("h"),
MILLISECONDS("ms"),
MICROSECONDS("µs"),
NANOSECONDS("ns"),
TICKS("t"),
VOLTS("V"),
AMPS("A"),
WATTS("W"),
JOULES("J"),
NO_UNITS("")
}
64 changes: 64 additions & 0 deletions src/main/java/mods/eln/metrics/MetricsSubsystem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package mods.eln.metrics

import mods.eln.Eln
import mods.eln.misc.Utils
import java.util.concurrent.ConcurrentLinkedQueue

object MetricsSubsystem {
private val metricSinks = mutableListOf<IMetricsAbstraction>()
private val metricThread: Thread
private val metricsIngested = ConcurrentLinkedQueue<MetricData>()

init {
if (Eln.prometheusEnable) {
metricSinks.add(PrometheusMetricsImpl)
}
if (Eln.cloudwatchEnable) {
metricSinks.add(CloudwatchMetricsImpl)
}

metricThread = Thread {
// Wait a tick for data. We don't need to be exactly on the tick, but it's not worth going faster really.
Thread.sleep(50)

while (true) {
val metric = metricsIngested.remove()?: break
metricSinks.forEach {
try {
it.putMetric(metric.value, metric.unit, metric.metricName, metric.namespace, metric.description)
} catch (e: Exception) {
Utils.println("The ${it.javaClass.name} metric subsystem failed. $e")
}
}
}
}
metricThread.start()
}

/**
* Put a metric to the metrics subsystem (Now with thread safety!)
*
* @param value The value of the metric in question (a double)
* @param unit The unit type (seconds, volts, watts, etc.)
* @param metricName The name of the metric (for example, time to complete X)
* @param namespace The part of the code we are in. For example, "MNA"
*/
fun putMetric(value: Double, unit: UnitTypes, metricName: String, namespace: String, description: String) {
metricsIngested.add(MetricData(value, unit, metricName, namespace, description))
}

/**
* Simple thing to just put data directly in (for use by simplistic abstractions)
*/
fun putMetric(metricData: MetricData) {
metricsIngested.add(metricData)
}
}

class SimpleMetric(val metricName: String, val namespace: String, val description: String, val unit: UnitTypes) {
fun putMetric(value: Double) {
MetricsSubsystem.putMetric(MetricData(value, unit, metricName, namespace, description))
}
}

data class MetricData(val value: Double, val unit: UnitTypes, val metricName: String, val namespace: String, val description: String)
44 changes: 44 additions & 0 deletions src/main/java/mods/eln/metrics/PrometheusMetricsImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package mods.eln.metrics

import io.prometheus.client.Gauge
import io.prometheus.client.exporter.HTTPServer
import mods.eln.Eln

object PrometheusMetricsImpl: IMetricsAbstraction {

init {
// I have no idea how this works. I guess saving it to a value is not necessary?
HTTPServer("0.0.0.0", Eln.prometheusPort)
}

private val metricMap = mutableMapOf<String, Gauge>()

/**
* Put a metric to Prometheus.
*
* @param value The value of the metric in question
* @param unit The unit type (Unused for Prometheus)
* @param metricName The name of the metric (for example, time to complete X)
* @param namespace The part of the code we are in. For example, "MNA"
* @param description The description (a help text for Prometheus)
*/
override fun putMetric(value: Double, unit: UnitTypes, metricName: String, namespace: String, description: String) {
createMetric(metricName, namespace, description)
val metric = metricMap[metricName]?: return
metric.set(value)
}

/**
* You can't have duplicate metric names registered, so we're going to handle that for you.
*
* @param metricName The name of the metrics (for example, time to complete X)
* @param namespace The part of the code we are in. For example, "MNA"
* @param description The description (a help text for Prometheus)
*/
fun createMetric(metricName: String, namespace: String, description: String) {
if (metricName !in metricMap) {
val metric = Gauge.build().name("${namespace}_$metricName").help(description).register()
metricMap[metricName] = metric
}
}
}
Loading

0 comments on commit 28821cd

Please sign in to comment.