Compare commits

..

No commits in common. "4896fb63857b0c80e50dae981bbd873debf0b60b" and "909c1151d455255f32240fdac714ee5f6fe1b81d" have entirely different histories.

8 changed files with 69 additions and 296 deletions

View File

@ -1,7 +1,10 @@
root = "." root = "." # Корень проекта
tmp_dir = "tmp" tmp_dir = "tmp"
[build] [build]
cmd = "go build -o ./tmp/app ." binary = "app" # Имя исполняемого файла
bin = "./tmp/app" cmd = "go build -o app main.go"
include_ext = ["go"] include_ext = ["go", "tpl", "tmpl", "html"]
[run]
cmd = "./app"

View File

@ -3,10 +3,9 @@ services:
image: cosmtrek/air:v1.61.7 image: cosmtrek/air:v1.61.7
volumes: volumes:
- .:/app - .:/app
working_dir: /app
ports: ports:
- "9101:9101" - "9101:9101"
command: ["air", "-c", ".air.toml"] command: air -c .air.toml
prometheus: prometheus:
image: prom/prometheus:v3.1.0 image: prom/prometheus:v3.1.0

View File

@ -1,132 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/stretchr/testify/assert"
)
// Тест: успешное обновление метрик через /update
func TestJSONHandler_ValidRequest(t *testing.T) {
exporter := NewMetricsExporter()
server := httptest.NewServer(http.HandlerFunc(exporter.JSONHandler))
defer server.Close()
// JSON-запрос
requestData := MetricRequest{
ID: "test_api",
Name: "Test API",
URL: "http://localhost/api",
Method: "GET",
Type: "gauge",
Metrics: map[string]float64{
"response_time": 200.5,
},
}
// Маршалим JSON и проверяем ошибку
jsonData, err := json.Marshal(requestData)
assert.NoError(t, err, "Ошибка при маршалинге JSON")
// Отправляем запрос
resp, err := http.Post(server.URL, "application/json", bytes.NewBuffer(jsonData))
assert.NoError(t, err, "Ошибка при отправке запроса на /update")
assert.Equal(t, http.StatusOK, resp.StatusCode)
// Проверяем тело ответа
body, err := io.ReadAll(resp.Body)
assert.NoError(t, err, "Ошибка чтения тела ответа")
assert.Equal(t, "Metrics updated", string(body))
}
// Тест: успешная обработка некорректного JSON
func TestJSONHandler_InvalidJSON(t *testing.T) {
exporter := NewMetricsExporter()
server := httptest.NewServer(http.HandlerFunc(exporter.JSONHandler))
defer server.Close()
// Отправляем некорректный JSON
resp, err := http.Post(server.URL, "application/json", bytes.NewBuffer([]byte("{invalid json}")))
assert.NoError(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
}
// Тест: проверка создания и обновления Gauge метрики
func TestUpdateMetric_Gauge(t *testing.T) {
exporter := NewMetricsExporter()
request := MetricRequest{
ID: "test",
Name: "Test Gauge",
URL: "http://test.com",
Method: "GET",
Type: "gauge",
Metrics: map[string]float64{
"load": 75.5,
},
}
// Вызываем обновление метрики
exporter.UpdateMetric(request)
// Проверяем, что метрика существует
metric, exists := exporter.gaugeMetrics["vks_test_load"]
assert.True(t, exists, "Метрика должна быть зарегистрирована")
assert.NotNil(t, metric, "Метрика не должна быть nil")
}
// Тест: проверка создания и увеличения Counter метрики
func TestUpdateMetric_Counter(t *testing.T) {
exporter := NewMetricsExporter()
request := MetricRequest{
ID: "test",
Name: "Test Counter",
URL: "http://test.com",
Method: "POST",
Type: "counter",
Metrics: map[string]float64{
"requests": 1,
},
}
// Вызываем обновление метрики дважды
exporter.UpdateMetric(request)
exporter.UpdateMetric(request)
// Проверяем, что метрика существует
metric, exists := exporter.counterMetrics["vks_test_requests"]
assert.True(t, exists, "Метрика должна быть зарегистрирована")
assert.NotNil(t, metric, "Метрика не должна быть nil")
}
// Тест: получение метрик по /metrics
func TestMetricsEndpoint(t *testing.T) {
// Регистрируем метрику
gauge := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "test_metric",
Help: "Test metric for /metrics endpoint",
})
customRegistry.MustRegister(gauge)
gauge.Set(50.5)
server := httptest.NewServer(promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{}))
defer server.Close()
// Делаем GET-запрос к /metrics
resp, err := http.Get(server.URL)
assert.NoError(t, err, "Ошибка при запросе к /metrics")
assert.Equal(t, http.StatusOK, resp.StatusCode)
// Проверяем, что в ответе есть test_metric
body, err := io.ReadAll(resp.Body)
assert.NoError(t, err, "Ошибка чтения тела ответа")
assert.Contains(t, string(body), "test_metric")
}

11
go.mod
View File

