add job and instance target labels

This commit is contained in:
Martin Ptáček
2023-07-29 21:12:08 +02:00
parent e9a5947e63
commit dffb10d748
9 changed files with 108 additions and 48 deletions

View File

@ -7,11 +7,11 @@
<deviceKey> <deviceKey>
<Key> <Key>
<type value="VIRTUAL_DEVICE_PATH" /> <type value="VIRTUAL_DEVICE_PATH" />
<value value="$USER_HOME$/.android/avd/new3_device.avd" /> <value value="$USER_HOME$/.android/avd/new4_device.avd" />
</Key> </Key>
</deviceKey> </deviceKey>
</Target> </Target>
</targetSelectedWithDropDown> </targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-07-27T06:47:49.277741303Z" /> <timeTargetWasSelectedWithDropDown value="2023-07-29T19:05:07.449016617Z" />
</component> </component>
</project> </project>

View File

@ -17,6 +17,8 @@ private const val defaultPrometheusServerPort: Int = 10101
private const val defaultRemoteWriteScrapeInterval: Int = 30 // seconds private const val defaultRemoteWriteScrapeInterval: Int = 30 // seconds
private const val defaultRemoteWriteMaxSamplesPerExport: Int = 60 // seconds private const val defaultRemoteWriteMaxSamplesPerExport: Int = 60 // seconds
private const val defaultRemoteWriteExportInterval: Int = 120 // seconds private const val defaultRemoteWriteExportInterval: Int = 120 // seconds
private const val defaultRemoteWriteJobLabel: String = "test job"
private const val defaultRemoteWriteInstanceLabel: String = "test instance"
// serialization classes for parsing YAML configuration file // serialization classes for parsing YAML configuration file
@Serializable @Serializable
@ -41,6 +43,8 @@ data class PromConfigFile(
?: defaultRemoteWriteMaxSamplesPerExport).toString(), ?: defaultRemoteWriteMaxSamplesPerExport).toString(),
remoteWriteExportInterval = (this.remote_write?.export_interval remoteWriteExportInterval = (this.remote_write?.export_interval
?: defaultRemoteWriteExportInterval).toString(), ?: defaultRemoteWriteExportInterval).toString(),
remoteWriteInstanceLabel = this.remote_write?.instance ?: defaultRemoteWriteInstanceLabel,
remoteWriteJobLabel = this.remote_write?.job ?: defaultRemoteWriteJobLabel,
) )
} }
} }
@ -65,6 +69,8 @@ data class RemoteWriteConfigFile(
val remote_write_endpoint: String? = null, val remote_write_endpoint: String? = null,
val max_samples_per_export: Int? = null, val max_samples_per_export: Int? = null,
val export_interval: Int? = null, val export_interval: Int? = null,
val job: String? = null,
val instance: String? = null,
) )
// configuration of a work manager worker // configuration of a work manager worker
@ -81,6 +87,8 @@ data class PromConfiguration(
val remoteWriteEndpoint: String = "", val remoteWriteEndpoint: String = "",
val remoteWriteExportInterval: String = defaultRemoteWriteExportInterval.toString(), val remoteWriteExportInterval: String = defaultRemoteWriteExportInterval.toString(),
val remoteWriteMaxSamplesPerExport: String = defaultRemoteWriteMaxSamplesPerExport.toString(), val remoteWriteMaxSamplesPerExport: String = defaultRemoteWriteMaxSamplesPerExport.toString(),
val remoteWriteInstanceLabel: String = defaultRemoteWriteInstanceLabel,
val remoteWriteJobLabel: String = defaultRemoteWriteJobLabel,
) { ) {
fun toStructuredText(): String { fun toStructuredText(): String {
@ -98,6 +106,8 @@ data class PromConfiguration(
export_interval: $remoteWriteExportInterval export_interval: $remoteWriteExportInterval
max_samples_per_export: $remoteWriteMaxSamplesPerExport max_samples_per_export: $remoteWriteMaxSamplesPerExport
remote_write_endpoint: "$remoteWriteEndpoint" remote_write_endpoint: "$remoteWriteEndpoint"
instance: "$remoteWriteInstanceLabel"
job: "$remoteWriteJobLabel"
""".trimIndent() """.trimIndent()
} }

View File

@ -335,10 +335,10 @@ private fun RemoteWritePage(
) )
TextField( TextField(
value = uiState.promConfig.remoteWriteMaxSamplesPerExport, value = uiState.promConfig.remoteWriteInstanceLabel,
singleLine = true, singleLine = true,
onValueChange = { onValueChange = {
promViewModel.updatePromConfig(UpdatePromConfig.RemoteWriteMaxSamplesPerExport, it) promViewModel.updatePromConfig(UpdatePromConfig.RemoteWriteInstanceLabel, it)
}, },
label = { label = {
Text(text = "Target label instance") Text(text = "Target label instance")
@ -347,10 +347,10 @@ private fun RemoteWritePage(
) )
TextField( TextField(
value = uiState.promConfig.remoteWriteExportInterval, value = uiState.promConfig.remoteWriteJobLabel,
singleLine = true, singleLine = true,
onValueChange = { onValueChange = {
promViewModel.updatePromConfig(UpdatePromConfig.RemoteWriteExportInterval, it) promViewModel.updatePromConfig(UpdatePromConfig.RemoteWriteJobLabel, it)
}, },
label = { label = {
Text(text = "Target label job") Text(text = "Target label job")

View File

@ -42,6 +42,8 @@ enum class UpdatePromConfig {
RemoteWriteEndpoint, RemoteWriteEndpoint,
RemoteWriteExportInterval, RemoteWriteExportInterval,
RemoteWriteMaxSamplesPerExport, RemoteWriteMaxSamplesPerExport,
RemoteWriteJobLabel,
RemoteWriteInstanceLabel,
} }
enum class ExporterState { enum class ExporterState {
@ -388,6 +390,23 @@ class PromViewModel : ViewModel() {
) )
) )
} }
UpdatePromConfig.RemoteWriteInstanceLabel -> _uiState.update {current ->
current.copy(
promConfig = current.promConfig.copy(
remoteWriteInstanceLabel = value as String
)
)
}
UpdatePromConfig.RemoteWriteJobLabel -> _uiState.update {current ->
current.copy(
promConfig = current.promConfig.copy(
remoteWriteJobLabel = value as String
)
)
}
} }
} }
} }

