in confgenerator/logging_modify_fields.go [76:241]
func (p LoggingProcessorModifyFields) components(tag, uid string) ([]fluentbit.Component, error) {
var lua strings.Builder
lua.WriteString(`
function process(tag, timestamp, record)
`)
var components []fluentbit.Component
// Step 1: Obtain any source values needed for move or copy
fieldMappings := map[string]string{}
moveFromFields := []string{}
var dests []string
for dest, field := range p.Fields {
if field == nil {
// Nothing to do for this field
continue
}
dests = append(dests, dest)
}
sort.Strings(dests)
omitFilters := map[string]*filter.Filter{}
for i, dest := range dests {
field := p.Fields[dest]
if field.MoveFrom == "" && field.CopyFrom == "" && field.StaticValue == nil {
// Default to modifying field in place
field.CopyFrom = dest
}
for j, name := range []*string{&field.MoveFrom, &field.CopyFrom} {
if *name == "" {
continue
}
m, err := filter.NewMember(*name)
if err != nil {
return nil, fmt.Errorf("failed to parse field %q: %w", *name, err)
}
key, err := m.LuaAccessor(false)
if err != nil {
return nil, fmt.Errorf("failed to convert field %q to Lua accessor: %w", *name, err)
}
if _, ok := fieldMappings[key]; !ok {
new := fmt.Sprintf("__field_%d", i)
fieldMappings[key] = new
fmt.Fprintf(&lua, "local %s = %s;\n", new, key)
}
field.sourceVar = fieldMappings[key]
if j == 0 {
ra, err := m.LuaAccessor(true)
if err != nil {
return nil, fmt.Errorf("failed to convert %v to Lua accessor: %w", m, err)
}
moveFromFields = append(moveFromFields, ra)
}
}
if field.OmitIf != "" {
f, err := filter.NewFilter(field.OmitIf)
if err != nil {
return nil, fmt.Errorf("failed to parse filter %q: %w", field.OmitIf, err)
}
field.omitVar = fmt.Sprintf("omit%d", i)
omitFilters[field.omitVar] = f
}
}
// Step 2: OmitIf conditions
if len(omitFilters) > 0 {
fcomponents, flua := filter.AllFluentConfig(tag, omitFilters)
components = append(components, fcomponents...)
lua.WriteString(flua)
}
// Step 3: Remove any MoveFrom fields
sort.Strings(moveFromFields)
last := ""
for _, ra := range moveFromFields {
if last != ra {
fmt.Fprintf(&lua, `%s(nil);
`, ra)
}
last = ra
}
// Step 4: Assign values
for _, dest := range dests {
field := p.Fields[dest]
outM, err := filter.NewMember(dest)
if err != nil {
return nil, fmt.Errorf("failed to parse output field %q: %w", dest, err)
}
src := "nil"
if field.sourceVar != "" {
src = field.sourceVar
}
if field.StaticValue != nil {
src = filter.LuaQuote(*field.StaticValue)
}
fmt.Fprintf(&lua, "local v = %s;\n", src)
if field.DefaultValue != nil {
fmt.Fprintf(&lua, "if v == nil then v = %s end;\n", filter.LuaQuote(*field.DefaultValue))
}
// Process MapValues
var keys []string
for k := range field.MapValues {
keys = append(keys, k)
}
sort.Strings(keys)
for i, k := range keys {
if i > 0 {
lua.WriteString("else")
}
fmt.Fprintf(&lua, "if v == %s then v = %s\n", filter.LuaQuote(k), filter.LuaQuote(field.MapValues[k]))
}
if len(keys) > 0 {
if field.MapValuesExclusive {
lua.WriteString("else v = nil\n")
}
lua.WriteString("end\n")
}
// Process Type
var conv string
switch field.Type {
case "integer":
// Fluent-bit currently targets Lua 5.1, which uses the same type for numbers and integers.
// When converting back to msgpack, if a number can be represented as an integer, fluent-bit does so, otherwise it uses a float.
// If fluent-bit ever supports Lua 5.3, we can switch this to math.tointeger and use proper integers.
conv = "math.floor(tonumber(v))"
case "float":
conv = "tonumber(v)"
case "YesNoBoolean":
// Used by the mysql logging receiver; not allowed in user config by validation.
// First we check if v is truthy according to Lua (i.e. not nil).
// The "and" operator returns the first argument's value if it is false (so nil),
// so this expression produces true, false, or nil depending on the input.
conv = `(v and v == "Yes")`
}
if conv != "" {
// Leave existing string value if not convertible
fmt.Fprintf(&lua, `
local v2 = %s
if v2 ~= fail then v = v2
end
`, conv)
}
// Omit if
if field.omitVar != "" {
fmt.Fprintf(&lua, "if %s then v = nil end;\n", field.omitVar)
}
ra, err := outM.LuaAccessor(true)
if err != nil {
return nil, fmt.Errorf("failed to convert %v to Lua accessor: %w", outM, err)
}
fmt.Fprintf(&lua, "%s(v)\n", ra)
}
lua.WriteString("return 2, timestamp, record\n")
lua.WriteString("end\n")
// Execute Lua code
components = append(components, fluentbit.LuaFilterComponents(tag, "process", lua.String())...)
return components, nil
}