@ -1,24 +1,17 @@
module exporter module prometheus_exporter
go 1.23.5 go 1.23.5
require ( require github.com/prometheus/client_golang v1.20.5
github.com/prometheus/client_golang v1.20.5
github.com/stretchr/testify v1.9.0
)
require ( require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/compress v1.17.9 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect
golang.org/x/sys v0.22.0 // indirect golang.org/x/sys v0.22.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

18
go.sum
View File

@ -2,23 +2,14 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
@ -27,16 +18,7 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

119
main.go
View File

@ -4,9 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"log"
"net/http" "net/http"
"os"
"sync" "sync"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
@ -22,16 +20,14 @@ type MetricRequest struct {
Name string `json:"name"` Name string `json:"name"`
URL string `json:"url"` URL string `json:"url"`
Method string `json:"method"` Method string `json:"method"`
Type string `json:"type"` Type string `json:"type"` // "gauge", "counter" и т.д.
Metrics map[string]float64 `json:"metrics"` Metrics map[string]any `json:"metrics"`
} }
// Экспортёр метрик // Экспортёр метрик
type MetricsExporter struct { type MetricsExporter struct {
gaugeMetrics map[string]*prometheus.GaugeVec gaugeMetrics map[string]*prometheus.GaugeVec
counterMetrics map[string]*prometheus.CounterVec counterMetrics map[string]*prometheus.CounterVec
histogramMetrics map[string]*prometheus.HistogramVec
summaryMetrics map[string]*prometheus.SummaryVec
mu sync.Mutex // Защита от одновременного доступа mu sync.Mutex // Защита от одновременного доступа
} }
@ -40,13 +36,11 @@ func NewMetricsExporter() *MetricsExporter {
return &MetricsExporter{ return &MetricsExporter{
gaugeMetrics: make(map[string]*prometheus.GaugeVec), gaugeMetrics: make(map[string]*prometheus.GaugeVec),
counterMetrics: make(map[string]*prometheus.CounterVec), counterMetrics: make(map[string]*prometheus.CounterVec),
histogramMetrics: make(map[string]*prometheus.HistogramVec),
summaryMetrics: make(map[string]*prometheus.SummaryVec),
} }
} }
// Обновление или создание метрики // Обновление или создание метрики
func (me *MetricsExporter) UpdateMetric(request MetricRequest) { func (me *MetricsExporter) UpdateMetric(name string, value interface{}) {
me.mu.Lock() me.mu.Lock()
defer me.mu.Unlock() defer me.mu.Unlock()
@ -58,100 +52,44 @@ func (me *MetricsExporter) UpdateMetric(request MetricRequest) {
labels := []string{"name", "url", "method"} labels := []string{"name", "url", "method"}
labelValues := []string{request.Name, request.URL, request.Method} labelValues := []string{request.Name, request.URL, request.Method}
// Обработка метрик в зависимости от типа if request.Type == "gauge" {
switch request.Type { // Регистрируем Gauge, если он ещё не существует
case "gauge": if _, exists := me.gaugeMetrics[fullMetricName]; !exists {
me.updateGauge(fullMetricName, labels, labelValues, value)
case "counter":
me.updateCounter(fullMetricName, labels, labelValues, value)
case "histogram":
me.updateHistogram(fullMetricName, labels, labelValues, value)
case "summary":
me.updateSummary(fullMetricName, labels, labelValues, value)
default:
log.Printf("Неподдерживаемый тип метрики: %s\n", request.Type)
}
}
}
// Обновление `Gauge` метрик
func (me *MetricsExporter) updateGauge(name string, labels []string, labelValues []string, value float64) {
if _, exists := me.gaugeMetrics[name]; !exists {
gaugeVec := prometheus.NewGaugeVec( gaugeVec := prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: name, Name: fullMetricName,
Help: fmt.Sprintf("Gauge metric %s", name), Help: fmt.Sprintf("Metric %s from %s", metricName, request.ID),
}, },
labels, labels,
) )
customRegistry.MustRegister(gaugeVec) customRegistry.MustRegister(gaugeVec)
me.gaugeMetrics[name] = gaugeVec me.gaugeMetrics[fullMetricName] = gaugeVec
} }
me.gaugeMetrics[name].WithLabelValues(labelValues...).Set(value) // Обновляем значение Gauge
log.Printf("Gauge обновлён: %s = %f\n", name, value) me.gaugeMetrics[fullMetricName].WithLabelValues(labelValues...).Set(floatVal)
}
// Обновление `Counter` метрик } else if request.Type == "counter" {
func (me *MetricsExporter) updateCounter(name string, labels []string, labelValues []string, value float64) { // Регистрируем Counter, если он ещё не существует
if value < 0 { if _, exists := me.counterMetrics[fullMetricName]; !exists {
log.Printf("Ошибка: Counter %s не может быть отрицательным\n", name)
return
}
if _, exists := me.counterMetrics[name]; !exists {
counterVec := prometheus.NewCounterVec( counterVec := prometheus.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: name, Name: fullMetricName,
Help: fmt.Sprintf("Counter metric %s", name), Help: fmt.Sprintf("Metric %s from %s", metricName, request.ID),
}, },
labels, labels,
) )
customRegistry.MustRegister(counterVec) customRegistry.MustRegister(counterVec)
me.counterMetrics[name] = counterVec me.counterMetrics[fullMetricName] = counterVec
} }
me.counterMetrics[name].WithLabelValues(labelValues...).Add(value) // Инкрементируем Counter
log.Printf("Counter обновлён: %s += %f\n", name, value) me.counterMetrics[fullMetricName].WithLabelValues(labelValues...).Add(floatVal)
}
// Обновление `Histogram` метрик
func (me *MetricsExporter) updateHistogram(name string, labels []string, labelValues []string, value float64) {
if _, exists := me.histogramMetrics[name]; !exists {
histogramVec := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: name,
Help: fmt.Sprintf("Histogram metric %s", name),
Buckets: prometheus.LinearBuckets(10, 10, 10), // Пример диапазонов
},
labels,
)
customRegistry.MustRegister(histogramVec)
me.histogramMetrics[name] = histogramVec
} }
me.histogramMetrics[name].WithLabelValues(labelValues...).Observe(value)
log.Printf("Histogram обновлён: %s = %f\n", name, value)
}
// Обновление `Summary` метрик
func (me *MetricsExporter) updateSummary(name string, labels []string, labelValues []string, value float64) {
if _, exists := me.summaryMetrics[name]; !exists {
summaryVec := prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Name: name,
Help: fmt.Sprintf("Summary metric %s", name),
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, // Пример целей
},
labels,
)
customRegistry.MustRegister(summaryVec)
me.summaryMetrics[name] = summaryVec
} }
me.summaryMetrics[name].WithLabelValues(labelValues...).Observe(value)
log.Printf("Summary обновлён: %s = %f\n", name, value)
} }
// Обработчик для приёма JSON // Обработчик для приёма JSON
func (me *MetricsExporter) JSONHandler(w http.ResponseWriter, r *http.Request) { func (me *MetricsExporter) JSONHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost { if r.Method != http.MethodPost {
log.Printf("Неверный метод: %s (ожидался POST)", r.Method)
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed) http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
return return
} }
@ -159,33 +97,26 @@ func (me *MetricsExporter) JSONHandler(w http.ResponseWriter, r *http.Request) {
// Читаем тело запроса // Читаем тело запроса
body, err := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
log.Printf("Ошибка чтения тела запроса: %s\n", err)
http.Error(w, "Failed to read request body", http.StatusInternalServerError) http.Error(w, "Failed to read request body", http.StatusInternalServerError)
return return
} }
defer r.Body.Close() defer r.Body.Close()
// Парсим JSON // Парсим JSON
var request MetricRequest var data MetricRequest
if err := json.Unmarshal(body, &request); err != nil { if err := json.Unmarshal(body, &data); err != nil {
log.Printf("Ошибка парсинга JSON: %s\nТело запроса: %s\n", err, string(body))
http.Error(w, "Invalid JSON format", http.StatusBadRequest) http.Error(w, "Invalid JSON format", http.StatusBadRequest)
return return
} }
// Обновляем метрики // Обновляем метрики
log.Printf("Обновление метрик для запроса ID: %s\n", request.ID) me.UpdateMetric(name, value)
me.UpdateMetric(request)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte("Metrics updated")) w.Write([]byte("Metrics updated"))
log.Printf("Метрики обновлены успешно для ID: %s\n", request.ID)
} }
func main() { func main() {
log.SetOutput(os.Stdout) // Логируем в стандартный вывод
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
exporter := NewMetricsExporter() exporter := NewMetricsExporter()
// Используем кастомный реестр в обработчике /metrics // Используем кастомный реестр в обработчике /metrics
@ -193,10 +124,8 @@ func main() {
http.HandleFunc("/update", exporter.JSONHandler) // Обработчик для приёма JSON http.HandleFunc("/update", exporter.JSONHandler) // Обработчик для приёма JSON
port := ":9101" port := ":9101"
log.Printf("Starting server on port %s\n", port) fmt.Println("Starting server on port", port)
if err := http.ListenAndServe(port, nil); err != nil { if err := http.ListenAndServe(port, nil); err != nil {
log.Fatalf("Error starting server: %s\n", err) fmt.Println("Error starting server:", err)
} }
} }
//TODO: сделать переменные окружения, настроить канал, дописать тесты

View File

@ -1,12 +0,0 @@
curl -X POST -H "Content-Type: application/json" -d '{
"id": "gauge_test",
"name": "Gauge Metric",
"url": "http://127.0.0.1:8081/gauge",
"method": "GET",
"type": "gauge",
"metrics": {
"temperature": 36.6
}
}' "http://localhost:9101/update"
curl http://localhost:9101/metrics

11
test_data.txt Normal file
View File

@ -0,0 +1,11 @@
curl -X POST -H "Content-Type: application/json" -d '{
"id": "mock_api_2",
"name": "Mock /ping",
"url": "http://127.0.0.1:8081/ping",
"method": "GET",
"type": "gauge",
"metrics": {
"response_time": 120.5,
"status_code": 200,
}
}' http://localhost:9101/update