mirror of
https://github.com/mii443/prometheus-android-exporter.git
synced 2025-08-22 23:25:40 +00:00
pushprox worker kinda works
This commit is contained in:
@ -73,6 +73,9 @@ dependencies {
|
|||||||
def nav_version = "2.5.3"
|
def nav_version = "2.5.3"
|
||||||
implementation("androidx.navigation:navigation-compose:$nav_version")
|
implementation("androidx.navigation:navigation-compose:$nav_version")
|
||||||
|
|
||||||
|
// custom - work manager
|
||||||
|
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
||||||
|
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.10.0'
|
implementation 'androidx.core:core-ktx:1.10.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
||||||
@ -87,7 +90,3 @@ dependencies {
|
|||||||
debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
|
debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
|
||||||
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
|
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
|
|
||||||
}
|
|
@ -16,7 +16,6 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:theme="@style/Theme.PrometheusAndroidExporter">
|
android:theme="@style/Theme.PrometheusAndroidExporter">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
@ -24,6 +23,12 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name="androidx.work.impl.foreground.SystemForegroundService"
|
||||||
|
android:foregroundServiceType="location|microphone"
|
||||||
|
tools:node="merge" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.birdthedeveloper.prometheus.android.prometheus.android.exporter
|
||||||
|
|
||||||
|
import androidx.work.Data
|
||||||
|
|
||||||
|
data class PushProxConfig(
|
||||||
|
val pushProxUrl : String,
|
||||||
|
val pushProxFqdn : String,
|
||||||
|
){
|
||||||
|
companion object{
|
||||||
|
fun fromData(data : Data) : PushProxConfig{
|
||||||
|
return PushProxConfig(
|
||||||
|
data.getString("0")!!,
|
||||||
|
data.getString("1")!!,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toData() : Data {
|
||||||
|
return Data.Builder()
|
||||||
|
.putString("0", pushProxUrl)
|
||||||
|
.putString("1", pushProxFqdn)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class PromServerConfig(
|
||||||
|
//TODO implement this
|
||||||
|
val dummy : String,
|
||||||
|
)
|
@ -176,7 +176,7 @@ private fun PushProxPage(
|
|||||||
){
|
){
|
||||||
val uiState : PromUiState by promViewModel.uiState.collectAsState()
|
val uiState : PromUiState by promViewModel.uiState.collectAsState()
|
||||||
|
|
||||||
// if showDialogText == "", do not display alert dialog
|
// if showDialogText is empty string, do not display alert dialog
|
||||||
val showDialogText : MutableState<String> = remember { mutableStateOf("") }
|
val showDialogText : MutableState<String> = remember { mutableStateOf("") }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
@ -193,8 +193,20 @@ private fun PushProxPage(
|
|||||||
modifier = Modifier.padding(bottom = 30.dp)
|
modifier = Modifier.padding(bottom = 30.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(uiState.pushProxTurnedOn){
|
||||||
|
Text(
|
||||||
|
text = """
|
||||||
|
To edit PushProx proxy URL or FQDN, turn it off first.
|
||||||
|
""".trimIndent(),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.padding(bottom = 12.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
TextField(
|
TextField(
|
||||||
value = uiState.fqdn,
|
value = uiState.fqdn,
|
||||||
|
singleLine = true,
|
||||||
|
enabled = !uiState.pushProxTurnedOn,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
promViewModel.updatePushProxFQDN(it)
|
promViewModel.updatePushProxFQDN(it)
|
||||||
},
|
},
|
||||||
@ -206,6 +218,8 @@ private fun PushProxPage(
|
|||||||
|
|
||||||
TextField(
|
TextField(
|
||||||
value = uiState.pushProxURL,
|
value = uiState.pushProxURL,
|
||||||
|
singleLine = true,
|
||||||
|
enabled = !uiState.pushProxTurnedOn,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
promViewModel.updatePushProxURL(it)
|
promViewModel.updatePushProxURL(it)
|
||||||
},
|
},
|
||||||
|
@ -1,34 +1,19 @@
|
|||||||
package com.birdthedeveloper.prometheus.android.prometheus.android.exporter
|
package com.birdthedeveloper.prometheus.android.prometheus.android.exporter
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.BoringLayout.Metrics
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material.Button
|
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Surface
|
import androidx.compose.material.Surface
|
||||||
import androidx.compose.material.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.birdthedeveloper.prometheus.android.prometheus.android.exporter.ui.theme.PrometheusAndroidExporterTheme
|
import com.birdthedeveloper.prometheus.android.prometheus.android.exporter.ui.theme.PrometheusAndroidExporterTheme
|
||||||
import io.prometheus.client.Collector
|
|
||||||
import io.prometheus.client.CollectorRegistry
|
|
||||||
import io.prometheus.client.exporter.common.TextFormat
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.io.StringWriter
|
|
||||||
|
|
||||||
//TODO
|
//TODO
|
||||||
//https://www.geeksforgeeks.org/how-to-launch-an-application-automatically-on-system-boot-up-in-android/
|
//https://www.geeksforgeeks.org/how-to-launch-an-application-automatically-on-system-boot-up-in-android/
|
||||||
@ -39,7 +24,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
val promViewModel: PromViewModel = ViewModelProvider(this)[PromViewModel::class.java]
|
val promViewModel: PromViewModel = ViewModelProvider(this)[PromViewModel::class.java]
|
||||||
promViewModel.setApplicationContext { this.applicationContext }
|
promViewModel.initializeWithApplicationContext { this.applicationContext }
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
PrometheusAndroidExporterTheme {
|
PrometheusAndroidExporterTheme {
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
package com.birdthedeveloper.prometheus.android.prometheus.android.exporter
|
||||||
|
|
@ -4,9 +4,16 @@ import android.content.Context
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.Data
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.NetworkType
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.Operation
|
||||||
|
import androidx.work.OutOfQuotaPolicy
|
||||||
|
import androidx.work.WorkManager
|
||||||
import io.prometheus.client.CollectorRegistry
|
import io.prometheus.client.CollectorRegistry
|
||||||
import io.prometheus.client.exporter.common.TextFormat
|
import io.prometheus.client.exporter.common.TextFormat
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
@ -27,28 +34,28 @@ data class PromUiState(
|
|||||||
val serverTurnedOn : Boolean = false,
|
val serverTurnedOn : Boolean = false,
|
||||||
val pushProxTurnedOn : Boolean = false,
|
val pushProxTurnedOn : Boolean = false,
|
||||||
val serverPort : Int? = null, // if null, use default port
|
val serverPort : Int? = null, // if null, use default port
|
||||||
val fqdn : String = "",
|
val fqdn : String = "test.example.com",
|
||||||
val pushProxURL : String = "",
|
val pushProxURL : String = "143.42.59.63:8080",
|
||||||
val configFileState : ConfigFileState = ConfigFileState.LOADING,
|
val configFileState : ConfigFileState = ConfigFileState.LOADING,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val TAG : String = "PROMVIEWMODEL"
|
private val TAG : String = "PROMVIEWMODEL"
|
||||||
|
|
||||||
class PromViewModel(): ViewModel() {
|
class PromViewModel(): ViewModel() {
|
||||||
|
// constants
|
||||||
private val DEFAULT_SERVER_PORT : Int = 10101 //TODO register with prometheus community
|
private val DEFAULT_SERVER_PORT : Int = 10101 //TODO register with prometheus community
|
||||||
|
private val PROM_UNIQUE_WORK : String = "prom_unique_job"
|
||||||
|
|
||||||
|
|
||||||
private val _uiState = MutableStateFlow(PromUiState())
|
private val _uiState = MutableStateFlow(PromUiState())
|
||||||
val uiState : StateFlow<PromUiState> = _uiState.asStateFlow()
|
val uiState : StateFlow<PromUiState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
// app - level components
|
private lateinit var getContext: () -> Context
|
||||||
private val collectorRegistry: CollectorRegistry = CollectorRegistry()
|
|
||||||
private lateinit var pushProxClient : PushProxClient
|
|
||||||
private val prometheusServer : PrometheusServer = PrometheusServer()
|
|
||||||
private lateinit var metricsEngine: MetricsEngine
|
|
||||||
private lateinit var androidCustomExporter: AndroidCustomExporter
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Log.v(TAG, "Checking for configuration file")
|
Log.v(TAG, "Checking for configuration file")
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
//TODO check for configuration file
|
||||||
delay(1000)
|
delay(1000)
|
||||||
_uiState.update { current ->
|
_uiState.update { current ->
|
||||||
current.copy(configFileState = ConfigFileState.MISSING)
|
current.copy(configFileState = ConfigFileState.MISSING)
|
||||||
@ -60,13 +67,8 @@ class PromViewModel(): ViewModel() {
|
|||||||
return DEFAULT_SERVER_PORT
|
return DEFAULT_SERVER_PORT
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setApplicationContext(getContext : () -> Context){
|
fun initializeWithApplicationContext(getContext : () -> Context){
|
||||||
// initalize app - level components
|
this.getContext = getContext
|
||||||
metricsEngine = MetricsEngine(getContext())
|
|
||||||
androidCustomExporter = AndroidCustomExporter(metricsEngine).register(collectorRegistry)
|
|
||||||
pushProxClient = PushProxClient(collectorRegistry)
|
|
||||||
//TODO somehow this PushProx definition does not work here
|
|
||||||
// -> fix it
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateTabIndex(index : Int){
|
fun updateTabIndex(index : Int){
|
||||||
@ -85,22 +87,16 @@ class PromViewModel(): ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun performScrape() : String {
|
|
||||||
val writer : StringWriter = StringWriter()
|
|
||||||
TextFormat.write004(writer, collectorRegistry.metricFamilySamples())
|
|
||||||
|
|
||||||
return writer.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
// if result is not null, it contains an error message
|
// if result is not null, it contains an error message
|
||||||
fun turnServerOn() : String?{
|
fun turnServerOn() : String?{
|
||||||
try{
|
try{
|
||||||
prometheusServer.startInBackground(
|
//TODO rewrite asap
|
||||||
PrometheusServerConfig(
|
// prometheusServer.startInBackground(
|
||||||
getPromServerPort(),
|
// PrometheusServerConfig(
|
||||||
::performScrape
|
// getPromServerPort(),
|
||||||
)
|
// ::performScrape
|
||||||
)
|
// )
|
||||||
|
// )
|
||||||
}catch(e : Exception){
|
}catch(e : Exception){
|
||||||
Log.v(TAG, e.toString())
|
Log.v(TAG, e.toString())
|
||||||
return "Prometheus server failed!"
|
return "Prometheus server failed!"
|
||||||
@ -119,8 +115,8 @@ class PromViewModel(): ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun validatePushProxSettings() : String? {
|
private fun validatePushProxSettings() : String? {
|
||||||
val fqdn = _uiState.value.fqdn.trim()
|
val fqdn = _uiState.value.fqdn.trim().trim('\n')
|
||||||
val url = _uiState.value.pushProxURL.trim()
|
val url = _uiState.value.pushProxURL.trim().trim('\n')
|
||||||
|
|
||||||
if( fqdn.isEmpty() ) return "Fully Qualified Domain Name cannot be empty!"
|
if( fqdn.isEmpty() ) return "Fully Qualified Domain Name cannot be empty!"
|
||||||
if( url.isEmpty() ) return "PushProx URL cannot be empty!"
|
if( url.isEmpty() ) return "PushProx URL cannot be empty!"
|
||||||
@ -128,25 +124,42 @@ class PromViewModel(): ViewModel() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun launchPushProxUsingWorkManager(){
|
||||||
|
val workManagerInstance = WorkManager.getInstance(getContext())
|
||||||
|
|
||||||
|
// worker configuration
|
||||||
|
val inputData : Data = PushProxConfig(
|
||||||
|
pushProxFqdn = _uiState.value.fqdn,
|
||||||
|
pushProxUrl = _uiState.value.pushProxURL,
|
||||||
|
).toData()
|
||||||
|
|
||||||
|
// constraints
|
||||||
|
val constraints = Constraints.Builder()
|
||||||
|
.setRequiredNetworkType(NetworkType.NOT_REQUIRED)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
// setup worker request
|
||||||
|
val workerRequest = OneTimeWorkRequestBuilder<PushProxWorker>()
|
||||||
|
.setInputData(inputData)
|
||||||
|
.setConstraints(constraints)
|
||||||
|
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
// enqueue
|
||||||
|
workManagerInstance.beginUniqueWork(
|
||||||
|
PROM_UNIQUE_WORK,
|
||||||
|
ExistingWorkPolicy.KEEP,
|
||||||
|
workerRequest,
|
||||||
|
).enqueue()
|
||||||
|
}
|
||||||
|
|
||||||
// if result is not null, it contains an error message
|
// if result is not null, it contains an error message
|
||||||
fun turnPushProxOn() : String?{
|
fun turnPushProxOn() : String?{
|
||||||
|
|
||||||
val error : String? = validatePushProxSettings()
|
val error : String? = validatePushProxSettings()
|
||||||
if(error != null){
|
if(error != null){ return error }
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
try{
|
// idempotent call
|
||||||
pushProxClient.startBackground(
|
launchPushProxUsingWorkManager()
|
||||||
PushProxConfig(
|
|
||||||
performScrape = ::performScrape,
|
|
||||||
fqdn = _uiState.value.fqdn.trim(),
|
|
||||||
proxyUrl = _uiState.value.pushProxURL.trim(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}catch(e : Exception){
|
|
||||||
return "PushProx client failed!"
|
|
||||||
}
|
|
||||||
|
|
||||||
_uiState.update { current ->
|
_uiState.update { current ->
|
||||||
current.copy(
|
current.copy(
|
||||||
@ -158,7 +171,14 @@ class PromViewModel(): ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun turnPushProxOff(){
|
fun turnPushProxOff(){
|
||||||
//TODO implement
|
val workerManagerInstance = WorkManager.getInstance(getContext())
|
||||||
|
workerManagerInstance.cancelUniqueWork(PROM_UNIQUE_WORK)
|
||||||
|
|
||||||
|
_uiState.update {current ->
|
||||||
|
current.copy(
|
||||||
|
pushProxTurnedOn = false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updatePushProxURL(url : String){
|
fun updatePushProxURL(url : String){
|
||||||
|
@ -12,6 +12,7 @@ import io.ktor.client.statement.HttpResponse
|
|||||||
import io.ktor.http.HttpMethod
|
import io.ktor.http.HttpMethod
|
||||||
import io.prometheus.client.CollectorRegistry
|
import io.prometheus.client.CollectorRegistry
|
||||||
import io.prometheus.client.Counter
|
import io.prometheus.client.Counter
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@ -51,13 +52,6 @@ private class Counters(collectorRegistry: CollectorRegistry) {
|
|||||||
fun pollError(){ pollErrorCounter.inc() }
|
fun pollError(){ pollErrorCounter.inc() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration object for this pushprox client
|
|
||||||
data class PushProxConfig(
|
|
||||||
val fqdn: String,
|
|
||||||
val proxyUrl: String,
|
|
||||||
val performScrape: suspend () -> String,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error in parsing HTTP header "Id" from HTTP request from Prometheus
|
// Error in parsing HTTP header "Id" from HTTP request from Prometheus
|
||||||
class PushProxIdParseException(message: String) : Exception(message)
|
class PushProxIdParseException(message: String) : Exception(message)
|
||||||
|
|
||||||
@ -68,31 +62,26 @@ data class PushProxContext(
|
|||||||
val pushUrl : String,
|
val pushUrl : String,
|
||||||
val backoff : StrategyBackoff<Unit>,
|
val backoff : StrategyBackoff<Unit>,
|
||||||
val fqdn : String,
|
val fqdn : String,
|
||||||
val performScrape: suspend () -> String,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is a stripped down kotlin implementation of github.com/prometheus-community/PushProx client
|
// This is a stripped down kotlin implementation of github.com/prometheus-community/PushProx client
|
||||||
class PushProxClient(
|
class PushProxClient(
|
||||||
private val collectorRegistry: CollectorRegistry
|
collectorRegistry: CollectorRegistry,
|
||||||
|
private val performScrape: suspend () -> String
|
||||||
) {
|
) {
|
||||||
private val counters : Counters = Counters(collectorRegistry)
|
private val counters : Counters = Counters(collectorRegistry)
|
||||||
private val retryInitialWaitSeconds : Int = 1
|
private val retryInitialWaitSeconds : Int = 1
|
||||||
private val retryMaxWaitSeconds : Int = 5
|
private val retryMaxWaitSeconds : Int = 5
|
||||||
private var isRunning : Boolean = false
|
|
||||||
|
|
||||||
// Use this function to start exporting metrics to pushprox in the background
|
// Use this function to start exporting metrics to pushprox in the background
|
||||||
fun startBackground(config: PushProxConfig) {
|
suspend fun startBackground(config: PushProxConfig) {
|
||||||
if(!isRunning){
|
|
||||||
isRunning = true
|
|
||||||
|
|
||||||
val client : HttpClient = HttpClient() //TODO close this thing
|
val client : HttpClient = HttpClient() //TODO close this thing
|
||||||
val context : PushProxContext = processConfig(client, config)
|
val context : PushProxContext = processConfig(client, config)
|
||||||
loop(context)
|
loop(context)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun processConfig(client : HttpClient, config : PushProxConfig) : PushProxContext {
|
private fun processConfig(client : HttpClient, config : PushProxConfig) : PushProxContext {
|
||||||
var modifiedProxyURL = config.proxyUrl.trim('/')
|
var modifiedProxyURL = config.pushProxUrl.trim('/')
|
||||||
|
|
||||||
if(
|
if(
|
||||||
!modifiedProxyURL.startsWith("http://") &&
|
!modifiedProxyURL.startsWith("http://") &&
|
||||||
@ -109,8 +98,7 @@ class PushProxClient(
|
|||||||
pollURL,
|
pollURL,
|
||||||
pushURL,
|
pushURL,
|
||||||
newBackoffFromFlags(),
|
newBackoffFromFlags(),
|
||||||
config.fqdn,
|
config.pushProxFqdn,
|
||||||
config.performScrape,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +131,6 @@ class PushProxClient(
|
|||||||
"X-Prometheus-Scrape-Timeout: 9.5\r\n"
|
"X-Prometheus-Scrape-Timeout: 9.5\r\n"
|
||||||
|
|
||||||
val result : String = httpHeaders + "\r\n" + scrapedMetrics
|
val result : String = httpHeaders + "\r\n" + scrapedMetrics
|
||||||
log("result", result)
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +139,7 @@ class PushProxClient(
|
|||||||
// perform scrape
|
// perform scrape
|
||||||
lateinit var scrapedMetrics : String
|
lateinit var scrapedMetrics : String
|
||||||
try {
|
try {
|
||||||
scrapedMetrics = context.performScrape()
|
scrapedMetrics = performScrape()
|
||||||
}catch(e : Exception){
|
}catch(e : Exception){
|
||||||
counters.scrapeError()
|
counters.scrapeError()
|
||||||
log("scrape exception", e.toString())
|
log("scrape exception", e.toString())
|
||||||
@ -173,7 +160,6 @@ class PushProxClient(
|
|||||||
log("push exception", e.toString())
|
log("push exception", e.toString())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun newBackoffFromFlags() : StrategyBackoff<Unit> {
|
private fun newBackoffFromFlags() : StrategyBackoff<Unit> {
|
||||||
@ -187,27 +173,27 @@ class PushProxClient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
//TODO migrate to work manager
|
//TODO migrate to work manager
|
||||||
private fun loop(context : PushProxContext) {
|
private suspend fun loop(context : PushProxContext) {
|
||||||
// fire and forget a new coroutine
|
var shouldContinue : Boolean = true
|
||||||
GlobalScope.launch {
|
while (shouldContinue) {
|
||||||
launch {
|
|
||||||
while (true) {
|
|
||||||
val job = launch {
|
|
||||||
log("pushprox main loop", "loop start")
|
log("pushprox main loop", "loop start")
|
||||||
var result = context.backoff.withRetries {
|
|
||||||
// register poll error using try-catch block
|
// register poll error using try-catch block
|
||||||
try {
|
try {
|
||||||
doPoll(context)
|
doPoll(context)
|
||||||
} catch (e: Exception) {
|
}catch(e : CancellationException){
|
||||||
|
shouldContinue = false
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
for(exception in e.suppressed){
|
||||||
|
if(exception is CancellationException){
|
||||||
|
shouldContinue = false
|
||||||
|
}
|
||||||
|
}
|
||||||
log("exception encountered!", e.toString())
|
log("exception encountered!", e.toString())
|
||||||
counters.pollError()
|
counters.pollError()
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
log("pushprox main loop", "loop end")
|
||||||
}
|
|
||||||
job.join() // wait for the job to finish
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
package com.birdthedeveloper.prometheus.android.prometheus.android.exporter
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.Data
|
||||||
|
import androidx.work.ForegroundInfo
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import io.prometheus.client.CollectorRegistry
|
||||||
|
import io.prometheus.client.exporter.common.TextFormat
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import java.io.StringWriter
|
||||||
|
|
||||||
|
private val TAG = "PUSH_PROX_WORKER"
|
||||||
|
|
||||||
|
class PushProxWorker(
|
||||||
|
private val context : Context,
|
||||||
|
parameters : WorkerParameters
|
||||||
|
): CoroutineWorker(context, parameters){
|
||||||
|
|
||||||
|
override suspend fun doWork():Result {
|
||||||
|
//TODO implement this
|
||||||
|
val cache: PushProxWorkerCache = PushProxWorkerCache.getInstance {
|
||||||
|
return@getInstance context
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
val pushProxConfig : PushProxConfig = PushProxConfig.fromData(inputData)
|
||||||
|
|
||||||
|
cache.startBackground(pushProxConfig)
|
||||||
|
|
||||||
|
}catch(e : Exception){
|
||||||
|
Log.v(TAG, e.toString())
|
||||||
|
return Result.failure()
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// thread-safe singleton
|
||||||
|
class PushProxWorkerCache private constructor(
|
||||||
|
private val getContext: () -> Context
|
||||||
|
){
|
||||||
|
private val collectorRegistry: CollectorRegistry = CollectorRegistry()
|
||||||
|
private val metricsEngine : MetricsEngine = MetricsEngine(getContext())
|
||||||
|
private val pushProxClient = PushProxClient(collectorRegistry, ::performScrape)
|
||||||
|
private lateinit var androidCustomExporter : AndroidCustomExporter
|
||||||
|
|
||||||
|
init {
|
||||||
|
Log.v(TAG, "Initializing WorkerCache")
|
||||||
|
androidCustomExporter = AndroidCustomExporter(metricsEngine).register(collectorRegistry)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun performScrape() : String{
|
||||||
|
val writer = StringWriter()
|
||||||
|
TextFormat.write004(writer, collectorRegistry.metricFamilySamples())
|
||||||
|
return writer.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun startBackground(pushProxConfig : PushProxConfig){
|
||||||
|
pushProxClient.startBackground(pushProxConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private var instance : PushProxWorkerCache? = null
|
||||||
|
|
||||||
|
fun getInstance(getContext: () -> Context) : PushProxWorkerCache {
|
||||||
|
if(instance == null){
|
||||||
|
synchronized(PushProxWorkerCache::class.java){
|
||||||
|
if (instance == null){
|
||||||
|
instance = PushProxWorkerCache(getContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user