more otel
This commit is contained in:
1 parent
9620e3bfa6
commit
21f04d696b
88 files changed
+23419
-192
No files matched your search
@@ -0,0 +1,103 @@
|
||||
// Package prometheus provides optional Prometheus range queries for rollout impact.
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Point struct {
|
||||
Timestamp float64 `json:"timestamp"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
baseURL string
|
||||
queryTemplate string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func NewFromEnv() *Client {
|
||||
baseURL := strings.TrimRight(os.Getenv("OTEL_PROMETHEUS_URL"), "/")
|
||||
if baseURL == "" {
|
||||
return nil
|
||||
}
|
||||
query := os.Getenv("OTEL_PROMETHEUS_ERROR_QUERY")
|
||||
if query == "" {
|
||||
query = `sum(rate(http_server_requests_total{service_name=%q,http_response_status_code=~"5.."}[5m]))`
|
||||
}
|
||||
return &Client{baseURL: baseURL, queryTemplate: query, httpClient: &http.Client{Timeout: 10 * time.Second}}
|
||||
}
|
||||
|
||||
func (c *Client) Configured() bool { return c != nil }
|
||||
|
||||
func (c *Client) ErrorRate(ctx context.Context, service, release string, start, end time.Time) ([]Point, error) {
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("prometheus is not configured")
|
||||
}
|
||||
query := renderQuery(c.queryTemplate, service, release)
|
||||
params := url.Values{
|
||||
"query": {query},
|
||||
"start": {strconv.FormatInt(start.Unix(), 10)},
|
||||
"end": {strconv.FormatInt(end.Unix(), 10)},
|
||||
"step": {"300"},
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+"/api/v1/query_range?"+params.Encode(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode/100 != 2 {
|
||||
return nil, fmt.Errorf("prometheus returned HTTP %s", response.Status)
|
||||
}
|
||||
var payload struct {
|
||||
Status string `json:"status"`
|
||||
Error string `json:"error"`
|
||||
Data struct {
|
||||
Result []struct {
|
||||
Values [][]json.RawMessage `json:"values"`
|
||||
} `json:"result"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := json.NewDecoder(response.Body).Decode(&payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if payload.Status != "success" {
|
||||
return nil, fmt.Errorf("prometheus query failed: %s", payload.Error)
|
||||
}
|
||||
points := make([]Point, 0)
|
||||
for _, series := range payload.Data.Result {
|
||||
for _, value := range series.Values {
|
||||
if len(value) != 2 {
|
||||
continue
|
||||
}
|
||||
var timestamp float64
|
||||
var sample string
|
||||
if json.Unmarshal(value[0], ×tamp) == nil && json.Unmarshal(value[1], &sample) == nil {
|
||||
points = append(points, Point{Timestamp: timestamp, Value: sample})
|
||||
}
|
||||
}
|
||||
}
|
||||
return points, nil
|
||||
}
|
||||
|
||||
func renderQuery(template, service, release string) string {
|
||||
values := []string{strconv.Quote(service), strconv.Quote(release)}
|
||||
for _, value := range values {
|
||||
if strings.Contains(template, "%q") {
|
||||
template = strings.Replace(template, "%q", value, 1)
|
||||
}
|
||||
}
|
||||
return template
|
||||
}
|
||||
Reference in new issue
Block a user