Skip to content

add telemetry #144

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7056a23
feat: add metrics file support and telemetry tracking for lint rule e…
juev Mar 7, 2025
09d957b
feat: update README and add metrics printing for linting results
juev Mar 7, 2025
289a031
refactor: remove metrics file handling from flags and manager
juev Mar 25, 2025
e592b17
feat: refactor metrics handling to use singleton pattern and improve …
juev Mar 25, 2025
683b47a
fix: remove unnecessary newline in error handling in service.go
juev Mar 25, 2025
cd2d273
feat: enhance metrics handling for linter warnings and refactor relat…
juev Mar 25, 2025
0a673ec
fix: optimize error handling in GetLinterWarningsCountLabels and clea…
juev Mar 25, 2025
8a13e8c
fix: filter out non-warning errors in error processing logic
juev Mar 25, 2025
5451019
feat: move linter warning metrics increment to dedicated processing f…
juev Mar 25, 2025
3c25629
feat: invoke dedicated processing for linter warnings count metrics
juev Mar 25, 2025
59c8d07
feat: add linter warnings metrics collection and processing logic
juev Mar 25, 2025
8af65bb
Update cmd/dmt/main.go
juev Mar 25, 2025
23335b7
chore: remove unused slices import from main.go
juev Mar 25, 2025
d290455
feat: implement Prometheus metrics conversion and storage management
juev Mar 26, 2025
7b09f19
feat: refactor metrics handling and improve Prometheus client initial…
juev Mar 26, 2025
f75dc54
refactor: optimize label handling for Prometheus metrics conversion
juev Mar 26, 2025
677695e
refactor: rename Prometheus metrics service constructor to follow nam…
juev Mar 26, 2025
8ad7c9f
chore: add Apache 2.0 license header to storage.go
juev Mar 26, 2025
48c8c40
feat: add DMT runtime duration metrics tracking
juev Mar 26, 2025
b30cd6b
refactor: update metrics client initialization and streamline DMT inf…
juev Mar 26, 2025
79d30fa
chore: add Apache 2.0 license header to convert.go
juev Mar 26, 2025
68aa45c
Merge branch 'main' into feature/telemetry
juev Mar 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions cmd/dmt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@ func runLint(dir string) {
mng.Run()
mng.PrintResult()

metricsClient, err := metrics.NewPrometheusMetricsService(os.Getenv("DMT_METRICS_URL"), os.Getenv("DMT_METRICS_TOKEN"))
if err != nil {
logger.ErrorF("Failed to create metrics client: %v", err)
}
metricsClient := metrics.GetClient()
metricsClient.AddMetrics(metrics.GetInfoMetric(dir))

labels := mng.GetLinterWarningsCountLabels()
metricsClient.AddMetrics(metrics.GetLinterWarningsCountMetrics(labels)...)

mng.ProcessLinterWarningsCountMetrics()
metricsClient.AddMetrics(metrics.GetLinterWarningsMetrics(cfg.GlobalSettings)...)

metricsClient.AddMetrics(metrics.GetInfo(dir))
metricsClient.Send(context.Background())

if mng.HasCriticalErrors() {
Expand Down
27 changes: 27 additions & 0 deletions internal/manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/deckhouse/dmt/internal/flags"
"github.com/deckhouse/dmt/internal/fsutils"
"github.com/deckhouse/dmt/internal/logger"
"github.com/deckhouse/dmt/internal/metrics"
"github.com/deckhouse/dmt/internal/module"
"github.com/deckhouse/dmt/internal/values"
"github.com/deckhouse/dmt/pkg"
Expand Down Expand Up @@ -254,6 +255,32 @@ func (m *Manager) HasCriticalErrors() bool {
return m.errors.ContainsErrors()
}

func (m *Manager) GetLinterWarningsCountLabels() map[string]map[string]struct{} {
result := make(map[string]map[string]struct{})
for i := range m.errors.GetErrors() {
err := m.errors.GetErrors()[i]
if err.Level != pkg.Warn {
continue
}
if _, ok := result[err.LinterID]; !ok {
result[err.LinterID] = make(map[string]struct{})
}
result[err.LinterID][err.RuleID] = struct{}{}
}

return result
}

func (m *Manager) ProcessLinterWarningsCountMetrics() {
for i := range m.errors.GetErrors() {
err := m.errors.GetErrors()[i]
if err.Level != pkg.Warn {
continue
}
metrics.IncLinterWarning(err.LinterID, err.RuleID)
}
}

func isExistsOnFilesystem(parts ...string) bool {
_, err := os.Stat(filepath.Join(parts...))
return err == nil
Expand Down
134 changes: 129 additions & 5 deletions internal/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,41 @@ import (
"github.com/prometheus/client_golang/prometheus"

"github.com/deckhouse/dmt/internal/flags"
"github.com/deckhouse/dmt/pkg/config/global"
)

func GetInfo(dir string) PrometheusCollectorFunc {
var (
metrics *PrometheusMetricsService
)

var (
dmtInfo = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "dmt_info",
Help: "DMT info",
}, []string{"version", "id", "repository"})

dmtLinterWarningsCount = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "dmt_linter_warnings_count",
Help: "DMT linter warnings count",
}, []string{"version", "linter", "rule"})

dmtLinterWarnings = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "dmt_linter_warnings",
Help: "DMT linter warnings",
}, []string{"version", "linter"})
)

