Like Prometheus, but for logs.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
loki/pkg/tool/printer/printer.go

244 lines
5.8 KiB

package printer
import (
"encoding/json"
"fmt"
"io"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/alecthomas/chroma/v2/quick"
"github.com/mitchellh/colorstring"
"gopkg.in/yaml.v3"
"github.com/grafana/loki/v3/pkg/tool/rules"
"github.com/grafana/loki/v3/pkg/tool/rules/rwrulefmt"
)
// Printer is used for printing formatted output from the lokitool
type Printer struct {
disableColor bool
colorizer colorstring.Colorize
}
// New returns a Printer struct
func New(color bool) *Printer {
return &Printer{
disableColor: color,
colorizer: colorstring.Colorize{
Colors: colorstring.DefaultColors,
Reset: true,
Disable: color,
},
}
}
// Println is a convenience wrapper for fmt.Println with support for color
// codes.
func (p *Printer) Println(a string) {
fmt.Println(p.colorizer.Color(a))
}
// Printf is a convenience wrapper for fmt.Printf with support for color
// codes.
func (p *Printer) Printf(format string, a ...interface{}) {
fmt.Printf(p.colorizer.Color(format), a...)
}
// PrintAlertmanagerConfig prints the current alertmanager config
func (p *Printer) PrintAlertmanagerConfig(config string, templates map[string]string) error {
// go-text-template
if !p.disableColor {
err := quick.Highlight(os.Stdout, config, "yaml", "terminal", "swapoff")
if err != nil {
return err
}
} else {
fmt.Println(config)
}
fmt.Printf("\nTemplates:\n")
for fn, template := range templates {
fmt.Println(fn + ":")
if !p.disableColor {
err := quick.Highlight(os.Stdout, template, "go-text-template", "terminal", "swapoff")
if err != nil {
return nil
}
} else {
fmt.Println(template)
}
}
return nil
}
// PrintRuleGroups prints the current alertmanager config
func (p *Printer) PrintRuleGroups(rules map[string][]rwrulefmt.RuleGroup) error {
encodedRules, err := yaml.Marshal(&rules)
if err != nil {
return err
}
// go-text-template
if !p.disableColor {
return quick.Highlight(os.Stdout, string(encodedRules), "yaml", "terminal", "swapoff")
}
fmt.Println(string(encodedRules))
return nil
}
// PrintRuleGroup prints the current alertmanager config
func (p *Printer) PrintRuleGroup(rule rwrulefmt.RuleGroup) error {
encodedRule, err := yaml.Marshal(&rule)
if err != nil {
return err
}
// go-text-template
if !p.disableColor {
return quick.Highlight(os.Stdout, string(encodedRule), "yaml", "terminal", "swapoff")
}
fmt.Println(string(encodedRule))
return nil
}
// PrintComparisonResult prints the differences between the staged rules namespace
// and active rules namespace
func (p *Printer) PrintComparisonResult(results []rules.NamespaceChange, verbose bool) error {
created, updated, deleted := rules.SummarizeChanges(results)
// If any changes are detected, print the symbol legend
if (created + updated + deleted) > 0 {
fmt.Println("Changes are indicated with the following symbols:")
if created > 0 {
p.Println("[green] +[reset] created")
}
if updated > 0 {
p.Println("[yellow] ~[reset] updated")
}
if deleted > 0 {
p.Println("[red] -[reset] deleted")
}
fmt.Println()
fmt.Println("The following changes will be made if the provided rule set is synced:")
} else {
fmt.Println("no changes detected")
return nil
}
for _, change := range results {
switch change.State {
case rules.Created:
p.Printf("[green]+ Namespace: %v\n", change.Namespace)
for _, c := range change.GroupsCreated {
p.Printf("[green] + Group: %v\n", c.Name)
}
case rules.Updated:
p.Printf("[yellow]~ Namespace: %v\n", change.Namespace)
for _, c := range change.GroupsCreated {
p.Printf("[green] + Group: %v\n", c.Name)
}
for _, c := range change.GroupsUpdated {
p.Printf("[yellow] ~ Group: %v\n", c.New.Name)
// Print the full diff of the rules if verbose is set
if verbose {
newYaml, _ := yaml.Marshal(c.New)
separated := strings.Split(string(newYaml), "\n")
for _, l := range separated {
p.Printf("[green]+ %v\n", l)
}
oldYaml, _ := yaml.Marshal(c.Original)
separated = strings.Split(string(oldYaml), "\n")
for _, l := range separated {
p.Printf("[red]- %v\n", l)
}
}
}
for _, c := range change.GroupsDeleted {
p.Printf("[red] - Group: %v\n", c.Name)
}
case rules.Deleted:
p.Printf("[red]- Namespace: %v\n", change.Namespace)
for _, c := range change.GroupsDeleted {
p.Printf("[red] - Group: %v\n", c.Name)
}
}
}
fmt.Println()
fmt.Printf("Diff Summary: %v Groups Created, %v Groups Updated, %v Groups Deleted\n", created, updated, deleted)
return nil
}
func (p *Printer) PrintRuleSet(rules map[string][]rwrulefmt.RuleGroup, format string, writer io.Writer) error {
nsKeys := make([]string, 0, len(rules))
for k := range rules {
nsKeys = append(nsKeys, k)
}
sort.Strings(nsKeys)
type namespaceAndRuleGroup struct {
Namespace string `json:"namespace" yaml:"namespace"`
RuleGroup string `json:"rulegroup" yaml:"rulegroup"`
}
var items []namespaceAndRuleGroup
for _, ns := range nsKeys {
for _, rg := range rules[ns] {
items = append(items, namespaceAndRuleGroup{
Namespace: ns,
RuleGroup: rg.Name,
})
}
}
switch format {
case "json":
output, err := json.Marshal(items)
if err != nil {
return err
}
// go-text-template
if !p.disableColor {
return quick.Highlight(writer, string(output), "json", "terminal", "swapoff")
}
fmt.Fprint(writer, string(output))
case "yaml":
output, err := yaml.Marshal(items)
if err != nil {
return err
}
// go-text-template
if !p.disableColor {
return quick.Highlight(writer, string(output), "yaml", "terminal", "swapoff")
}
fmt.Fprint(writer, string(output))
default:
w := tabwriter.NewWriter(writer, 0, 0, 1, ' ', tabwriter.Debug)
fmt.Fprintln(w, "Namespace\t Rule Group")
for _, item := range items {
fmt.Fprintf(w, "%s\t %s\n", item.Namespace, item.RuleGroup)
}
w.Flush()
}
return nil
}