doPush impl almost done

This commit is contained in:
Martin Ptáček
2023-05-01 16:19:50 +02:00
parent 06e2f5866b
commit 7ca0d241a3
8 changed files with 127 additions and 53 deletions

View File

@ -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
```

View File

@ -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'

View File

@ -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
pushProxClient = PushProxClient(
config = PushProxConfig(
"test.example.com",
"143.42.59.63",
1,
5,
true
if (!pushProxStarted) {
pushProxClient = PushProxClient(
config = PushProxConfig(
"test.example.com",
"http://143.42.59.63:8080",
1,
5,
collectorRegistry,
::reallyCollectMetrics,
)
)
)
pushProxClient.startBackground()
pushProxClient.startBackground()
pushProxStarted = true
}
return writer.toString()
}

View File

@ -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()
}

View File

@ -1,2 +0,0 @@
# Server configuration, intended use of this demo
#TODO demo , describe all files

View File

@ -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:

View File

@ -13,7 +13,7 @@ http{
}
upstream pushprox{
server pushprox:8080;
server pushprox:8080;
}
upstream grafana{
@ -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

View File

@ -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"
]