mirror of
https://github.com/mii443/prometheus-android-exporter.git
synced 2025-08-22 15:15:35 +00:00
add job and instance target labels
This commit is contained in:
4
client/.idea/deploymentTargetDropDown.xml
generated
4
client/.idea/deploymentTargetDropDown.xml
generated
@ -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>
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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"
|
||||||
|
Reference in New Issue
Block a user