View File

@ -74,6 +74,10 @@ class PromWorker(
collectorRegistry = collectorRegistry, collectorRegistry = collectorRegistry,
exportInterval = config.remoteWriteExportInterval.toInt(), exportInterval = config.remoteWriteExportInterval.toInt(),
maxSamplesPerExport = config.remoteWriteMaxSamplesPerExport.toInt(), maxSamplesPerExport = config.remoteWriteMaxSamplesPerExport.toInt(),
targetLabels = mapOf(
"job" to config.remoteWriteJobLabel,
"instance" to config.remoteWriteInstanceLabel,
),
) { context } ) { context }
) )
launch { launch {

View File

@ -142,6 +142,7 @@ class PushProxClient(private val pushProxConfig: PushProxConfig) {
Log.d(TAG, "Polling finished") Log.d(TAG, "Polling finished")
} else { } else {
Log.d(TAG, "Skipping poll because network not available") Log.d(TAG, "Skipping poll because network not available")
throw Exception("Device is not connected to any network")
} }
} }
@ -177,24 +178,30 @@ class PushProxClient(private val pushProxConfig: PushProxConfig) {
} }
// push metrics to pushprox // push metrics to pushprox
try { // only try to push metrics if device is connected to the network
val scrapeId: String = getIdFromResponseBody(pollResponseBody) if (Util.deviceIsConnectedToInternet(context.getContext())){
val pushRequestBody: String = composeRequestBody(scrapedMetrics, scrapeId) try {
val scrapeId: String = getIdFromResponseBody(pollResponseBody)
val pushRequestBody: String = composeRequestBody(scrapedMetrics, scrapeId)
context.client.request(context.pushUrl) { context.client.request(context.pushUrl) {
method = HttpMethod.Post method = HttpMethod.Post
setBody(pushRequestBody) setBody(pushRequestBody)
} }
pushProxConfig.countSuccessfulScrape() pushProxConfig.countSuccessfulScrape()
} catch (e: Exception) { } catch (e: Exception) {
if (e is CancellationException) { if (e is CancellationException) {
throw e throw e
}
counters.pushError()
Log.v(TAG, "Push exception $e")
return
} }
}else{
counters.pushError() counters.pushError()
Log.v(TAG, "Push exception $e") Log.d(TAG, "device is not connected to any network")
return
} }
} }

View File

