func()

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
}