Writing the WASM Program

2 minutes read

In this use case, we want to enrich the log data using location data already present in the logs. Specifically, the logs generated by the application already include an IpAddr field. We want to compute and add a region field (e.g., America/Asia) based on this field.

With Fluent Bit, there are no additional requirements for executing WASM plugins. We need to write a program in a language that can compile to WASM. In our case, we will use Golang.

package main

import (
   "fmt"
   "net"
   "strings"
   "unsafe"

   "github.com/valyala/fastjson"
)

//export go_filter
func go_filter(tag uint8, tag_len uint, time_sec uint, time_nsec uint, record uint8, record_len uint) *uint8 {

   brecord := unsafe.Slice(record, record_len)

   br := string(brecord)

   var p fastjson.Parser
   value, err := p.Parse(br)
   if err != nil {
       fmt.Println(err)
       return nil
   }
   obj, err := value.Object()
   if err != nil {
       fmt.Println(err)
       return nil }

   var ar fastjson.Arena

   var ipAddr string

   if obj.Get("ipAddr") != nil {
       ipAddr = obj.Get("ipAddr").String()
   }

   ipAddrTrimmed := strings.Trim(ipAddr, `"`)
   fr, err := getRegion(ipAddrTrimmed)
   if err != nil {
       obj.Set("region", ar.NewString("unknown"))
   } else {
       obj.Set("region", ar.NewString(fr))
   }

   s := obj.String()
   s += string(rune(0)) // Note: explicit null terminator.
   rv := []byte(s)

   return &rv[0]
}

// Continent represents a continent and its IP range
type Continent struct {
   Name string
   Start uint32
   End uint32
}

// Function to convert an IPv4 address to a 32-bit integer
func ipToUint32(ip net.IP) uint32 {
   ip = ip.To4()
   return uint32(ip[0])<<24 + uint32(ip[1])<<16 + uint32(ip[2])<<8 + uint32(ip[3])
}

// Function to determine the continent for a given IP address
func getRegion(ipStr string) (string, error) {
   // List of continents
   continents := []Continent{
       {"Africa", 0, 613566758},                    // ~1/7th of the total range
       {"Asia", 613566759, 1227133516},             // ~1/7th of the total range
       {"Europe", 1227133517, 1840700274},          // ~1/7th of the total range
       {"North America", 1840700275, 2454267032},   // ~1/7th of the total range
       {"Australia", 2454267033, 3067833790},       // ~1/7th of the total range
       {"South America", 3067833791, 3681400548},   // ~1/7th of the total range
       {"Antarctica", 3681400549, 4294967295},      // ~1/7th of the total range
   }

   ip := net.ParseIP(ipStr)
   if ip == nil {
       return "", fmt.Errorf("invalid IP address")
   }

   ipInt := ipToUint32(ip)

   for _, continent := range continents {
       if ipInt >= continent.Start && ipInt <= continent.End {
           return continent.Name, nil
       }
   }
   return "", fmt.Errorf("IP address not in any continent range")
}

func main() {}