@ -81,12 +81,13 @@ data class RemoteWriteConfiguration(
val collectorRegistry: CollectorRegistry, val collectorRegistry: CollectorRegistry,
val maxSamplesPerExport: Int, val maxSamplesPerExport: Int,
val exportInterval: Int, val exportInterval: Int,
val targetLabels: Map<String, String>,
val getContext: () -> Context, val getContext: () -> Context,
) )
class RemoteWriteSender(private val config: RemoteWriteConfiguration) { class RemoteWriteSender(private val config: RemoteWriteConfiguration) {
private val lastTimeRingBuffer = LastTimeRingBuffer(config.scrapeInterval) private val lastTimeRingBuffer = LastTimeRingBuffer(config.scrapeInterval)
private val storage: RemoteWriteSenderStorage = RemoteWriteSenderSimpleMemoryStorage() private val storage: RemoteWriteSenderStorage = RemoteWriteSenderSimpleMemoryStorage(config.targetLabels)
private var scrapesAreBeingSent: Boolean = false private var scrapesAreBeingSent: Boolean = false
private lateinit var client: HttpClient private lateinit var client: HttpClient
private var lastTimeRemoteWriteSent: Long = 0 private var lastTimeRemoteWriteSent: Long = 0
@ -228,33 +229,40 @@ class RemoteWriteSender(private val config: RemoteWriteConfiguration) {
private suspend fun sendRequestToRemoteWrite(body: ByteArray, numOfMetricScrapes: Int) { private suspend fun sendRequestToRemoteWrite(body: ByteArray, numOfMetricScrapes: Int) {
Log.d(TAG, "Exporting remote write to prometheus now") Log.d(TAG, "Exporting remote write to prometheus now")
val response = client.post(config.remoteWriteEndpoint) {
setBody(body)
headers {
append(HttpHeaders.ContentEncoding, "snappy")
append(HttpHeaders.ContentType, "application/protobuf")
append(HttpHeaders.UserAgent, "Prometheus Android Exporter")
header("X-Prometheus-Remote-Write-Version", "0.1.0")
}
}
Log.d(TAG, "Response status: ${response.status}") // only send the request if device is online, otherwise throw exception
// ExponentialBackoff will catch the exception
when (response.status) { if (Util.deviceIsConnectedToInternet(config.getContext())){
HttpStatusCode.NoContent -> { val response = client.post(config.remoteWriteEndpoint) {
// this export was successful setBody(body)
storage.removeNumberOfScrapedSamples(numOfMetricScrapes) headers {
append(HttpHeaders.ContentEncoding, "snappy")
append(HttpHeaders.ContentType, "application/protobuf")
append(HttpHeaders.UserAgent, "Prometheus Android Exporter")
header("X-Prometheus-Remote-Write-Version", "0.1.0")
}
} }
HttpStatusCode.BadRequest -> { Log.d(TAG, "Response status: ${response.status}")
// probably some error or race condition has occured
// give up trying to send this data
storage.removeNumberOfScrapedSamples(numOfMetricScrapes)
}
else -> { when (response.status) {
throw TryExportMetricsAgainException("Status code: ${response.status.description}") HttpStatusCode.NoContent -> {
// this export was successful
storage.removeNumberOfScrapedSamples(numOfMetricScrapes)
}
HttpStatusCode.BadRequest -> {
// probably some error or race condition has occured
// give up trying to send this data
storage.removeNumberOfScrapedSamples(numOfMetricScrapes)
}
else -> {
throw TryExportMetricsAgainException("Status code: ${response.status.description}")
}
} }
}else{
throw TryExportMetricsAgainException("Device is not connected to any network")
} }
} }
} }

View File

@ -12,7 +12,7 @@ private typealias ConverterHashMap = HashMap<List<TimeSeriesLabel>, MutableList<
private const val TAG: String = "REMOTE_WRITE_SENDER_MEMORY_SIMPLE_STORAGE" private const val TAG: String = "REMOTE_WRITE_SENDER_MEMORY_SIMPLE_STORAGE"
class RemoteWriteSenderSimpleMemoryStorage : RemoteWriteSenderStorage() { class RemoteWriteSenderSimpleMemoryStorage(val targetLabels: Map<String, String>) : RemoteWriteSenderStorage() {
private val data: Queue<MetricsScrape> = LinkedList() private val data: Queue<MetricsScrape> = LinkedList()
private fun filterExpiredMetrics(metrics: MutableList<MetricsScrape>) { private fun filterExpiredMetrics(metrics: MutableList<MetricsScrape>) {
@ -107,6 +107,17 @@ class RemoteWriteSenderSimpleMemoryStorage : RemoteWriteSenderStorage() {
return hashmapToProtobufWriteRequest(hashmap) return hashmapToProtobufWriteRequest(hashmap)
} }
private fun addTargetLabels(labels: MutableList<TimeSeriesLabel>){
targetLabels.forEach {
val label = TimeSeriesLabel(
value = it.value,
name = it.key,
)
labels.add(label)
}
}
private fun processStorageTimeSeries(hashMap: ConverterHashMap, timeSeries: StorageTimeSeries) { private fun processStorageTimeSeries(hashMap: ConverterHashMap, timeSeries: StorageTimeSeries) {
// add remote write label to labels // add remote write label to labels
@ -114,6 +125,7 @@ class RemoteWriteSenderSimpleMemoryStorage : RemoteWriteSenderStorage() {
// and those scraped by Remote Write // and those scraped by Remote Write
val labels: MutableList<TimeSeriesLabel> = timeSeries.labels.toMutableList() val labels: MutableList<TimeSeriesLabel> = timeSeries.labels.toMutableList()
labels.add(remoteWriteLabel) labels.add(remoteWriteLabel)
addTargetLabels(labels)
val immutableLabels: List<TimeSeriesLabel> = labels.toList() val immutableLabels: List<TimeSeriesLabel> = labels.toList()
if (hashMap[immutableLabels] == null) { if (hashMap[immutableLabels] == null) {

View File

@ -42,7 +42,7 @@ remote_write:
export_interval: 60 # default export_interval: 60 # default
# instance and job target labels, no defaults are provided # instance and job target labels, defaults are provided
# string values # string values
instance: instance: "test instance"
job: job: "test job"