Skip to content

Commit

Permalink
Merge pull request #792 from MarathonLabs/feature/grafana-cloud
Browse files Browse the repository at this point in the history
feat(analytics): migrate to grafana cloud
  • Loading branch information
Malinskiy authored May 4, 2023
2 parents 1365090 + c104911 commit 4bd2c5e
Show file tree
Hide file tree
Showing 29 changed files with 233 additions and 124 deletions.
6 changes: 5 additions & 1 deletion analytics/usage/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ plugins {
`java-library`
id("org.jetbrains.kotlin.jvm")
id("org.jetbrains.dokka")
jacoco
}

setupDeployment()
setupKotlinCompiler()
setupTestTask()

dependencies {
implementation(Analytics.googleAnalyticsWrapper)
implementation(Libraries.okhttp)
implementation(Libraries.kotlinStdLib)
testRuntimeOnly(TestLibraries.jupiterEngine)
testImplementation(TestLibraries.junit5)
testImplementation(TestLibraries.kluent)
testImplementation(TestLibraries.mockitoKotlin)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.malinskiy.marathon.usageanalytics

sealed class Event {
data class TestsTotal(
val total: Int
) : Event()

data class TestsRun(
val value: Int
) : Event()

data class Devices(
val total: Int
) : Event()

data class Executed(
val seconds: Long
) : Event()
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.malinskiy.marathon.usageanalytics.tracker

internal class EmptyTracker : UsageTracker {
override fun trackEvent(event: Event) {
import com.malinskiy.marathon.usageanalytics.Event

}
}
class EmptyTracker : UsageTracker {
override fun trackEvent(event: Event) = Unit
override fun meta(version: String, vendor: String, releaseMode: String) = Unit
override fun close() = Unit
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.malinskiy.marathon.usageanalytics.tracker

import com.malinskiy.marathon.usageanalytics.Event
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.lang.Exception
import java.util.UUID

class GrafanaCloud : UsageTracker {
private val client = OkHttpClient()
private val key =
"967310:eyJrIjoiMmEzNWM4ZmI2ZTExNjg1OWJiZDUxOWE3YWU1N2NiMTI5MDRhOTk2ZiIsIm4iOiJtYXJhdGhvbi1vc3MtcHVibGlzaGluZyIsImlkIjo4NDcyNDZ9"
private val accumulator = mutableListOf<Event>()
private val tags = mutableMapOf<String, String>()

override fun trackEvent(event: Event) {
accumulator.add(event)
}

override fun meta(version: String, vendor: String, releaseMode: String) {
tags["version"] = version
tags["releaseMode"] = releaseMode
tags["vendor"] = vendor
tags["id"] = UUID.randomUUID().toString()
}

private fun sendEvents(events: Collection<Event>) {
val body = convertToJson(events)
send(body)
}

private fun convertToJson(events: Collection<Event>): String {
return StringBuilder().apply {
append("[")
append(
events.map { event ->
when (event) {
is Event.Devices -> Metric(name = "testing.device", value = event.total, tags = tags)
is Event.Executed -> Metric(name = "testing.duration", value = event.seconds, tags = tags)
is Event.TestsTotal -> Metric(name = "testing.test", value = event.total, tags = tags)
is Event.TestsRun -> Metric(name = "testing.executed", value = event.value, tags = tags)
}
}.joinToString(separator = ",") { it.toJson() }
)
append("]")
}.toString()
}

override fun close() {
sendEvents(accumulator)
}

private fun send(body: String) {
try {
val request = Request.Builder()
.url("https://graphite-prod-13-prod-us-east-0.grafana.net/graphite/metrics")
.header("Authorization", "Bearer $key")
.post(body.toByteArray().toRequestBody("application/json".toMediaType()))
.build()

client.newCall(request).execute().use { response ->
if (!response.isSuccessful) {
//se la vie, we shouldn't handle analytics errors because users don't care about them
}
}
} catch (e: Exception) {
//se la vie, we shouldn't handle analytics errors because users don't care about them
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.malinskiy.marathon.usageanalytics.tracker

data class Metric(
val name: String,
val value: Number,
val time: Long = System.currentTimeMillis() / 1000,
val interval: Int = 1,
val tags: Map<String, String> = emptyMap(),
) {
fun toJson() = """{"name":"$name","interval":$interval,"value":$value,"time":$time,"tags":[${
tags.map { "${it.key}=${it.value}" }.joinToString(separator = ",") { "\"$it\""}
}]}"""
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.malinskiy.marathon.usageanalytics.tracker

import com.malinskiy.marathon.usageanalytics.Event

interface UsageTracker {
fun trackEvent(event: Event)
}
fun meta(version: String, vendor: String, releaseMode: String)
fun close()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.malinskiy.marathon.usageanalytics.tracker

import org.amshove.kluent.`should be equal to`
import org.junit.jupiter.api.Test


class MetricTest {

@Test
fun toJson() {
val metric = Metric(
name = "testing.count",
interval = 1,
value = 42,
time = 1000,
tags = mapOf("id" to "123")
)
metric.toJson().`should be equal to`("""{"name":"testing.count","interval":1,"value":42,"time":1000,"tags":["id=123"]}""")
}
}
7 changes: 2 additions & 5 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ object Versions {
val apacheCommonsText = "1.10.0"
val apacheCommonsIO = "2.11.0"
val apacheCommonsCodec = "1.15"
val okhttp = "4.10.0"
val influxDbClient = "2.23"
val influxDb2Client = "6.8.0"
val clikt = "3.5.2"
Expand All @@ -52,7 +53,6 @@ object Versions {
val allureKotlin = "2.4.0"
val allureEnvironment = "1.0.0"
val mockitoKotlin = "2.2.0"
val googleAnalitycsWrapper = "2.0.0"
val dokka = "1.8.10"
val koin = "3.4.0"
val jsonAssert = "1.5.1"
Expand Down Expand Up @@ -104,6 +104,7 @@ object Libraries {
val koin = "io.insert-koin:koin-core:${Versions.koin}"
val bugsnag = "com.bugsnag:bugsnag:${Versions.bugsnag}"
val kotlinProcess = "com.github.pgreze:kotlin-process:${Versions.kotlinProcess}"
val okhttp = "com.squareup.okhttp3:okhttp:${Versions.okhttp}"
}

object TestLibraries {
Expand All @@ -126,7 +127,3 @@ object TestLibraries {
val testContainersInflux = "org.testcontainers:influxdb:${Versions.testContainers}"
val adamServerStubJunit5 = "com.malinskiy.adam:server-stub-junit5:${Versions.adam}"
}

object Analytics {
val googleAnalyticsWrapper = "com.brsanthu:google-analytics-java:${Versions.googleAnalitycsWrapper}"
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ import com.malinskiy.marathon.di.marathonStartKoin
import com.malinskiy.marathon.exceptions.ExceptionsReporterFactory
import com.malinskiy.marathon.ios.AppleVendor
import com.malinskiy.marathon.log.MarathonLogging
import com.malinskiy.marathon.usageanalytics.TrackActionType
import com.malinskiy.marathon.usageanalytics.UsageAnalytics
import com.malinskiy.marathon.usageanalytics.tracker.Event
import org.koin.core.context.stopKoin
import org.koin.dsl.module
import kotlin.system.exitProcess
Expand Down Expand Up @@ -59,7 +56,8 @@ private fun execute(cliConfiguration: CliConfiguration) {
}

val configuration = ConfigurationFactory(
marathonfileDir = marathonStartConfiguration.marathonfile.canonicalFile.parentFile
marathonfileDir = marathonStartConfiguration.marathonfile.canonicalFile.parentFile,
analyticsTracking = marathonStartConfiguration.analyticsTracking,
).parse(marathonStartConfiguration.marathonfile)
val vendorConfiguration = configuration.vendorConfiguration
val modules = when (vendorConfiguration) {
Expand All @@ -75,8 +73,6 @@ private fun execute(cliConfiguration: CliConfiguration) {
val application = marathonStartKoin(configuration, modules)
val marathon: Marathon = application.koin.get()

UsageAnalytics.enable = marathonStartConfiguration.analyticsTracking
UsageAnalytics.USAGE_TRACKER.trackEvent(Event(TrackActionType.RunType, "cli"))
val success = try {
marathon.run(marathonStartConfiguration.executionCommand)
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class MarathonRunCommonOptions : OptionGroup() {
.default(File("Marathonfile"))
val analyticsTracking by option("--analyticsTracking", help="Enable anonymous analytics tracking")
.convert { it.toBoolean() }
.default(false)
.default(true)
val bugsnagReporting by option("--bugsnag", help="Enable/Disable anonymous crash reporting. Enabled by default")
.convert { it.toBoolean() }
.default(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class ConfigurationFactory(
registerModule(JavaTimeModule())
},
private val environmentVariableSubstitutor: StringSubstitutor = StringSubstitutor(StringLookupFactory.INSTANCE.environmentVariableStringLookup()),
private val analyticsTracking: Boolean? = null,
) {
fun parse(marathonfile: File): Configuration {
val configWithEnvironmentVariablesReplaced = environmentVariableSubstitutor.replace(marathonfile.readText())
Expand All @@ -69,8 +70,13 @@ class ConfigurationFactory(
val resolvedApplication = it.application?.let { ddd -> marathonfileDir.resolve(ddd) }
val resolvedTestApplication = it.testApplication?.let { ddd -> marathonfileDir.resolve(ddd) }
val resolvedExtraApplications = it.extraApplications?.map { ddd -> marathonfileDir.resolve(ddd) }

AppleTestBundleConfiguration(resolvedApplication, resolvedTestApplication, resolvedExtraApplications, resolvedDerivedDataDir).apply { validate() }

AppleTestBundleConfiguration(
resolvedApplication,
resolvedTestApplication,
resolvedExtraApplications,
resolvedDerivedDataDir
).apply { validate() }
}
val optionalDevices = configuration.vendorConfiguration.devicesFile?.resolveAgainst(marathonfileDir)
?: marathonfileDir.resolve("Marathondevices")
Expand All @@ -89,7 +95,7 @@ class ConfigurationFactory(
authentication = optionalSshAuthentication,
knownHostsPath = optionalknownHostsPath,
)

configuration.vendorConfiguration.copy(
bundle = resolvedBundle,
devicesFile = optionalDevices,
Expand All @@ -101,7 +107,10 @@ class ConfigurationFactory(
is VendorConfiguration.EmptyVendorConfiguration -> throw ConfigurationException("No vendor configuration specified")
}

return configuration.copy(vendorConfiguration = vendorConfiguration)
return configuration.copy(
vendorConfiguration = vendorConfiguration,
analyticsTracking = analyticsTracking ?: configuration.analyticsTracking
)
} catch (e: JsonProcessingException) {
throw ConfigurationException("Error parsing config file ${marathonfile.absolutePath}", e)
}
Expand Down
Loading

0 comments on commit 4bd2c5e

Please sign in to comment.