mirror of
https://github.com/mii443/prometheus-android-exporter.git
synced 2025-08-22 15:15:35 +00:00
doPush impl almost done
This commit is contained in:
22
README.md
22
README.md
@ -1,2 +1,24 @@
|
||||
# prometheus-android-exporter
|
||||
Prometheus exporter for android
|
||||
|
||||
# Not public
|
||||
143.42.59.63:9090 - prometheus
|
||||
|
||||
# Client
|
||||
Android app written in kotlin using Jetpack Compose.
|
||||
|
||||
# Server
|
||||
|
||||
## TL;DR
|
||||
```
|
||||
cd ./server
|
||||
|
||||
# edit ansible_inventory
|
||||
|
||||
# To apply ansible playbook
|
||||
$ ansible-playbook ansible_playbook.yaml
|
||||
|
||||
# To only apply changed configuration
|
||||
$ ansible-playbook ansible_playbook.yaml --tags config
|
||||
```
|
||||
|
||||
|
@ -59,9 +59,8 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.0-RC"
|
||||
|
||||
// custom - ktor web client
|
||||
implementation("io.ktor:ktor-server-core:2.2.4")
|
||||
runtimeOnly("io.ktor:ktor-client-core:2.2.4")
|
||||
implementation("io.ktor:ktor-client-cio:2.2.4")
|
||||
implementation("io.ktor:ktor-client-core:2.3.0")
|
||||
implementation("io.ktor:ktor-client-cio:2.3.0")
|
||||
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
|
@ -18,6 +18,7 @@ import com.birdthedeveloper.prometheus.android.prometheus.android.exporter.ui.th
|
||||
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
|
||||
|
||||
@ -27,6 +28,7 @@ class MainActivity : ComponentActivity() {
|
||||
private lateinit var metricsEngine: MetricsEngine
|
||||
private lateinit var customExporter: AndroidCustomExporter
|
||||
|
||||
private var pushProxStarted : Boolean = false
|
||||
private lateinit var pushProxClient : PushProxClient
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -51,22 +53,32 @@ class MainActivity : ComponentActivity() {
|
||||
customExporter = AndroidCustomExporter(metricsEngine).register(collectorRegistry)
|
||||
}
|
||||
|
||||
fun CollectMetrics(): String{
|
||||
private suspend fun reallyCollectMetrics() : String {
|
||||
delay(500)
|
||||
val writer = StringWriter()
|
||||
TextFormat.write004(writer, collectorRegistry.metricFamilySamples())
|
||||
return writer.toString()
|
||||
}
|
||||
|
||||
private fun CollectMetrics(): String{
|
||||
val writer = StringWriter()
|
||||
TextFormat.write004(writer, collectorRegistry.metricFamilySamples())
|
||||
|
||||
// initialize PushProx
|
||||
if (!pushProxStarted) {
|
||||
pushProxClient = PushProxClient(
|
||||
config = PushProxConfig(
|
||||
"test.example.com",
|
||||
"143.42.59.63",
|
||||
"http://143.42.59.63:8080",
|
||||
1,
|
||||
5,
|
||||
true
|
||||
|
||||
collectorRegistry,
|
||||
::reallyCollectMetrics,
|
||||
)
|
||||
)
|
||||
pushProxClient.startBackground()
|
||||
pushProxStarted = true
|
||||
}
|
||||
|
||||
return writer.toString()
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.birdthedeveloper.prometheus.android.prometheus.android.exporter
|
||||
|
||||
import android.preference.PreferenceActivity.Header
|
||||
import android.util.Log
|
||||
import io.github.reugn.kotlin.backoff.StrategyBackoff
|
||||
import io.github.reugn.kotlin.backoff.strategy.ExponentialStrategy
|
||||
@ -8,14 +9,17 @@ import io.ktor.client.call.body
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.client.request.HttpRequestBuilder
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.request
|
||||
import io.ktor.client.request.setBody
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.URLBuilder
|
||||
import io.ktor.http.Url
|
||||
import io.ktor.http.maxAge
|
||||
import io.prometheus.client.CollectorRegistry
|
||||
import io.prometheus.client.Counter
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
@ -26,35 +30,38 @@ import kotlinx.coroutines.runBlocking
|
||||
import kotlin.time.Duration
|
||||
|
||||
// Counters for monitoring the pushprox itself, compatible with the reference implementation in go.
|
||||
private class Counters(enabled: Boolean) {
|
||||
private val enabled : Boolean
|
||||
private class Counters(collectorRegistry: CollectorRegistry?) {
|
||||
private val collectorRegistry : CollectorRegistry?
|
||||
private lateinit var scrapeErrorCounter : Counter
|
||||
private lateinit var pushErrorCounter : Counter
|
||||
private lateinit var pollErrorCounter : Counter
|
||||
private lateinit var pollSuccessCounter : Counter
|
||||
private var enabled : Boolean = false
|
||||
|
||||
init {
|
||||
this.enabled = enabled
|
||||
if (enabled){
|
||||
this.collectorRegistry = collectorRegistry
|
||||
if (collectorRegistry != null){
|
||||
this.enabled = true
|
||||
|
||||
// following 3 counters are compatible with reference implementation
|
||||
scrapeErrorCounter = Counter.build()
|
||||
.name("pushprox_client_scrape_errors_total")
|
||||
.help("Number of scrape errors")
|
||||
.register()
|
||||
.register(collectorRegistry)
|
||||
pushErrorCounter = Counter.build()
|
||||
.name("pushprox_client_push_errors_total")
|
||||
.help("Number of push errors")
|
||||
.register()
|
||||
.register(collectorRegistry)
|
||||
pollErrorCounter = Counter.build()
|
||||
.name("pushprox_client_poll_errors_total")
|
||||
.help("Number of poll errors")
|
||||
.register()
|
||||
.register(collectorRegistry)
|
||||
|
||||
// custom
|
||||
pollSuccessCounter = Counter.build()
|
||||
.name("pushprox_client_poll_total")
|
||||
.help("Number of succesfull polls")
|
||||
.register()
|
||||
.register(collectorRegistry)
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,11 +80,13 @@ data class PushProxConfig(
|
||||
val proxyURL: String,
|
||||
val retryInitialWaitSeconds: Int = 1, //TODO will this be even used?
|
||||
val retryMaxWaitSeconds: Int = 5, //TODO will this be even used?
|
||||
val enablePushProxClientMonitoring: Boolean = true,
|
||||
val collectorRegistry: CollectorRegistry? = null,
|
||||
val performScrape: suspend () -> String,
|
||||
)
|
||||
|
||||
// This is a stripped down kotlin implementation of github.com/prometheus-community/PushProx client
|
||||
class PushProxClient(config: PushProxConfig) {
|
||||
//TODO dispose this thing - delete http client
|
||||
private val config: PushProxConfig
|
||||
private val pollURL: String
|
||||
private val pushURL: String
|
||||
@ -88,8 +97,8 @@ class PushProxClient(config: PushProxConfig) {
|
||||
init {
|
||||
this.config = config
|
||||
|
||||
// make sure proxyURL ends with a single '/'
|
||||
val modifiedProxyURL = config.proxyURL.trim('/') + '/'
|
||||
// make sure proxyURL ends without '/'
|
||||
val modifiedProxyURL = config.proxyURL.trim('/')
|
||||
log("ModifiedUrl", modifiedProxyURL)
|
||||
|
||||
pollURL = "$modifiedProxyURL/poll"
|
||||
@ -99,7 +108,7 @@ class PushProxClient(config: PushProxConfig) {
|
||||
// initialize resource - heavier objects
|
||||
private fun setup(){
|
||||
// init counters if they are enabled
|
||||
counters = Counters(config.enablePushProxClientMonitoring)
|
||||
counters = Counters(config.collectorRegistry)
|
||||
client = HttpClient(CIO)
|
||||
}
|
||||
|
||||
@ -110,10 +119,15 @@ class PushProxClient(config: PushProxConfig) {
|
||||
}
|
||||
|
||||
private suspend fun doPoll(){
|
||||
log("poll", "polling now")
|
||||
log(pollURL, pollURL)
|
||||
val response : HttpResponse = client.post(pollURL){
|
||||
setBody(config.myFqdn)
|
||||
}
|
||||
log("here", "here")
|
||||
val responseBody: String = response.body<String>()
|
||||
doPush(responseBody)
|
||||
// response body is not needed
|
||||
log("responseBody in poll", responseBody)
|
||||
log("got scrape request", responseBody)
|
||||
|
||||
@ -121,16 +135,43 @@ class PushProxClient(config: PushProxConfig) {
|
||||
|
||||
}
|
||||
|
||||
private fun parseRequest(request : String) : HttpRequestBuilder {
|
||||
var result : HttpRequestBuilder = HttpRequestBuilder()
|
||||
|
||||
private fun getIdFromResponseBody(responseBody: String) : String {
|
||||
//TODO implement this
|
||||
|
||||
return result
|
||||
return "5"
|
||||
//TODO throw custom exception if this is wrong
|
||||
}
|
||||
|
||||
private fun doPush() {
|
||||
// responseBody: response body of /poll request
|
||||
private suspend fun doPush(responseBody : String) {
|
||||
//TODO implement
|
||||
|
||||
// perform scrape
|
||||
lateinit var scrapedMetrics : String
|
||||
try {
|
||||
scrapedMetrics = config.performScrape()
|
||||
}catch(e : Exception){
|
||||
counters.scrapeError()
|
||||
log("scrape exception", e.toString())
|
||||
return
|
||||
}
|
||||
|
||||
try{
|
||||
log("scraped metrics in doPush", scrapedMetrics)
|
||||
val scrapeId : String = getIdFromResponseBody(responseBody)
|
||||
val response : HttpResponse = client.request(pushURL) {
|
||||
header("id", scrapeId)
|
||||
header("X-prometheus-scrape-timeout", "4") //TODO this is dummy for now
|
||||
method = HttpMethod.Post
|
||||
|
||||
setBody(scrapedMetrics)
|
||||
}
|
||||
|
||||
}catch(e : Exception){
|
||||
counters.pushError()
|
||||
log("push exception", e.toString())
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun handleErr(){
|
||||
@ -151,10 +192,7 @@ class PushProxClient(config: PushProxConfig) {
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun exceptionTest(){
|
||||
delay(1000L)
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
|
||||
private fun loop(backoff: StrategyBackoff<Unit>) {
|
||||
// fire and forget a new coroutine
|
||||
@ -162,26 +200,18 @@ class PushProxClient(config: PushProxConfig) {
|
||||
launch {
|
||||
while (true) {
|
||||
val job = launch {
|
||||
log("pushprox loop now", "-")
|
||||
log("pushprox main loop", "loop start")
|
||||
var result = backoff.withRetries {
|
||||
val result: Deferred<Unit> = async {
|
||||
delay(1000L)
|
||||
}
|
||||
|
||||
log("progress", "after poll")
|
||||
|
||||
// register poll error
|
||||
// register poll error using try-catch block
|
||||
try {
|
||||
result.await()
|
||||
doPoll()
|
||||
} catch (e: Exception) {
|
||||
log("progress", "catched")
|
||||
log("exception", e.toString())
|
||||
log("exception encountered!", e.toString())
|
||||
counters.pollError()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
log("pushprox loop end", "end")
|
||||
log("pushprox main loop", "loop end")
|
||||
}
|
||||
job.join()
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
# Server configuration, intended use of this demo
|
||||
#TODO demo , describe all files
|
@ -105,6 +105,7 @@
|
||||
mode: 0644
|
||||
force: true
|
||||
register: config_files
|
||||
tags: config
|
||||
|
||||
- name: Copy docker-compose.yaml
|
||||
ansible.builtin.copy:
|
||||
@ -115,19 +116,22 @@
|
||||
mode: 0644
|
||||
force: true
|
||||
register: compose_file
|
||||
tags: config
|
||||
|
||||
- name: Pull images
|
||||
community.docker.docker_compose:
|
||||
pull: true
|
||||
project_src: "{{ '/home/' + new_user_name }}"
|
||||
tags: config
|
||||
|
||||
- name: Start docker compose
|
||||
community.docker.docker_compose:
|
||||
state: present
|
||||
project_src: "{{ '/home/' + new_user_name }}"
|
||||
restarted: "{{ (config_files.changed | bool) or (compose_file.changed | bool) }}"
|
||||
tags: config
|
||||
|
||||
- name: Create docker compose systemd service
|
||||
- name: Create docker compose systemd service to start docker compose on boot
|
||||
block:
|
||||
- name: Copy docker compose unit file
|
||||
ansible.builtin.template:
|
||||
|
@ -46,9 +46,14 @@ http{
|
||||
server {
|
||||
listen 8080;
|
||||
|
||||
proxy_read_timeout 3600;
|
||||
proxy_connect_timeout 3600;
|
||||
proxy_send_timeout 3600;
|
||||
|
||||
location / {
|
||||
proxy_pass http://pushprox;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Prometheus UI server configuration
|
||||
|
@ -10,4 +10,8 @@ scrape_configs:
|
||||
|
||||
- job_name: "android phones"
|
||||
proxy_url: "http://pushprox:8080" #TODO add mobile phones here
|
||||
static_configs:
|
||||
- targets: [
|
||||
"test.example.com"
|
||||
]
|
||||
|
||||
|
Reference in New Issue
Block a user