Skip to content

Commit

Permalink
Add logp package and its minor dependencies (elastic#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
kvch committed Feb 15, 2022
1 parent d059cc9 commit 556ff76
Show file tree
Hide file tree
Showing 36 changed files with 4,981 additions and 7 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
This repository is the home to the common libraries used by Elastic Agent and Beats.

Provided packages:
* `github.com/elastic/elastic-agent-libs/config` the previous `config.go` file from `github.com/elastic/beats/v7/libbeat/common`. A minimal wrapper around `github.com/elastic/go-ucfg`. It contains helpers for merging and accessing configuration objects.
* `github.com/elastic/elastic-agent-libs/config` the previous `config.go` file from `github.com/elastic/beats/v7/libbeat/common`. A minimal wrapper around `github.com/elastic/go-ucfg`. It contains helpers for merging and accessing configuration objects and flags.
* `github.com/elastic/elastic-agent-libs/str` the previous `stringset.go` file from `github.com/elastic/beats/v7/libbeat/common`. It provides a string set implementation.
* `github.com/elastic/elastic-agent-libs/file` is responsible for rotating and writing input and output files.
* `github.com/elastic/elastic-agent-libs/logp` is the well known logger from libbeat.
290 changes: 290 additions & 0 deletions config/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package config

import (
"flag"
"strings"

ucfg "github.com/elastic/go-ucfg"
cfgflag "github.com/elastic/go-ucfg/flag"
)

// StringsFlag collects multiple usages of the same flag into an array of strings.
// Duplicate values will be ignored.
type StringsFlag struct {
list *[]string
isDefault bool
flag *flag.Flag
}

// SettingsFlag captures key/values pairs into an Config object.
// The flag backed by SettingsFlag can be used multiple times.
// Values are overwritten by the last usage of a key.
type SettingsFlag cfgflag.FlagValue

// flagOverwrite provides a flag value, which always overwrites the same setting
// in an Config object.
type flagOverwrite struct {
config *ucfg.Config
path string
value string
}

// StringArrFlag creates and registers a new StringsFlag with the given FlagSet.
// If no FlagSet is passed, flag.CommandLine will be used as target FlagSet.
func StringArrFlag(fs *flag.FlagSet, name, def, usage string) *StringsFlag {
var arr *[]string
if def != "" {
arr = &[]string{def}
} else {
arr = &[]string{}
}

return StringArrVarFlag(fs, arr, name, usage)
}

// StringArrVarFlag creates and registers a new StringsFlag with the given
// FlagSet. Results of the flag usage will be appended to `arr`. If the slice
// is not initially empty, its first value will be used as default. If the flag
// is used, the slice will be emptied first. If no FlagSet is passed,
// flag.CommandLine will be used as target FlagSet.
func StringArrVarFlag(fs *flag.FlagSet, arr *[]string, name, usage string) *StringsFlag {
if fs == nil {
fs = flag.CommandLine
}
f := NewStringsFlag(arr)
f.Register(fs, name, usage)
return f
}

// NewStringsFlag creates a new, but unregistered StringsFlag instance.
// Results of the flag usage will be appended to `arr`. If the slice is not
// initially empty, its first value will be used as default. If the flag is
// used, the slice will be emptied first.
func NewStringsFlag(arr *[]string) *StringsFlag {
if arr == nil {
panic("No target array")
}
return &StringsFlag{list: arr, isDefault: true}
}

// Register registers the StringsFlag instance with a FlagSet.
// A valid FlagSet must be used.
// Register panics if the flag is already registered.
func (f *StringsFlag) Register(fs *flag.FlagSet, name, usage string) {
if f.flag != nil {
panic("StringsFlag is already registered")
}

fs.Var(f, name, usage)
f.flag = fs.Lookup(name)
if f.flag == nil {
panic("Failed to lookup registered flag")
}

if len(*f.list) > 0 {
f.flag.DefValue = (*f.list)[0]
}
}

// String joins all it's values set into a comma-separated string.
func (f *StringsFlag) String() string {
if f == nil || f.list == nil {
return ""
}

l := *f.list
return strings.Join(l, ", ")
}

// SetDefault sets the flags new default value.
// This overwrites the contents in the backing array.
func (f *StringsFlag) SetDefault(v string) {
if f.flag != nil {
f.flag.DefValue = v
}

*f.list = []string{v}
f.isDefault = true
}

// Set is used to pass usage of the flag to StringsFlag. Set adds the new value
// to the backing array. The array will be emptied on Set, if the backing array
// still contains the default value.
func (f *StringsFlag) Set(v string) error {
// Ignore duplicates, can be caused by multiple flag parses
if f.isDefault {
*f.list = []string{v}
} else {
for _, old := range *f.list {
if old == v {
return nil
}
}
*f.list = append(*f.list, v)
}
f.isDefault = false
return nil
}

// Get returns the backing slice its contents as interface{}. The type used is
// `[]string`.
func (f *StringsFlag) Get() interface{} {
return f.List()
}

// List returns the current set values.
func (f *StringsFlag) List() []string {
return *f.list
}

// Type reports the type of contents (string) expected to be parsed by Set.
// It is used to build the CLI usage string.
func (f *StringsFlag) Type() string {
return "string"
}

// SettingFlag defines a setting flag, name and it's usage. The return value is
// the Config object settings are applied to.
func SettingFlag(fs *flag.FlagSet, name, usage string) *C {
cfg := NewConfig()
SettingVarFlag(fs, cfg, name, usage)
return cfg
}

// SettingVarFlag defines a setting flag, name and it's usage.
// Settings are applied to the Config object passed.
func SettingVarFlag(fs *flag.FlagSet, def *C, name, usage string) {
if fs == nil {
fs = flag.CommandLine
}

f := NewSettingsFlag(def)
fs.Var(f, name, usage)
}

// NewSettingsFlag creates a new SettingsFlag instance, not registered with any
// FlagSet.
func NewSettingsFlag(def *C) *SettingsFlag {
opts := append(
[]ucfg.Option{
ucfg.MetaData(ucfg.Meta{Source: "command line flag"}),
},
configOpts...,
)

tmp := cfgflag.NewFlagKeyValue(def.access(), true, opts...)
return (*SettingsFlag)(tmp)
}

func (f *SettingsFlag) access() *cfgflag.FlagValue {
return (*cfgflag.FlagValue)(f)
}

// Config returns the config object the SettingsFlag stores applied settings to.
func (f *SettingsFlag) Config() *C {
return fromConfig(f.access().Config())
}

// Set sets a settings value in the Config object. The input string must be a
// key-value pair like `key=value`. If the value is missing, the value is set
// to the boolean value `true`.
func (f *SettingsFlag) Set(s string) error {
return f.access().Set(s)
}

// Get returns the Config object used to store values.
func (f *SettingsFlag) Get() interface{} {
return f.Config()
}

// String always returns an empty string. It is required to fulfil
// the flag.Value interface.
func (f *SettingsFlag) String() string {
return ""
}

// Type reports the type of contents (setting=value) expected to be parsed by Set.
// It is used to build the CLI usage string.
func (f *SettingsFlag) Type() string {
return "setting=value"
}

// ConfigOverwriteFlag defines a new flag updating a setting in an Config
// object. The name is used as the flag its name the path parameter is the
// full setting name to be used when the flag is set.
func ConfigOverwriteFlag(
fs *flag.FlagSet,
config *C,
name, path, def, usage string,
) *string {
if config == nil {
panic("Missing configuration")
}
if path == "" {
panic("empty path")
}

if fs == nil {
fs = flag.CommandLine
}

if def != "" {
err := config.SetString(path, -1, def)
if err != nil {
panic(err)
}
}

f := newOverwriteFlag(config, path, def)
fs.Var(f, name, usage)
return &f.value
}

func newOverwriteFlag(c *C, path, def string) *flagOverwrite {
return &flagOverwrite{config: c.access(), path: path, value: def}
}

func (f *flagOverwrite) String() string {
return f.value
}

func (f *flagOverwrite) Set(v string) error {
opts := append(
[]ucfg.Option{
ucfg.MetaData(ucfg.Meta{Source: "command line flag"}),
},
configOpts...,
)

err := f.config.SetString(f.path, -1, v, opts...)
if err != nil {
return err
}
f.value = v
return nil
}

func (f *flagOverwrite) Get() interface{} {
return f.value
}

func (f *flagOverwrite) Type() string {
return "string"
}
Loading

0 comments on commit 556ff76

Please sign in to comment.