132 lines
4.0 KiB
Go
132 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"sync"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
)
|
|
|
|
// Создаём кастомный реестр
|
|
var customRegistry = prometheus.NewRegistry()
|
|
|
|
// Структура JSON
|
|
type MetricRequest struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
URL string `json:"url"`
|
|
Method string `json:"method"`
|
|
Type string `json:"type"` // "gauge", "counter" и т.д.
|
|
Metrics map[string]any `json:"metrics"`
|
|
}
|
|
|
|
// Экспортёр метрик
|
|
type MetricsExporter struct {
|
|
gaugeMetrics map[string]*prometheus.GaugeVec
|
|
counterMetrics map[string]*prometheus.CounterVec
|
|
mu sync.Mutex // Защита от одновременного доступа
|
|
}
|
|
|
|
// Создаём новый экспортёр
|
|
func NewMetricsExporter() *MetricsExporter {
|
|
return &MetricsExporter{
|
|
gaugeMetrics: make(map[string]*prometheus.GaugeVec),
|
|
counterMetrics: make(map[string]*prometheus.CounterVec),
|
|
}
|
|
}
|
|
|
|
// Обновление или создание метрики
|
|
func (me *MetricsExporter) UpdateMetric(name string, value interface{}) {
|
|
me.mu.Lock()
|
|
defer me.mu.Unlock()
|
|
|
|
for metricName, value := range request.Metrics {
|
|
// Уникальное имя метрики
|
|
fullMetricName := fmt.Sprintf("vks_%s_%s", request.ID, metricName)
|
|
|
|
// Лейблы
|
|
labels := []string{"name", "url", "method"}
|
|
labelValues := []string{request.Name, request.URL, request.Method}
|
|
|
|
if request.Type == "gauge" {
|
|
// Регистрируем Gauge, если он ещё не существует
|
|
if _, exists := me.gaugeMetrics[fullMetricName]; !exists {
|
|
gaugeVec := prometheus.NewGaugeVec(
|
|
prometheus.GaugeOpts{
|
|
Name: fullMetricName,
|
|
Help: fmt.Sprintf("Metric %s from %s", metricName, request.ID),
|
|
},
|
|
labels,
|
|
)
|
|
customRegistry.MustRegister(gaugeVec)
|
|
me.gaugeMetrics[fullMetricName] = gaugeVec
|
|
}
|
|
// Обновляем значение Gauge
|
|
me.gaugeMetrics[fullMetricName].WithLabelValues(labelValues...).Set(floatVal)
|
|
|
|
} else if request.Type == "counter" {
|
|
// Регистрируем Counter, если он ещё не существует
|
|
if _, exists := me.counterMetrics[fullMetricName]; !exists {
|
|
counterVec := prometheus.NewCounterVec(
|
|
prometheus.CounterOpts{
|
|
Name: fullMetricName,
|
|
Help: fmt.Sprintf("Metric %s from %s", metricName, request.ID),
|
|
},
|
|
labels,
|
|
)
|
|
customRegistry.MustRegister(counterVec)
|
|
me.counterMetrics[fullMetricName] = counterVec
|
|
}
|
|
// Инкрементируем Counter
|
|
me.counterMetrics[fullMetricName].WithLabelValues(labelValues...).Add(floatVal)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Обработчик для приёма JSON
|
|
func (me *MetricsExporter) JSONHandler(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
// Читаем тело запроса
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
http.Error(w, "Failed to read request body", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer r.Body.Close()
|
|
|
|
// Парсим JSON
|
|
var data MetricRequest
|
|
if err := json.Unmarshal(body, &data); err != nil {
|
|
http.Error(w, "Invalid JSON format", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Обновляем метрики
|
|
me.UpdateMetric(name, value)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte("Metrics updated"))
|
|
}
|
|
|
|
func main() {
|
|
exporter := NewMetricsExporter()
|
|
|
|
// Используем кастомный реестр в обработчике /metrics
|
|
http.Handle("/metrics", promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{}))
|
|
http.HandleFunc("/update", exporter.JSONHandler) // Обработчик для приёма JSON
|
|
|
|
port := ":9101"
|
|
fmt.Println("Starting server on port", port)
|
|
if err := http.ListenAndServe(port, nil); err != nil {
|
|
fmt.Println("Error starting server:", err)
|
|
}
|
|
}
|