Skip to content

Latest commit

 

History

History
87 lines (67 loc) · 3.89 KB

how-to-add-a-new-rule.md

File metadata and controls

87 lines (67 loc) · 3.89 KB

How to add a new rule

1. Create a new rule

We demonstrate how to inject code for a new package through an example. Here we choose os.Setenv() and inject logging code at the beginning of its function to record the key and value whenever user calls it.

First, we create the following file structure under the pkg/rules directory:

pkg/rules/os
├── rule.go
└── setup.go

The os directory is simply a directory name, it can be any name you like. rule.go is the rule file where we define the rule, the name is mandatory. setup.go is the setup file where we register the rule, it also can be any name you like.

package os

import "github.com/alibaba/opentelemetry-go-auto-instrumentation/api"

func init() {
    api.NewRule("os", "Setenv", "", "onEnterSetenv", "").Register()
}

In the rule.go file, we define a new rule for the os package. The NewRule function takes five arguments: the import path, the function name, the receiver type string, the onEnter function name, and the onExit function name.

  • The import path is the package path where the function is located, e.g. "github.com/example/pkg".
  • The function name is the name of the function we want to inject code into., e.g. "foo".
  • The receiver type string is the type of the receiver of the function, e.g. "*http.Client", or "" if the function is not a method.
  • The onEnter function name is the name of the function that will be injected at the beginning of the target function.
  • The onExit function name is the name of the function that will be injected at the end of the target function. It can be empty if we don't need to inject code at the end of the function.

setup.go contains the definition of onEnterSetenv:

//go:build ignore
package os

import (
    "fmt"
    "os"
)

func onEnterSetenv(call os.CallContext, key, value string) {
    fmt.Printf("Setting environment variable %s to %s", key, value)
}

The onEnterSetenv function is the probe code that will be injected at the beginning of the os.Setenv() function.

The heading //go:build ignore is a build constraint that tells the Go tool to ignore this file when building the package. This is because the file is not a part of the package, but is used as a template to generate the actual probe code.

That os.CallContext is a special type that is used to pass the context information between the trampoline function and the probe code. The os.CallContext type is defined in the os package and is automatically generated by the tool. You must add such parameter as the first parameter of the onEnterSetenv function, even if you don't use it.

The key, value string are the parameters of the os.Setenv() function, just copied from the original function signature func Setenv(key, value string) error.

2. Register the rule

After completing this process, we introduce our newly created package in rule_enabler.go to make the rule take effect:

package main

import (
    ...
    _ "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/os"
)

Dont forget recomplie the tool after adding the new rule, because currently rules is coded within the tool.

3. Verify the rule

To make sure the rule is working as expected, we can write a simple demo program to test it:

$ mkdir setenv && cd setenv
$ go mod init setenv
$ cat <<EOF > main.go
package main
import "os"
func main(){ os.Setenv("hello", "world") }
EOF
$ ~/otelbuild -- main.go
$ ./main
Setting environment variable hello to world%

The output shows that the rule is working as expected. The Setting environment variable hello to world message is printed when the os.Setenv() function is called, that is, the probe code is injected successfully.

There are many advanced features that is not covered in this example, such as the InstStructRule, InstFileRule and APIs of CallContext. You can refer to the existing rules in the pkg/rules directory for more information.