func GetClient() *PrometheusMetricsService {
if metrics != nil {
return metrics
}

metrics = NewPrometheusMetricsService(os.Getenv("DMT_METRICS_URL"), os.Getenv("DMT_METRICS_TOKEN"))

return metrics
}

func GetInfoMetric(dir string) PrometheusCollectorFunc {
return func(_ context.Context) (string, prometheus.Metric) {
repository := cmp.Or(os.Getenv("DMT_REPOSITORY"), getRepositoryAddress(dir))
if repository == "" {
Expand All @@ -40,16 +72,108 @@ func GetInfo(dir string) PrometheusCollectorFunc {
}
id := cmp.Or(os.Getenv("DMT_METRICS_ID"), repositoryID)

c := prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "dmt_info",
Help: "DMT info",
}, []string{"version", "id", "repository"}).With(prometheus.Labels{
c := dmtInfo.With(prometheus.Labels{
"id": id,
"version": flags.Version,
"repository": repository,
})

c.Add(1)

return "dmt_info", c
}
}

func GetLinterWarningsCountMetrics(labels map[string]map[string]struct{}) []PrometheusCollectorFunc {
result := make([]PrometheusCollectorFunc, 0)
for linter, rules := range labels {
for rule := range rules {
result = append(result, func(_ context.Context) (string, prometheus.Metric) {
return "dmt_linter_warnings_count", dmtLinterWarningsCount.With(prometheus.Labels{
"version": flags.Version,
"linter": linter,
"rule": rule,
})
})
}
}

return result
}

func IncLinterWarning(linter, rule string) {
dmtLinterWarningsCount.With(prometheus.Labels{
"version": flags.Version,
"linter": linter,
"rule": rule,
}).Add(1)
}

func GetLinterWarningsMetrics(cfg global.Global) []PrometheusCollectorFunc {
result := make([]PrometheusCollectorFunc, 0)
if cfg.Linters.Templates.IsWarn() {
c := dmtLinterWarnings.With(prometheus.Labels{"version": flags.Version, "linter": "templates"})
c.Add(1)
result = append(result, func(_ context.Context) (string, prometheus.Metric) {
return "dmt_linter_warnings", c
})
}
if cfg.Linters.Images.IsWarn() {
c := dmtLinterWarnings.With(prometheus.Labels{"version": flags.Version, "linter": "images"})
c.Add(1)
result = append(result, func(_ context.Context) (string, prometheus.Metric) {
return "dmt_linter_warnings", c
})
}
if cfg.Linters.Container.IsWarn() {
c := dmtLinterWarnings.With(prometheus.Labels{"version": flags.Version, "linter": "container"})
c.Add(1)
result = append(result, func(_ context.Context) (string, prometheus.Metric) {
return "dmt_linter_warnings", c
})
}
if cfg.Linters.Rbac.IsWarn() {
c := dmtLinterWarnings.With(prometheus.Labels{"version": flags.Version, "linter": "rbac"})
c.Add(1)
result = append(result, func(_ context.Context) (string, prometheus.Metric) {
return "dmt_linter_warnings", c
})
}
if cfg.Linters.Hooks.IsWarn() {
c := dmtLinterWarnings.With(prometheus.Labels{"version": flags.Version, "linter": "hooks"})
c.Add(1)
result = append(result, func(_ context.Context) (string, prometheus.Metric) {
return "dmt_linter_warnings", c
})
}
if cfg.Linters.Module.IsWarn() {
c := dmtLinterWarnings.With(prometheus.Labels{"version": flags.Version, "linter": "module"})
c.Add(1)
result = append(result, func(_ context.Context) (string, prometheus.Metric) {
return "dmt_linter_warnings", c
})
}
if cfg.Linters.OpenAPI.IsWarn() {
c := dmtLinterWarnings.With(prometheus.Labels{"version": flags.Version, "linter": "openapi"})
c.Add(1)
result = append(result, func(_ context.Context) (string, prometheus.Metric) {
return "dmt_linter_warnings", c
})
}
if cfg.Linters.NoCyrillic.IsWarn() {
c := dmtLinterWarnings.With(prometheus.Labels{"version": flags.Version, "linter": "no-cyrillic"})
c.Add(1)
result = append(result, func(_ context.Context) (string, prometheus.Metric) {
return "dmt_linter_warnings", c
})
}
if cfg.Linters.License.IsWarn() {
c := dmtLinterWarnings.With(prometheus.Labels{"version": flags.Version, "linter": "license"})
c.Add(1)
result = append(result, func(_ context.Context) (string, prometheus.Metric) {
return "dmt_linter_warnings", c
})
}

return result
}
37 changes: 17 additions & 20 deletions internal/metrics/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,18 @@ type PrometheusMetricsService struct {
metricsFuncs []PrometheusCollectorFunc
}

func NewPrometheusMetricsService(url, token string) (*PrometheusMetricsService, error) {
func NewPrometheusMetricsService(url, token string) *PrometheusMetricsService {
if url == "" || token == "" {
return nil, nil
return nil
}

client, err := promremote.NewClient(promremote.NewConfig(promremote.WriteURLOption(url)))
if err != nil {
return nil, err
}
client, _ := promremote.NewClient(promremote.NewConfig(promremote.WriteURLOption(url)))

return &PrometheusMetricsService{
url: url,
token: token,
client: client,
}, nil
}
}

func (p *PrometheusMetricsService) AddMetrics(fns ...PrometheusCollectorFunc) {
Expand All @@ -68,21 +65,21 @@ func (p *PrometheusMetricsService) Send(ctx context.Context) {
if p == nil {
return
}
var timeSeries []promremote.TimeSeries
for _, fn := range p.metricsFuncs {
name, metric := fn(ctx)
_, err := p.client.WriteTimeSeries(
ctx,
[]promremote.TimeSeries{
promremote.ConvertMetric(metric, name),
},
promremote.WriteOptions{
Headers: map[string]string{
"Authorization": "Bearer " + p.token,
},
timeSeries = append(timeSeries, promremote.ConvertMetric(metric, name))
}
_, err := p.client.WriteTimeSeries(
ctx,
timeSeries,
promremote.WriteOptions{
Headers: map[string]string{
"Authorization": "Bearer " + p.token,
},
)
if err != nil {
logger.ErrorF("error in sending metrics: %v", err)
}
},
)
if err != nil {
logger.ErrorF("error in sending metrics: %v", err)
}
}
4 changes: 2 additions & 2 deletions internal/promremote/promremote.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"context"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"time"

Expand Down Expand Up @@ -254,7 +254,7 @@ func (c *client) WriteProto(
code: result.StatusCode,
}

body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
writeErr.err = fmt.Errorf("%w, body_read_error=%w", writeErr.err, err)
return result, writeErr
Expand Down
4 changes: 4 additions & 0 deletions pkg/config/global/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ type Linters struct {
type LinterConfig struct {
Impact *pkg.Level `mapstructure:"impact"`
}

func (c LinterConfig) IsWarn() bool {
return c.Impact != nil && *c.Impact == pkg.Warn
}