Skip to content

Commit

Permalink
Merge pull request #10 from navikt/influxdb
Browse files Browse the repository at this point in the history
Influxdb
  • Loading branch information
chris-santa authored May 18, 2021
2 parents f100d63 + d67b8fb commit 9ba5c32
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 1 deletion.
54 changes: 54 additions & 0 deletions dittnav-common-influxdb/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
plugins {
`maven-publish`
`java-library`
kotlin("jvm")
}

repositories {
jcenter()
mavenCentral()
mavenLocal()
}

publishing {
repositories {
maven {
url = uri("https://maven.pkg.github.com/navikt/dittnav-common-lib")
credentials {
username = "x-access-token"
password = System.getenv("GITHUB_TOKEN")
}
}
}
publications {
create<MavenPublication>("gpr") {
from(components["java"])
}
}
}

dependencies {
api(kotlin("stdlib-jdk8"))
implementation(Influxdb.java)
implementation(Kotlinx.coroutines)
implementation(Logback.classic)
implementation(Logstash.logbackEncoder)
testImplementation(kotlin("test-junit5"))
testImplementation(Junit.api)
testImplementation(Junit.engine)
testImplementation(Kluent.kluent)
testImplementation(Mockk.mockk)
}

tasks {
compileKotlin {
kotlinOptions.jvmTarget = "13"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "13"
}

withType<Test> {
useJUnitPlatform()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package no.nav.personbruker.dittnav.common.metrics

interface MetricsReporter {
suspend fun registerDataPoint(measurementName: String, fields: Map<String, Any>, tags: Map<String, String>)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package no.nav.personbruker.dittnav.common.metrics

import org.slf4j.LoggerFactory


class StubMetricsReporter : MetricsReporter {

val log = LoggerFactory.getLogger(StubMetricsReporter::class.java)

override suspend fun registerDataPoint(measurementName: String, fields: Map<String, Any>, tags: Map<String, String>) {
log.debug("Data point: { measurement: $measurementName, fields: $fields, tags: $tags }")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package no.nav.personbruker.dittnav.common.metrics.influxdb

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.influxdb.InfluxDB
import org.influxdb.InfluxDBFactory
import org.influxdb.dto.Point
import org.slf4j.LoggerFactory

internal class DataPointRelay(private val influxDB: InfluxDB) {

private val log = LoggerFactory.getLogger(DataPointRelay::class.java)

suspend fun submitDataPoint(point: Point) = withContext(Dispatchers.IO) {
try {
influxDB.write(point)
} catch (e: Exception) {
log.warn("Klarte ikke skrive event til InfluxDB.", e)
}
}
}

internal object DataPointRelayFactory {

internal fun createDataPointRelay(influxConfig: InfluxConfig): DataPointRelay {
val influxDb = InfluxDBFactory.connect(
"https://${influxConfig.hostName}:${influxConfig.hostPort}",
influxConfig.userName,
influxConfig.password
)

influxDb.setDatabase(influxConfig.databaseName)

influxDb.setRetentionPolicy(influxConfig.retentionPolicyName)

if (influxConfig.enableEventBatching) {
influxDb.enableBatch()
} else {
influxDb.disableBatch()
}

return DataPointRelay(influxDb)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package no.nav.personbruker.dittnav.common.metrics.influxdb

data class InfluxConfig (
val hostName: String,
val userName: String,
val password: String,
val hostPort: Int,
val databaseName: String,
val retentionPolicyName: String,
val applicationName: String,
val clusterName: String,
val namespace: String,
val enableEventBatching: Boolean = true,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package no.nav.personbruker.dittnav.common.metrics.influxdb

import no.nav.personbruker.dittnav.common.metrics.MetricsReporter
import org.influxdb.dto.Point
import java.util.concurrent.TimeUnit

class InfluxMetricsReporter internal constructor(influxConfig: InfluxConfig, private val dataPointRelay: DataPointRelay) : MetricsReporter {

constructor(influxConfig: InfluxConfig) : this(influxConfig, DataPointRelayFactory.createDataPointRelay(influxConfig))

override suspend fun registerDataPoint(measurementName: String, fields: Map<String, Any>, tags: Map<String, String>) {
val point = Point.measurement(measurementName)
.time(System.currentTimeMillis(), TimeUnit.MILLISECONDS)
.tag(tags)
.tag(DEFAULT_TAGS)
.fields(fields)
.build()

dataPointRelay.submitDataPoint(point)
}

private val DEFAULT_TAGS = listOf(
"application" to influxConfig.applicationName,
"cluster" to influxConfig.clusterName,
"namespace" to influxConfig.namespace
).toMap()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package no.nav.personbruker.dittnav.common.metrics.influxdb

import io.mockk.coEvery
import io.mockk.mockk
import io.mockk.slot
import kotlinx.coroutines.runBlocking
import org.amshove.kluent.*
import org.influxdb.dto.Point
import org.junit.jupiter.api.Test
import java.util.concurrent.TimeUnit

internal class InfluxMetricsReporterTest {
val dataPointRelay: DataPointRelay = mockk()

val databaseName = "testdb"
val retentionPolicyName = "retention"
val application = "testApp"
val cluster = "test"
val namespace = "test1"

val influxConfig = InfluxConfig(
"",
"",
"",
0,
databaseName,
retentionPolicyName,
application,
cluster,
namespace
)

val metricsReporter = InfluxMetricsReporter(influxConfig, dataPointRelay)

@Test
fun `Should construct a data point and add time of measurement and application-global tags`() {

val pointSlot = slot<Point>()

val measurementName = "INVENTORY"

val fieldName = "value"
val fieldVal = 123

val tagName = "type"
val tagVal = "APPLE"

coEvery { dataPointRelay.submitDataPoint(capture(pointSlot)) } returns Unit

val fields = mapOf(fieldName to fieldVal)
val tags = mapOf(tagName to tagVal)

val start = System.currentTimeMillis()

runBlocking {
metricsReporter.registerDataPoint(measurementName, fields, tags)
}

val point = pointSlot.captured

val resultMeasurement: String = point.getPrivateField("measurement")
val resultFields: Map<String, Any> = point.getPrivateField("fields")
val resultTags: Map<String, String> = point.getPrivateField("tags")
val resultTime: Long = point.getPrivateField("time")
val resultPrecision: TimeUnit = point.getPrivateField("precision")

val end = System.currentTimeMillis()


resultMeasurement `should be equal to` measurementName
resultFields `should be equal to` fields
resultTags.values `should contain same` listOf(application, cluster, namespace, tagVal)
resultTime `should be greater or equal to` start
resultTime `should be less or equal to` end
resultPrecision `should be equal to` TimeUnit.MILLISECONDS
}

private inline fun <reified T: Any> Point.getPrivateField(fieldName: String): T {
return this::class.java.getDeclaredField(fieldName).let {
it.isAccessible = true
val field = it.get(this)

if (field is T) {
field
} else {
throw TypeCastException("Could not fetch private field '$fieldName' as ${T::class.simpleName}")
}
}
}
}
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
rootProject.name = "dittnav-common-lib"
include("dittnav-common-logging")
include("dittnav-common-influx")
include("dittnav-common-influxdb")
include("dittnav-common-security-authenticated-user")
include("dittnav-common-test")
include("dittnav-common-utils")
include("dittnav-common-evicting-cache")

0 comments on commit 9ba5c32

Please sign in to comment.