1 Star 0 Fork 0

sqos / beats

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
mapstr.go 9.28 KB
一键复制 编辑 原始数据 按行查看 历史
package common
import (
"encoding/json"
"fmt"
"strings"
"github.com/pkg/errors"
)
// Event metadata constants. These keys are used within libbeat to identify
// metadata stored in an event.
const (
FieldsKey = "fields"
TagsKey = "tags"
)
var (
// ErrKeyNotFound indicates that the specified key was not found.
ErrKeyNotFound = errors.New("key not found")
)
// EventMetadata contains fields and tags that can be added to an event via
// configuration.
type EventMetadata struct {
Fields MapStr
FieldsUnderRoot bool `config:"fields_under_root"`
Tags []string
}
// MapStr is a map[string]interface{} wrapper with utility methods for common
// map operations like converting to JSON.
type MapStr map[string]interface{}
// Update copies all the key-value pairs from d to this map. If the key
// already exists then it is overwritten. This method does not merge nested
// maps.
func (m MapStr) Update(d MapStr) {
for k, v := range d {
m[k] = v
}
}
// DeepUpdate recursively copies the key-value pairs from d to this map.
// If the key is present and a map as well, the sub-map will be updated recursively
// via DeepUpdate.
func (m MapStr) DeepUpdate(d MapStr) {
for k, v := range d {
switch val := v.(type) {
case map[string]interface{}:
m[k] = deepUpdateValue(m[k], MapStr(val))
case MapStr:
m[k] = deepUpdateValue(m[k], val)
default:
m[k] = v
}
}
}
func deepUpdateValue(old interface{}, val MapStr) interface{} {
if old == nil {
return val
}
switch sub := old.(type) {
case MapStr:
sub.DeepUpdate(val)
return sub
case map[string]interface{}:
tmp := MapStr(sub)
tmp.DeepUpdate(val)
return tmp
default:
return val
}
}
// Delete deletes the given key from the map.
func (m MapStr) Delete(key string) error {
_, err := walkMap(key, m, opDelete)
return err
}
// CopyFieldsTo copies the field specified by key to the given map. It will
// overwrite the key if it exists. An error is returned if the key does not
// exist in the source map.
func (m MapStr) CopyFieldsTo(to MapStr, key string) error {
v, err := walkMap(key, m, opGet)
if err != nil {
return err
}
_, err = walkMap(key, to, mapStrOperation{putOperation{v}, true})
return err
}
// Clone returns a copy of the MapStr. It recursively makes copies of inner
// maps.
func (m MapStr) Clone() MapStr {
result := MapStr{}
for k, v := range m {
if innerMap, ok := tryToMapStr(v); ok {
v = innerMap.Clone()
}
result[k] = v
}
return result
}
// HasKey returns true if the key exist. If an error occurs then false is
// returned with a non-nil error.
func (m MapStr) HasKey(key string) (bool, error) {
hasKey, err := walkMap(key, m, opHasKey)
if err != nil {
return false, err
}
return hasKey.(bool), nil
}
// GetValue gets a value from the map. If the key does not exist then an error
// is returned.
func (m MapStr) GetValue(key string) (interface{}, error) {
return walkMap(key, m, opGet)
}
// Put associates the specified value with the specified key. If the map
// previously contained a mapping for the key, the old value is replaced and
// returned. The key can be expressed in dot-notation (e.g. x.y) to put a value
// into a nested map.
//
// If you need insert keys containing dots then you must use bracket notation
// to insert values (e.g. m[key] = value).
func (m MapStr) Put(key string, value interface{}) (interface{}, error) {
return walkMap(key, m, mapStrOperation{putOperation{value}, true})
}
// StringToPrint returns the MapStr as pretty JSON.
func (m MapStr) StringToPrint() string {
json, err := json.MarshalIndent(m, "", " ")
if err != nil {
return fmt.Sprintf("Not valid json: %v", err)
}
return string(json)
}
// String returns the MapStr as JSON.
func (m MapStr) String() string {
bytes, err := json.Marshal(m)
if err != nil {
return fmt.Sprintf("Not valid json: %v", err)
}
return string(bytes)
}
// Flatten flattens the given MapStr and returns a flat MapStr.
//
// Example:
// "hello": MapStr{"world": "test" }
//
// This is converted to:
// "hello.world": "test"
//
// This can be useful for testing or logging.
func (m MapStr) Flatten() MapStr {
return flatten("", m, MapStr{})
}
// flatten is a helper for Flatten. See docs for Flatten. For convenience the
// out parameter is returned.
func flatten(prefix string, in, out MapStr) MapStr {
for k, v := range in {
var fullKey string
if prefix == "" {
fullKey = k
} else {
fullKey = fmt.Sprintf("%s.%s", prefix, k)
}
if m, ok := tryToMapStr(v); ok {
flatten(fullKey, m, out)
} else {
out[fullKey] = v
}
}
return out
}
// MapStrUnion creates a new MapStr containing the union of the
// key-value pairs of the two maps. If the same key is present in
// both, the key-value pairs from dict2 overwrite the ones from dict1.
func MapStrUnion(dict1 MapStr, dict2 MapStr) MapStr {
dict := MapStr{}
for k, v := range dict1 {
dict[k] = v
}
for k, v := range dict2 {
dict[k] = v
}
return dict
}
// MergeFields merges the top-level keys and values in each source map (it does
// not perform a deep merge). If the same key exists in both, the value in
// fields takes precedence. If underRoot is true then the contents of the fields
// MapStr is merged with the value of the 'fields' key in ms.
//
// An error is returned if underRoot is true and the value of ms.fields is not a
// MapStr.
func MergeFields(ms, fields MapStr, underRoot bool) error {
if ms == nil || len(fields) == 0 {
return nil
}
fieldsMS := ms
if !underRoot {
f, ok := ms[FieldsKey]
if !ok {
fieldsMS = make(MapStr, len(fields))
ms[FieldsKey] = fieldsMS
} else {
// Use existing 'fields' value.
var err error
fieldsMS, err = toMapStr(f)
if err != nil {
return err
}
}
}
// Add fields and override.
for k, v := range fields {
fieldsMS[k] = v
}
return nil
}
// AddTags appends a tag to the tags field of ms. If the tags field does not
// exist then it will be created. If the tags field exists and is not a []string
// then an error will be returned. It does not deduplicate the list of tags.
func AddTags(ms MapStr, tags []string) error {
if ms == nil || len(tags) == 0 {
return nil
}
eventTags, exists := ms[TagsKey]
if !exists {
ms[TagsKey] = tags
return nil
}
switch arr := eventTags.(type) {
case []string:
ms[TagsKey] = append(arr, tags...)
case []interface{}:
for _, tag := range tags {
arr = append(arr, tag)
}
ms[TagsKey] = arr
default:
return errors.Errorf("expected string array by type is %T", eventTags)
}
return nil
}
// toMapStr performs a type assertion on v and returns a MapStr. v can be either
// a MapStr or a map[string]interface{}. If it's any other type or nil then
// an error is returned.
func toMapStr(v interface{}) (MapStr, error) {
m, ok := tryToMapStr(v)
if !ok {
return nil, errors.Errorf("expected map but type is %T", v)
}
return m, nil
}
func tryToMapStr(v interface{}) (MapStr, bool) {
switch m := v.(type) {
case MapStr:
return m, true
case map[string]interface{}:
return MapStr(m), true
default:
return nil, false
}
}
// walkMap walks the data MapStr to arrive at the value specified by the key.
// The key is expressed in dot-notation (eg. x.y.z). When the key is found then
// the given mapStrOperation is invoked.
func walkMap(key string, data MapStr, op mapStrOperation) (interface{}, error) {
var err error
keyParts := strings.Split(key, ".")
// Walk maps until reaching a leaf object.
m := data
for i, k := range keyParts[0 : len(keyParts)-1] {
v, exists := m[k]
if !exists {
if op.CreateMissingKeys {
newMap := MapStr{}
m[k] = newMap
m = newMap
continue
}
return nil, errors.Wrapf(ErrKeyNotFound, "key=%v", strings.Join(keyParts[0:i+1], "."))
}
m, err = toMapStr(v)
if err != nil {
return nil, errors.Wrapf(err, "key=%v", strings.Join(keyParts[0:i+1], "."))
}
}
// Execute the mapStrOperator on the leaf object.
v, err := op.Do(keyParts[len(keyParts)-1], m)
if err != nil {
return nil, errors.Wrapf(err, "key=%v", key)
}
return v, nil
}
// mapStrOperation types
// These are static mapStrOperation types that store no state and are reusable.
var (
opDelete = mapStrOperation{deleteOperation{}, false}
opGet = mapStrOperation{getOperation{}, false}
opHasKey = mapStrOperation{hasKeyOperation{}, false}
)
// mapStrOperation represents an operation that can be applied to map.
type mapStrOperation struct {
mapStrOperator
CreateMissingKeys bool
}
// mapStrOperator is an interface with a single function that performs an
// operation on a MapStr.
type mapStrOperator interface {
Do(key string, data MapStr) (value interface{}, err error)
}
type deleteOperation struct{}
func (op deleteOperation) Do(key string, data MapStr) (interface{}, error) {
value, found := data[key]
if !found {
return nil, ErrKeyNotFound
}
delete(data, key)
return value, nil
}
type getOperation struct{}
func (op getOperation) Do(key string, data MapStr) (interface{}, error) {
value, found := data[key]
if !found {
return nil, ErrKeyNotFound
}
return value, nil
}
type hasKeyOperation struct{}
func (op hasKeyOperation) Do(key string, data MapStr) (interface{}, error) {
_, found := data[key]
return found, nil
}
type putOperation struct {
Value interface{}
}
func (op putOperation) Do(key string, data MapStr) (interface{}, error) {
existingValue, _ := data[key]
data[key] = op.Value
return existingValue, nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/sqos/beats.git
git@gitee.com:sqos/beats.git
sqos
beats
beats
v6.1.4

搜索帮助

344bd9b3 5694891 D2dac590 5694891