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/tools/doc-generator/main.go

153 lines
4.2 KiB

Add configuration documentation generation tool (#7916) **What this PR does / why we need it**: Add a tool to generate configuration flags documentation based on the flags properties defined on registration on the code. This tool is based on the [Mimir doc generation tool](https://github.com/grafana/mimir/tree/main/tools/doc-generator) and adapted according to Loki configuration specifications. Prior to this PR, the configuration flags documentation was dispersed across two sources: * [_index.md](https://github.com/grafana/loki/blob/5550cd65ecd2299b219d26501221df0b191d8a78/docs/sources/configuration/_index.md) * configuration flags registration in the code This meant that there was no single source of truth. In this PR, the previous `_index.md` file is replaced with the new file generated by the tool. The next step includes adding a CI step that validates if the _index.md file was generated according to the flags settings. This will be done in a follow-up PR. **NOTE:** this is not a documentation update PR. Apart from some minor typo fixes, the documentation changes on the code, were copied from the `_index.md` file. **Which issue(s) this PR fixes**: Fixes https://github.com/grafana/loki-private/issues/83 **Special notes for your reviewer**: Files: * [docs/sources/configuration/index.template](https://github.com/grafana/loki/blob/5550cd65ecd2299b219d26501221df0b191d8a78/docs/sources/configuration/index.template): template used to generate the final configuration file * [/docs/sources/configuration/_index.md](https://github.com/grafana/loki/blob/c32e5d0acb3cdacc9e50bb71a83a9ba42721e0e2/docs/sources/configuration/_index.md): file generated by tool * `loki/pkg` directory files updated with up-to-date documentation from `_index.md` file * [tools/doc-generator](https://github.com/grafana/loki/tree/5550cd65ecd2299b219d26501221df0b191d8a78/tools/doc-generator) directory with documentation generation tool. **Checklist** - [ ] Reviewed the `CONTRIBUTING.md` guide - [ ] Documentation added - [ ] Tests updated - [ ] `CHANGELOG.md` updated - [ ] Changes that require user attention or interaction to upgrade are documented in `docs/sources/upgrading/_index.md`
3 years ago
// SPDX-License-Identifier: AGPL-3.0-only
// Provenance-includes-location: https://github.com/cortexproject/cortex/blob/master/tools/doc-generator/main.go
// Provenance-includes-license: Apache-2.0
// Provenance-includes-copyright: The Cortex Authors.
package main
import (
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
"github.com/grafana/loki/pkg/loki"
"github.com/grafana/loki/tools/doc-generator/parse"
)
const (
maxLineWidth = 80
tabWidth = 2
)
func removeFlagPrefix(block *parse.ConfigBlock, prefix string) {
for _, entry := range block.Entries {
switch entry.Kind {
case parse.KindBlock:
// Skip root blocks
if !entry.Root {
removeFlagPrefix(entry.Block, prefix)
}
case parse.KindField:
if strings.HasPrefix(entry.FieldFlag, prefix) {
entry.FieldFlag = "<prefix>" + entry.FieldFlag[len(prefix):]
}
}
}
}
func annotateFlagPrefix(blocks []*parse.ConfigBlock) {
// Find duplicated blocks
groups := map[string][]*parse.ConfigBlock{}
for _, block := range blocks {
groups[block.Name] = append(groups[block.Name], block)
}
// For each duplicated block, we need to fix the CLI flags, because
// in the documentation each block will be displayed only once but
// since they're duplicated they will have a different CLI flag
// prefix, which we want to correctly document.
for _, group := range groups {
if len(group) == 1 {
continue
}
// We need to find the CLI flags prefix of each config block. To do it,
// we pick the first entry from each config block and then find the
// different prefix across all of them.
var flags []string
for _, block := range group {
for _, entry := range block.Entries {
if entry.Kind == parse.KindField {
if len(entry.FieldFlag) > 0 {
flags = append(flags, entry.FieldFlag)
}
break
}
}
}
var allPrefixes []string
for i, prefix := range parse.FindFlagsPrefix(flags) {
if len(prefix) > 0 {
group[i].FlagsPrefix = prefix
allPrefixes = append(allPrefixes, prefix)
}
}
// Store all found prefixes into each block so that when we generate the
// markdown we also know which are all the prefixes for each root block.
for _, block := range group {
block.FlagsPrefixes = allPrefixes
}
}
// Finally, we can remove the CLI flags prefix from the blocks
// which have one annotated.
for _, block := range blocks {
if block.FlagsPrefix != "" {
removeFlagPrefix(block, block.FlagsPrefix)
}
}
}
func generateBlocksMarkdown(blocks []*parse.ConfigBlock) string {
md := &markdownWriter{}
md.writeConfigDoc(blocks)
return md.string()
}
func main() {
// Parse the generator flags.
flag.Parse()
if flag.NArg() != 1 {
fmt.Fprintf(os.Stderr, "Usage: doc-generator template-file")
os.Exit(1)
}
templatePath := flag.Arg(0)
// In order to match YAML config fields with CLI flags, we map
// the memory address of the CLI flag variables and match them with
// the config struct fields' addresses.
cfg := &loki.Config{}
flags := parse.Flags(cfg)
// Parse the config, mapping each config field with the related CLI flag.
blocks, err := parse.Config(cfg, flags, parse.RootBlocks)
if err != nil {
fmt.Fprintf(os.Stderr, "An error occurred while generating the doc: %s\n", err.Error())
os.Exit(1)
}
// Annotate the flags prefix for each root block, and remove the
// prefix wherever encountered in the config blocks.
annotateFlagPrefix(blocks)
// Generate documentation markdown.
data := struct {
ConfigFile string
GeneratedFileWarning string
}{
GeneratedFileWarning: "<!-- DO NOT EDIT THIS FILE - This file has been automatically generated from its .template, regenerate with `make doc` from root directory. -->",
Add configuration documentation generation tool (#7916) **What this PR does / why we need it**: Add a tool to generate configuration flags documentation based on the flags properties defined on registration on the code. This tool is based on the [Mimir doc generation tool](https://github.com/grafana/mimir/tree/main/tools/doc-generator) and adapted according to Loki configuration specifications. Prior to this PR, the configuration flags documentation was dispersed across two sources: * [_index.md](https://github.com/grafana/loki/blob/5550cd65ecd2299b219d26501221df0b191d8a78/docs/sources/configuration/_index.md) * configuration flags registration in the code This meant that there was no single source of truth. In this PR, the previous `_index.md` file is replaced with the new file generated by the tool. The next step includes adding a CI step that validates if the _index.md file was generated according to the flags settings. This will be done in a follow-up PR. **NOTE:** this is not a documentation update PR. Apart from some minor typo fixes, the documentation changes on the code, were copied from the `_index.md` file. **Which issue(s) this PR fixes**: Fixes https://github.com/grafana/loki-private/issues/83 **Special notes for your reviewer**: Files: * [docs/sources/configuration/index.template](https://github.com/grafana/loki/blob/5550cd65ecd2299b219d26501221df0b191d8a78/docs/sources/configuration/index.template): template used to generate the final configuration file * [/docs/sources/configuration/_index.md](https://github.com/grafana/loki/blob/c32e5d0acb3cdacc9e50bb71a83a9ba42721e0e2/docs/sources/configuration/_index.md): file generated by tool * `loki/pkg` directory files updated with up-to-date documentation from `_index.md` file * [tools/doc-generator](https://github.com/grafana/loki/tree/5550cd65ecd2299b219d26501221df0b191d8a78/tools/doc-generator) directory with documentation generation tool. **Checklist** - [ ] Reviewed the `CONTRIBUTING.md` guide - [ ] Documentation added - [ ] Tests updated - [ ] `CHANGELOG.md` updated - [ ] Changes that require user attention or interaction to upgrade are documented in `docs/sources/upgrading/_index.md`
3 years ago
ConfigFile: generateBlocksMarkdown(blocks),
}
// Load the template file.
tpl := template.New(filepath.Base(templatePath))
tpl, err = tpl.ParseFiles(templatePath)
if err != nil {
fmt.Fprintf(os.Stderr, "An error occurred while loading the template %s: %s\n", templatePath, err.Error())
os.Exit(1)
}
// Execute the template to inject generated doc.
if err := tpl.Execute(os.Stdout, data); err != nil {
fmt.Fprintf(os.Stderr, "An error occurred while executing the template %s: %s\n", templatePath, err.Error())
os.Exit(1)
}
}