Skip to content

Commit

Permalink
add an option to log the config in reverse order to make viewing in G…
Browse files Browse the repository at this point in the history
…rafana easier.

Also added this capability to Promtail
  • Loading branch information
slim-bean committed Jul 11, 2020
1 parent 02c923b commit bd28c2b
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 86 deletions.
89 changes: 7 additions & 82 deletions cmd/loki/main.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package main

import (
"errors"
"flag"
"fmt"
"io"
"os"
"reflect"
"strings"
Expand All @@ -14,11 +12,11 @@ import (
"github.com/prometheus/common/version"
"github.com/weaveworks/common/logging"
"github.com/weaveworks/common/tracing"
"gopkg.in/yaml.v2"

_ "github.com/grafana/loki/pkg/build"
"github.com/grafana/loki/pkg/cfg"
"github.com/grafana/loki/pkg/loki"
logutil "github.com/grafana/loki/pkg/util"

"github.com/cortexproject/cortex/pkg/util"

Expand All @@ -34,8 +32,8 @@ var lineReplacer = strings.NewReplacer("\n", "\\n ")
func main() {
printVersion := flag.Bool("version", false, "Print this builds version information")
printConfig := flag.Bool("print-config-stderr", false, "Dump the entire Loki config object to stderr")
printConfigInline := flag.Bool("print-config-stderr-inline", false, "Dump the entire Loki config object to stderr broken up by sections with escaped newline characters, "+
"this will display much better when captured by Loki and shown in Grafana")
logConfig := flag.Bool("log-config-reverse-order", false, "Dump the entire Loki config object at Info log "+
"level with the order reversed, reversing the order makes viewing the entries easier in Grafana.")

var config loki.Config
if err := cfg.Parse(&config); err != nil {
Expand Down Expand Up @@ -68,16 +66,16 @@ func main() {
}

if *printConfig {
err := dumpConfig(os.Stderr, &config, false)
err := logutil.PrintConfig(os.Stderr, &config)
if err != nil {
level.Error(util.Logger).Log("msg", "failed to print config to stderr", "err", err.Error())
}
}

if *printConfigInline {
err := dumpConfig(os.Stderr, &config, true)
if *logConfig {
err := logutil.LogConfig(&config)
if err != nil {
level.Error(util.Logger).Log("msg", "failed to print config to stderr", "err", err.Error())
level.Error(util.Logger).Log("msg", "failed to log config object", "err", err.Error())
}
}

Expand Down Expand Up @@ -106,76 +104,3 @@ func main() {
err = t.Run()
util.CheckFatal("running loki", err)
}

func dumpConfig(w io.Writer, config *loki.Config, escapeNewlines bool) error {
if !escapeNewlines {
lc, err := yaml.Marshal(&config)
if err != nil {
return err
}
fmt.Fprintf(w, "---\n# Loki Config\n# %s\n%s\n\n", version.Info(), string(lc))
return nil
}

s := reflect.ValueOf(config).Elem()
typeOfT := s.Type()

for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
if !f.CanInterface() {
continue
}
// Lookup the yaml tag to print the field name by the yaml tag, skip if it's not present or empty
if alias, ok := typeOfT.Field(i).Tag.Lookup("yaml"); ok {
if alias == "" {
continue
} else {
// yaml tags can contain other data like `omitempty` so split by comma and only return first result
fmt.Fprint(w, strings.Split(alias, ",")[0]+": ")
}
} else {
continue
}

if f.Kind() == reflect.Slice {
return errors.New("the config dumping function was not programmed to handle a configuration array as a top level item in the Loki config")
}

kind := f.Kind()
val := f.Interface()

// Dereference any pointers so we can properly determine the kind of the underlying object
if f.Kind() == reflect.Ptr {
if !f.IsNil() {
kind = f.Elem().Kind()
val = f.Elem().Interface()
}
}

// If the field is a struct, unmarshal it with a yaml unmarshaller and print it
if kind == reflect.Struct {
err := unmarshall(w, val)
if err != nil {
return err
}
} else {
// If it's not a struct, just print the value
fmt.Fprint(w, val)
}
fmt.Fprint(w, "\n")
}

os.Exit(0)

return nil
}

func unmarshall(w io.Writer, v interface{}) error {
fmt.Fprint(w, "\\n")
sc, err := yaml.Marshal(v)
if err != nil {
return err
}
fmt.Fprint(w, lineReplacer.Replace(string(sc)))
return nil
}
18 changes: 18 additions & 0 deletions cmd/promtail/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/grafana/loki/pkg/logentry/stages"
"github.com/grafana/loki/pkg/promtail"
"github.com/grafana/loki/pkg/promtail/config"
logutil "github.com/grafana/loki/pkg/util"
)

func init() {
Expand All @@ -28,6 +29,9 @@ func init() {
func main() {
printVersion := flag.Bool("version", false, "Print this builds version information")
dryRun := flag.Bool("dry-run", false, "Start Promtail but print entries instead of sending them to Loki.")
printConfig := flag.Bool("print-config-stderr", false, "Dump the entire Loki config object to stderr")
logConfig := flag.Bool("log-config-reverse-order", false, "Dump the entire Loki config object at Info log "+
"level with the order reversed, reversing the order makes viewing the entries easier in Grafana.")

// Load config, merging config file and CLI flags
var config config.Config
Expand Down Expand Up @@ -58,6 +62,20 @@ func main() {
stages.Debug = true
}

if *printConfig {
err := logutil.PrintConfig(os.Stderr, &config)
if err != nil {
level.Error(util.Logger).Log("msg", "failed to print config to stderr", "err", err.Error())
}
}

if *logConfig {
err := logutil.LogConfig(&config)
if err != nil {
level.Error(util.Logger).Log("msg", "failed to log config object", "err", err.Error())
}
}

p, err := promtail.New(config, *dryRun)
if err != nil {
level.Error(util.Logger).Log("msg", "error creating promtail", "error", err)
Expand Down
8 changes: 5 additions & 3 deletions docs/best-practices/current-best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ Lots of small, unfilled chunks are currently kryptonite for Loki. We are always

If you have an application that can log fast enough to fill these chunks quickly (much less than `max_chunk_age`), then it becomes more reasonable to use dynamic labels to break that up into separate streams.

## 8. Use `-print-config-stderr`
## 8. Use `-print-config-stderr` or `-log-config-reverse-order`

Starting in version 1.6.0 Loki has a flag which will dump the entire config object to stderr when Loki starts.
Starting in version 1.6.0 Loki and Promtail have flags which will dump the entire config object to stderr, or the log file, when they start.

We run Loki with this flag in all our environments and suggest you do too, it can be invaluable for debugging configuration issues as well as confirming the configuration Loki is currently running with.
`-print-config-stderr` is nice when running loki directly e.g. `./loki ` as you can get a quick output of the entire Loki config.

`-log-config-reverse-order` is the flag we run Loki with in all our environments, the config entries are reversed so that the order of configs reads correctly top to bottom when viewed in Grafana's Explore.
20 changes: 20 additions & 0 deletions docs/clients/promtail/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Promtail is configured in a YAML file (usually referred to as `config.yaml`)
which contains information on the Promtail server, where positions are stored,
and how to scrape logs from files.

* [Printing Promtail Config At Runtime](#printing-promtail-config-at-runtime)
* [Configuration File Reference](#configuration-file-reference)
* [server_config](#server_config)
* [client_config](#client_config)
Expand Down Expand Up @@ -37,6 +38,25 @@ and how to scrape logs from files.
* [Example Journal Config](#example-journal-config)
* [Example Syslog Config](#example-syslog-config)

## Printing Promtail Config At Runtime

If you pass Promtail the flag `-print-config-stderr` or `-log-config-reverse-order`, (or `-print-config-stderr=true`)
Promtail will dump the entire config object it has created from the built in defaults combined first with
overrides from config file, and second by overrides from flags.

The result is the value for every config object in the Promtail config struct.

Some values may not be relevant to your install, this is expected as every option has a default value if it is being used or not.

This config is what Promtail will use to run, it can be invaluable for debugging issues related to configuration and
is especially useful in making sure your config files and flags are being read and loaded properly.

`-print-config-stderr` is nice when running Promtail directly e.g. `./promtail ` as you can get a quick output of the entire Promtail config.

`-log-config-reverse-order` is the flag we run Promtail with in all our environments, the config entries are reversed so
that the order of configs reads correctly top to bottom when viewed in Grafana's Explore.


## Configuration File Reference

To specify which configuration file to load, pass the `-config.file` flag at the
Expand Down
7 changes: 6 additions & 1 deletion docs/configuration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Configuration examples can be found in the [Configuration Examples](examples.md)

## Printing Loki Config At Runtime

If you pass Loki the flag `-print-config-stderr`, (or `-print-config-stderr=true`)
If you pass Loki the flag `-print-config-stderr` or `-log-config-reverse-order`, (or `-print-config-stderr=true`)
Loki will dump the entire config object it has created from the built in defaults combined first with
overrides from config file, and second by overrides from flags.

Expand All @@ -44,6 +44,11 @@ this is expected as every option has a default value if it is being used or not.
This config is what Loki will use to run, it can be invaluable for debugging issues related to configuration and
is especially useful in making sure your config files and flags are being read and loaded properly.

`-print-config-stderr` is nice when running Loki directly e.g. `./loki ` as you can get a quick output of the entire Loki config.

`-log-config-reverse-order` is the flag we run Loki with in all our environments, the config entries are reversed so
that the order of configs reads correctly top to bottom when viewed in Grafana's Explore.

## Configuration File Reference

To specify which configuration file to load, pass the `-config.file` flag at the
Expand Down
39 changes: 39 additions & 0 deletions pkg/util/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package util

import (
"fmt"
"io"
"strings"

"github.com/cortexproject/cortex/pkg/util"
"github.com/go-kit/kit/log/level"
"github.com/prometheus/common/version"
"gopkg.in/yaml.v2"
)

// LogConfig takes a pointer to a config object, marshalls it to YAML and prints each line in REVERSE order
// The reverse order makes display in Grafana in easier which typically sorts newest entries at the top.
func LogConfig(cfg interface{}) error {
lc, err := yaml.Marshal(cfg)
if err != nil {
return err
}

cfgStr := string(lc)
cfgStrs := strings.Split(cfgStr, "\n")
for i := len(cfgStrs) - 1; i >= 0; i-- {
level.Info(util.Logger).Log("type", "config", "msg", cfgStrs[i])
}
return nil
}

// PrintConfig will takes a pointer to a config object, marshalls it to YAML and prints the result to the provided writer
// unlike LogConfig, PrintConfig prints the object in naturally ocurring order.
func PrintConfig(w io.Writer, config interface{}) error {
lc, err := yaml.Marshal(config)
if err != nil {
return err
}
fmt.Fprintf(w, "---\n# Loki Config\n# %s\n%s\n\n", version.Info(), string(lc))
return nil
}

0 comments on commit bd28c2b

Please sign in to comment.