model/audit_report.go
Rama main · réplica de lectura del repositorio original.
package model
import (
"slices"
"strings"
"github.com/coroot/coroot/timeseries"
"github.com/coroot/coroot/utils"
)
type AuditReportName string
const (
AuditReportSLO AuditReportName = "SLO"
AuditReportInstances AuditReportName = "Instances"
AuditReportCPU AuditReportName = "CPU"
AuditReportGPU AuditReportName = "GPU"
AuditReportMemory AuditReportName = "Memory"
AuditReportStorage AuditReportName = "Storage"
AuditReportNetwork AuditReportName = "Net"
AuditReportDNS AuditReportName = "DNS"
AuditReportLogs AuditReportName = "Logs"
AuditReportPostgres AuditReportName = "Postgres"
AuditReportRedis AuditReportName = "Redis"
AuditReportMongodb AuditReportName = "Mongodb"
AuditReportMemcached AuditReportName = "Memcached"
AuditReportMysql AuditReportName = "Mysql"
AuditReportJvm AuditReportName = "JVM"
AuditReportDotNet AuditReportName = ".NET"
AuditReportPython AuditReportName = "Python"
AuditReportNodejs AuditReportName = "Node.js"
AuditReportNode AuditReportName = "Node"
AuditReportDeployments AuditReportName = "Deployments"
AuditReportProfiling AuditReportName = "Profiling"
AuditReportTracing AuditReportName = "Tracing"
)
type ConfigurationHint struct {
Message string `json:"message"`
ReadMoreLink string `json:"read_more_link"`
}
type AuditReport struct {
app *Application
ctx timeseries.Context
checkConfigs CheckConfigs
detailed bool
Name AuditReportName `json:"name"`
Status Status `json:"status"`
Widgets []*Widget `json:"widgets"`
Checks []*Check `json:"checks"`
Custom bool `json:"custom"`
Instrumentation ApplicationType `json:"instrumentation"`
ConfigurationHint *ConfigurationHint `json:"configuration_hint,omitempty"`
}
func NewAuditReport(app *Application, ctx timeseries.Context, checkConfigs CheckConfigs, name AuditReportName, detailed bool) *AuditReport {
return &AuditReport{app: app, Name: name, ctx: ctx, checkConfigs: checkConfigs, detailed: detailed}
}
func (r *AuditReport) AddWidget(w *Widget) *Widget {
r.Widgets = append(r.Widgets, w)
return w
}
func (r *AuditReport) ArrangeWidgets() {
var ungrouped []*Widget
var groups []string
byGroup := map[string][]*Widget{}
priorities := map[string]int{}
for _, w := range r.Widgets {
if w.Group == "" {
ungrouped = append(ungrouped, w)
continue
}
if _, ok := byGroup[w.Group]; !ok {
groups = append(groups, w.Group)
priorities[w.Group] = w.groupPriority
}
byGroup[w.Group] = append(byGroup[w.Group], w)
}
if len(groups) == 0 {
return
}
slices.SortStableFunc(groups, func(a, b string) int {
return priorities[a] - priorities[b]
})
widgets := make([]*Widget, 0, len(r.Widgets)+len(groups))
widgets = append(widgets, ungrouped...)
for _, g := range groups {
widgets = append(widgets, &Widget{GroupHeader: g, Width: "100%"})
widgets = append(widgets, byGroup[g]...)
}
r.Widgets = widgets
}
func (r *AuditReport) GetOrCreateChartGroup(title string, doc *DocLink) *ChartGroup {
if !r.detailed {
return nil
}
for _, w := range r.Widgets {
if cg := w.ChartGroup; cg != nil {
if cg.Title == title {
return cg
}
}
}
cg := NewChartGroup(r.ctx, title)
w := &Widget{ChartGroup: cg, DocLink: doc}
cg.widget = w
r.Widgets = append(r.Widgets, w)
return cg
}
func (r *AuditReport) GetOrCreateChartInGroup(title string, chartTitle string, doc *DocLink) *Chart {
if !r.detailed {
return nil
}
return r.GetOrCreateChartGroup(title, doc).GetOrCreateChart(chartTitle)
}
func (r *AuditReport) GetOrCreateChart(title string, doc *DocLink) *Chart {
if !r.detailed {
return nil
}
for _, w := range r.Widgets {
if ch := w.Chart; ch != nil {
if ch.Title == title {
return ch
}
}
}
ch := NewChart(r.ctx, title)
w := &Widget{Chart: ch, DocLink: doc}
ch.widget = w
r.Widgets = append(r.Widgets, w)
return ch
}
func (r *AuditReport) GetOrCreateHeatmap(title string) *Heatmap {
if !r.detailed {
return nil
}
for _, w := range r.Widgets {
if h := w.Heatmap; h != nil {
if h.Title == title {
return h
}
}
}
h := NewHeatmap(r.ctx, title)
w := &Widget{Heatmap: h, Width: "100%"}
h.widget = w
r.Widgets = append(r.Widgets, w)
return h
}
func (r *AuditReport) GetOrCreateDependencyMap() *DependencyMap {
if !r.detailed {
return nil
}
for _, w := range r.Widgets {
if w.DependencyMap != nil {
return w.DependencyMap
}
}
dm := &DependencyMap{}
r.Widgets = append(r.Widgets, &Widget{DependencyMap: dm, Width: "100%"})
return dm
}
func (r *AuditReport) GetOrCreateTable(header ...string) *Table {
if !r.detailed {
return nil
}
for _, w := range r.Widgets {
if t := w.Table; t != nil && slices.Equal(t.Header, header) {
return t
}
}
t := NewTable(header...)
w := &Widget{Table: t, Width: "100%"}
t.widget = w
r.Widgets = append(r.Widgets, w)
return t
}
func (r *AuditReport) CreateCheck(cfg CheckConfig) *Check {
ch := &Check{
Id: cfg.Id,
Title: cfg.Title,
Status: OK,
Unit: cfg.Unit,
ConditionFormatTemplate: cfg.ConditionFormatTemplate,
typ: cfg.Type,
messageTemplate: cfg.MessageTemplate,
items: utils.NewStringSet(),
}
switch cfg.Id {
case Checks.SLOAvailability.Id:
availabilityCfg, _ := r.checkConfigs.GetAvailability(r.app.Id)
ch.Threshold = availabilityCfg.ObjectivePercentage
case Checks.SLOLatency.Id:
latencyCfg, _ := r.checkConfigs.GetLatency(r.app.Id, r.app.Category)
ch.Threshold = latencyCfg.ObjectivePercentage
ch.ConditionFormatTemplate = strings.Replace(
ch.ConditionFormatTemplate,
"<bucket>",
utils.FormatLatency(latencyCfg.ObjectiveBucket),
1,
)
case Checks.SLOLLMLatency.Id:
latencyCfg, _ := r.checkConfigs.GetLLMLatency(r.app.Id)
ch.Threshold = latencyCfg.ObjectivePercentage
ch.ConditionFormatTemplate = strings.Replace(
ch.ConditionFormatTemplate,
"<bucket>",
utils.FormatLatency(latencyCfg.ObjectiveBucket),
1,
)
default:
ch.Threshold = r.checkConfigs.GetSimple(cfg.Id, r.app.Id).Threshold
}
r.Checks = append(r.Checks, ch)
return ch
}