-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2144 from DataDog/yl/add-datadog-meter
RUM-5553: Add DatadogMeter to read vital data for benchmark
- Loading branch information
Showing
13 changed files
with
981 additions
and
0 deletions.
There are no files selected for viewing
105 changes: 105 additions & 0 deletions
105
tools/benchmark/src/main/java/com/datadog/benchmark/DatadogMeter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2016-Present Datadog, Inc. | ||
*/ | ||
|
||
package com.datadog.benchmark | ||
|
||
import com.datadog.benchmark.exporter.DatadogMetricExporter | ||
import com.datadog.benchmark.internal.reader.CPUVitalReader | ||
import com.datadog.benchmark.internal.reader.FpsVitalReader | ||
import com.datadog.benchmark.internal.reader.MemoryVitalReader | ||
import com.datadog.benchmark.internal.reader.VitalReader | ||
import com.datadog.benchmark.noop.NoOpObservableDoubleGauge | ||
import io.opentelemetry.api.OpenTelemetry | ||
import io.opentelemetry.api.metrics.Meter | ||
import io.opentelemetry.api.metrics.ObservableDoubleGauge | ||
import io.opentelemetry.sdk.OpenTelemetrySdk | ||
import io.opentelemetry.sdk.metrics.SdkMeterProvider | ||
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader | ||
import java.util.concurrent.TimeUnit | ||
|
||
/** | ||
* This class is responsible for managing the performance gauges related to CPU, FPS, and memory usage. | ||
* It provides functionalities to start and stop monitoring these metrics, which will be uploaded to | ||
* Datadog metric API. | ||
*/ | ||
class DatadogMeter private constructor(private val meter: Meter) { | ||
|
||
private val cpuVitalReader: CPUVitalReader = CPUVitalReader() | ||
private val memoryVitalReader: MemoryVitalReader = MemoryVitalReader() | ||
private val fpsVitalReader: FpsVitalReader = FpsVitalReader() | ||
|
||
private val gaugesByMetricName: MutableMap<String, ObservableDoubleGauge> = mutableMapOf() | ||
|
||
/** | ||
* Starts cpu, memory and fps gauges. | ||
*/ | ||
fun startGauges() { | ||
startGauge(cpuVitalReader) | ||
startGauge(memoryVitalReader) | ||
startGauge(fpsVitalReader) | ||
} | ||
|
||
/** | ||
* Stops cpu, memory and fps gauges. | ||
*/ | ||
fun stopAllGauges() { | ||
stopGauge(cpuVitalReader) | ||
stopGauge(memoryVitalReader) | ||
stopGauge(fpsVitalReader) | ||
} | ||
|
||
private fun startGauge(reader: VitalReader) { | ||
synchronized(reader) { | ||
// Close the gauge if it exists before. | ||
val metricName = reader.metricName() | ||
gaugesByMetricName[metricName]?.close() | ||
reader.start() | ||
meter.gaugeBuilder(metricName).apply { | ||
reader.unit()?.let { unit -> | ||
setUnit(unit) | ||
} | ||
}.buildWithCallback { observableDoubleMeasurement -> | ||
reader.readVitalData()?.let { data -> | ||
observableDoubleMeasurement.record(data) | ||
} | ||
}.also { observableDoubleGauge -> | ||
gaugesByMetricName[metricName] = observableDoubleGauge | ||
} | ||
} | ||
} | ||
|
||
private fun stopGauge(reader: VitalReader) { | ||
synchronized(reader) { | ||
reader.stop() | ||
gaugesByMetricName[reader.metricName()]?.close() | ||
gaugesByMetricName[reader.metricName()] = NoOpObservableDoubleGauge() | ||
} | ||
} | ||
|
||
companion object { | ||
|
||
/** | ||
* Creates an instance of [DatadogMeter] with given configuration. | ||
*/ | ||
fun create(datadogExporterConfiguration: DatadogExporterConfiguration): DatadogMeter { | ||
val sdkMeterProvider = SdkMeterProvider.builder() | ||
.registerMetricReader( | ||
PeriodicMetricReader.builder(DatadogMetricExporter(datadogExporterConfiguration)) | ||
.setInterval(datadogExporterConfiguration.intervalInSeconds, TimeUnit.SECONDS) | ||
.build() | ||
) | ||
.build() | ||
|
||
val openTelemetry: OpenTelemetry = OpenTelemetrySdk.builder() | ||
.setMeterProvider(sdkMeterProvider) | ||
.build() | ||
val meter = openTelemetry.getMeter(METER_INSTRUMENTATION_SCOPE_NAME) | ||
return DatadogMeter(meter) | ||
} | ||
|
||
private const val METER_INSTRUMENTATION_SCOPE_NAME = "datadog.open-telemetry" | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
tools/benchmark/src/main/java/com/datadog/benchmark/exporter/DatadogMetricExporter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.datadog.benchmark.exporter | ||
|
||
import android.os.Build | ||
import com.datadog.benchmark.DatadogExporterConfiguration | ||
import com.datadog.benchmark.internal.DatadogHttpClient | ||
import com.datadog.benchmark.internal.model.MetricContext | ||
import io.opentelemetry.sdk.common.CompletableResultCode | ||
import io.opentelemetry.sdk.metrics.InstrumentType | ||
import io.opentelemetry.sdk.metrics.data.AggregationTemporality | ||
import io.opentelemetry.sdk.metrics.data.MetricData | ||
import io.opentelemetry.sdk.metrics.export.MetricExporter | ||
|
||
internal class DatadogMetricExporter(datadogExporterConfiguration: DatadogExporterConfiguration) : MetricExporter { | ||
|
||
private val metricContext: MetricContext = MetricContext( | ||
deviceModel = Build.MODEL, | ||
osVersion = Build.VERSION.RELEASE, | ||
run = datadogExporterConfiguration.run ?: DEFAULT_RUN_NAME, | ||
applicationId = datadogExporterConfiguration.applicationId ?: DEFAULT_APPLICATION_ID, | ||
intervalInSeconds = datadogExporterConfiguration.intervalInSeconds | ||
) | ||
private val metricHttpClient: DatadogHttpClient = DatadogHttpClient( | ||
metricContext, | ||
datadogExporterConfiguration | ||
) | ||
|
||
override fun getAggregationTemporality(instrumentType: InstrumentType): AggregationTemporality { | ||
return AggregationTemporality.DELTA | ||
} | ||
|
||
override fun export(metrics: MutableCollection<MetricData>): CompletableResultCode { | ||
// currently no aggregation is required | ||
metricHttpClient.uploadMetric(metrics.toList()) | ||
return CompletableResultCode.ofSuccess() | ||
} | ||
|
||
override fun flush(): CompletableResultCode { | ||
// currently do nothing | ||
return CompletableResultCode.ofSuccess() | ||
} | ||
|
||
override fun shutdown(): CompletableResultCode { | ||
// currently do nothing | ||
return CompletableResultCode.ofSuccess() | ||
} | ||
|
||
companion object { | ||
private const val DEFAULT_RUN_NAME = "unknown run" | ||
private const val DEFAULT_APPLICATION_ID = "unassigned application id" | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
tools/benchmark/src/main/java/com/datadog/benchmark/exporter/LogsMetricExporter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2016-Present Datadog, Inc. | ||
*/ | ||
|
||
package com.datadog.benchmark.exporter | ||
|
||
import android.os.Build | ||
import android.util.Log | ||
import com.datadog.benchmark.DatadogExporterConfiguration | ||
import com.datadog.benchmark.internal.MetricRequestBodyBuilder | ||
import com.datadog.benchmark.internal.model.MetricContext | ||
import io.opentelemetry.sdk.common.CompletableResultCode | ||
import io.opentelemetry.sdk.metrics.InstrumentType | ||
import io.opentelemetry.sdk.metrics.data.AggregationTemporality | ||
import io.opentelemetry.sdk.metrics.data.MetricData | ||
import io.opentelemetry.sdk.metrics.export.MetricExporter | ||
|
||
internal class LogsMetricExporter(datadogExporterConfiguration: DatadogExporterConfiguration) : MetricExporter { | ||
|
||
private val metricContext: MetricContext = MetricContext( | ||
deviceModel = Build.MODEL, | ||
osVersion = Build.VERSION.RELEASE, | ||
run = datadogExporterConfiguration.run ?: DEFAULT_RUN_NAME, | ||
applicationId = datadogExporterConfiguration.applicationId ?: DEFAULT_APPLICATION_ID, | ||
intervalInSeconds = datadogExporterConfiguration.intervalInSeconds | ||
) | ||
|
||
override fun getAggregationTemporality(instrumentType: InstrumentType): AggregationTemporality { | ||
return AggregationTemporality.DELTA | ||
} | ||
|
||
override fun export(metrics: Collection<MetricData>): CompletableResultCode { | ||
print(metrics) | ||
return CompletableResultCode.ofSuccess() | ||
} | ||
|
||
private fun print(metrics: Collection<MetricData>) { | ||
MetricRequestBodyBuilder(metricContext).buildJsonElement(metrics.toList()).toString().apply { | ||
Log.i("LogsMetricExporter", this) | ||
} | ||
} | ||
|
||
override fun flush(): CompletableResultCode { | ||
// currently do nothing | ||
return CompletableResultCode.ofSuccess() | ||
} | ||
|
||
override fun shutdown(): CompletableResultCode { | ||
// currently do nothing | ||
return CompletableResultCode.ofSuccess() | ||
} | ||
|
||
companion object { | ||
private const val DEFAULT_RUN_NAME = "log run" | ||
private const val DEFAULT_APPLICATION_ID = "unassigned application id" | ||
} | ||
} |
91 changes: 91 additions & 0 deletions
91
tools/benchmark/src/main/java/com/datadog/benchmark/ext/FileExt.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2016-Present Datadog, Inc. | ||
*/ | ||
|
||
package com.datadog.benchmark.ext | ||
|
||
import java.io.File | ||
import java.nio.charset.Charset | ||
|
||
/* | ||
* The java.lang.File class throws a SecurityException for the following calls: | ||
* - canRead() | ||
* - canWrite() | ||
* - delete() | ||
* - exists() | ||
* - isFile() | ||
* - isDir() | ||
* - listFiles(…) | ||
* - length() | ||
* The following set of extension make sure that every call to those methods | ||
* is safeguarded to avoid crashing the customer's app. | ||
*/ | ||
|
||
@Suppress("TooGenericExceptionCaught", "SwallowedException") | ||
private fun <T> File.safeCall( | ||
default: T, | ||
lambda: File.() -> T | ||
): T { | ||
return try { | ||
lambda() | ||
} catch (e: SecurityException) { | ||
default | ||
} catch (e: Exception) { | ||
default | ||
} | ||
} | ||
|
||
/** | ||
* Non-throwing version of [File.canRead]. If exception happens, false is returned. | ||
*/ | ||
|
||
fun File.canReadSafe(): Boolean { | ||
return safeCall(default = false) { | ||
@Suppress("UnsafeThirdPartyFunctionCall") | ||
canRead() | ||
} | ||
} | ||
|
||
/** | ||
* Non-throwing version of [File.exists]. If exception happens, false is returned. | ||
*/ | ||
|
||
fun File.existsSafe(): Boolean { | ||
return safeCall(default = false) { | ||
@Suppress("UnsafeThirdPartyFunctionCall") | ||
exists() | ||
} | ||
} | ||
|
||
/** | ||
* Non-throwing version of [File.readText]. If exception happens, null is returned. | ||
*/ | ||
|
||
fun File.readTextSafe(charset: Charset = Charsets.UTF_8): String? { | ||
return if (existsSafe() && canReadSafe()) { | ||
safeCall(default = null) { | ||
@Suppress("UnsafeThirdPartyFunctionCall") | ||
readText(charset) | ||
} | ||
} else { | ||
null | ||
} | ||
} | ||
|
||
/** | ||
* Non-throwing version of [File.readLines]. If exception happens, null is returned. | ||
*/ | ||
fun File.readLinesSafe( | ||
charset: Charset = Charsets.UTF_8 | ||
): List<String>? { | ||
return if (existsSafe() && canReadSafe()) { | ||
safeCall(default = null) { | ||
@Suppress("UnsafeThirdPartyFunctionCall") | ||
readLines(charset) | ||
} | ||
} else { | ||
null | ||
} | ||
} |
Oops, something went wrong.