-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: polish how-to-add-a-new-rule.md (#33)
- Loading branch information
Showing
1 changed file
with
62 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,73 @@ | ||
# How to add a new rule | ||
|
||
To instrument a new framework, you call follow the steps: | ||
## 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. | ||
|
||
1. Create a new directory in the **pkg/rules** directory and create a new **rule.go** file | ||
2. Register a new rule that specifies the framework import path, function | ||
name, and the function signature you want to instrument, and onEnter/onExit | ||
functions you want to insert before/after calling original function, respectively, in the **rule.go** file, as shown | ||
below: | ||
```go | ||
package rules | ||
First, we create the following file structure under the `pkg/rules` directory: | ||
``` | ||
pkg/rules/os | ||
├── rule.go | ||
└── setup.go | ||
``` | ||
The `os` directory is the package name, and `rule.go` is the rule file where we define the rule. `setup.go` is the setup file where we register the rule. | ||
|
||
func init() { | ||
api.NewRule("framework_name", "function_name", "function_signature", "onEnterFunc", "onExitFunc").Register() | ||
} | ||
```go | ||
package os | ||
|
||
``` | ||
3. Implement the onEnter/onExit functions in the same directory with the **rule.go** file, as shown below: | ||
import "github.com/alibaba/opentelemetry-go-auto-instrumentation/api" | ||
|
||
```go | ||
package rules | ||
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 package name, the function name, the function signature, the onEnter function name, and the onExit function name. The `Register` function registers the rule. | ||
|
||
import framework_name | ||
`setup.go` contains the definition of `onEnterSetenv`: | ||
|
||
func onEnterFunc(ctx *framework_name.Context, arg1 int, arg2 bool) { | ||
println("onEnter") | ||
} | ||
```go | ||
//go:build ignore | ||
package os | ||
|
||
func onExitFunc(ctx *framework_name.Context, ret1 string) { | ||
println("onExit") | ||
} | ||
``` | ||
import ( | ||
"fmt" | ||
"os" | ||
) | ||
|
||
4. Import your new rule package by adding import statement within `rule_enabler.go` | ||
func onEnterSetenv(call os.CallContext, key, value string) { | ||
fmt.Printf("Setting environment variable %s to %s", key, value) | ||
} | ||
``` | ||
|
||
There are some concrete examples in the **pkg/rules** directory, such as **pkg/rules/test**. | ||
You can refer to them for more details. | ||
The `onEnterSetenv` function is the probe code that will be injected at the beginning of the `os.Setenv()` function. It takes the `os.CallContext` as the first argument and the key and value as the second and third arguments, respectively. | ||
|
||
## 2. Register the rule | ||
After completing this process, we introduce our newly created package in `rule_enabler.go` to make the rule take effect: | ||
|
||
```go | ||
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: | ||
|
||
```bash | ||
$ mkdir setenv | ||
$ go mod init setenv | ||
$ cat <<EOF > main.go | ||
package main | ||
import "os" | ||
func main(){ os.Setenv("hello", "world") } | ||
EOF | ||
$ ~/opentelemetry-go-auto-instrumentation/otel-go-auto-instrumentation -- 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. |