Skip to content

Commit

Permalink
Add --instrument-all and --rm-all options to generator (#73)
Browse files Browse the repository at this point in the history
* Add --instrument-all and --rm-all options to generator

These speed up the migration to/from autometrics for bigger, established
codebases.

* Add --inst-all, -i as aliases, more quickstart documentation


Fixes #72
  • Loading branch information
gagbo authored Oct 20, 2023
1 parent 5e4146a commit 02f531d
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 46 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ versioning](https://go.dev/doc/modules/version-numbers).

### Added

- [All] `autometrics` the go-generator binary accepts an `--instrument-all` flag, to process all
functions in the file even if they do not have any annotation
- [All] `autometrics` the go-generator binary accepts a `--rm-all` flag (that overrides the `--instrument-all` flag)
to remove autometrics from all annotated functions. This is useful to offboard autometrics after trying it:
```bash
AM_RM_ALL=true go generate ./... # Will remove all godoc and instrumentation calls
sed -i '/\/\/.*autometrics/d' **/*.go # A similar sed command will remove all comments containing 'autometrics'
# A go linter/formatter of your choice can then clean up all unused imports to remove the automatically added ones.
```

### Changed

### Deprecated
Expand Down
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,28 @@ compatibility comes out of the box).

### 3. Add directives for each function you want to instrument

#### 3a. The VERY quick way

Use find and sed to insert a `//go:generate` directive that will instrument all
the functions in all source files under the current directory:

(Replace `gsed` with `sed` on linux; `gsed` is installed with `brew gsed`)

``` bash
find . \
-type f \
-path ./vendor -prune -or \
-name '*.go*' \
-print0 | xargs -0 gsed -i -e '/package/{a\//go:generate autometrics --instrument-all --no-doc' -e ':a;n;ba}'
```

You can remove the `--no-doc` to get the full experience, but the generator will add a lot of comments if so.

#### 3b. The slower quick way

This grants you more control over what gets instrumented, but it is longer
to add.

> **Warning**
> You must both add the `//go:generate` directive, and one `//autometrics:inst`
directive per function you want to instrument
Expand All @@ -133,7 +155,7 @@ subsection to see details:

Once it is done, you can call the [generator](#4-generate-the-documentation-and-instrumentation-code)

#### For error-returning functions
##### For error-returning functions

<details><summary><i>Expand to instrument error returning functions</i></summary>

Expand Down Expand Up @@ -165,7 +187,7 @@ _must_ name the error return value. This is why we recommend to name the error
value you return for the function you want to instrument.
</details>

#### For HTTP handler functions
##### For HTTP handler functions

<details><summary><i>Expand to instrument HTTP handlers functions</i></summary>

Expand Down Expand Up @@ -477,6 +499,23 @@ instrumentation only, you have multiple options:
$ AM_NO_DOCGEN=true go generate ./...
```

##### Offboarding

If for some reason you want to stop using autometrics, the easiest way includes 3 steps.

First is to use the environment variable `AM_RM_ALL` to remove all generated documentation
and code:

``` console
$ AM_RM_ALL=true go generate ./...
```

Second step is to use grep/your text-editor/your IDE of choice to remove all lines starting with
`//go:generate autometrics` from your files so `go generate` will stop creating autometrics code.

Last step is to use your text-editor IDE to remove remnants:
- a "go imports" cleaner to remove the `autometrics` imports (or grep-ing again)
- remove the `autometrics.Init` call from the main entrypoint of your code.

# Contributing

Expand Down
4 changes: 4 additions & 0 deletions cmd/autometrics/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type args struct {
UseOtel bool `arg:"--otel" default:"false" help:"Use OpenTelemetry client library to instrument code instead of default Prometheus."`
AllowCustomLatencies bool `arg:"--custom-latency" default:"false" help:"Allow non-default latencies to be used in latency-based SLOs."`
DisableDocGeneration bool `arg:"--no-doc,env:AM_NO_DOCGEN" default:"false" help:"Disable documentation links generation for all instrumented functions. Has the same effect as --no-doc in the //autometrics:inst directive."`
ProcessAllFunctions bool `arg:"-i,--instrument-all,--inst-all,env:AM_INSTRUMENT_ALL" default:"false" help:"Instrument all function declared in the file to transform. Overwritten by the --rm-all argument if both are set."`
RemoveAllFunctions bool `arg:"--rm-all,env:AM_RM_ALL" default:"false" help:"Remove all function instrumentation in the file to transform."`
}

func (args) Version() string {
Expand Down Expand Up @@ -69,6 +71,8 @@ func main() {
args.PrometheusUrl,
args.AllowCustomLatencies,
args.DisableDocGeneration,
args.ProcessAllFunctions,
args.RemoveAllFunctions,
)
if err != nil {
log.Fatalf("error initialising autometrics context: %s", err)
Expand Down
10 changes: 9 additions & 1 deletion internal/autometrics/ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ type GeneratorContext struct {
//
// This can be set in the command for the generator or through the environment.
DisableDocGeneration bool
// Flag to ask the generator to process all function declaration even if they
// do not have any annotation. The flag is overriden by the RemoveEverything one.
InstrumentEverything bool
// Flag to ask the generator to only remove all autometrics generated code in the
// file.
RemoveEverything bool
// ImportMap maps the alias to import in the current file, to canonical names associated with that name.
ImportsMap map[string]string
}
Expand Down Expand Up @@ -143,11 +149,13 @@ func (c *GeneratorContext) SetCommentIdx(i int) {
c.FuncCtx.CommentIndex = i
}

func NewGeneratorContext(implementation autometrics.Implementation, prometheusUrl string, allowCustomLatencies, disableDocGeneration bool) (GeneratorContext, error) {
func NewGeneratorContext(implementation autometrics.Implementation, prometheusUrl string, allowCustomLatencies, disableDocGeneration, instrumentEverything, removeEverything bool) (GeneratorContext, error) {
ctx := GeneratorContext{
Implementation: implementation,
AllowCustomLatencies: allowCustomLatencies,
DisableDocGeneration: disableDocGeneration,
InstrumentEverything: instrumentEverything,
RemoveEverything: removeEverything,
RuntimeCtx: DefaultRuntimeCtxInfo(),
FuncCtx: GeneratorFunctionContext{},
ImportsMap: make(map[string]string),
Expand Down
30 changes: 15 additions & 15 deletions internal/generate/defer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func main(thisIsAContext context.Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -110,7 +110,7 @@ func main(thisIsAContext vanilla.Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -167,7 +167,7 @@ func main(thisIsAContext Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -224,7 +224,7 @@ func main(w http.ResponseWriter, req *http.Request) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -281,7 +281,7 @@ func main(w vanilla.ResponseWriter, req *vanilla.Request) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -338,7 +338,7 @@ func main(w ResponseWriter, req *Request) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -395,7 +395,7 @@ func main(thisIsAContext buffalo.Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -452,7 +452,7 @@ func main(thisIsAContext vanilla.Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -509,7 +509,7 @@ func main(thisIsAContext Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -568,7 +568,7 @@ func main(thisIsAContext echo.Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -627,7 +627,7 @@ func main(thisIsAContext vanilla.Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -686,7 +686,7 @@ func main(thisIsAContext Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -745,7 +745,7 @@ func main(thisIsAContext *gin.Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -804,7 +804,7 @@ func main(thisIsAContext *vanilla.Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down Expand Up @@ -863,7 +863,7 @@ func main(thisIsAContext *Context) {
" fmt.Println(hello) // line comment 3\n" +
"}\n"

ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true)
ctx, err := internal.NewGeneratorContext(autometrics.PROMETHEUS, defaultPrometheusInstanceUrl, false, true, false, false)
if err != nil {
t.Fatalf("error creating the generation context: %s", err)
}
Expand Down
17 changes: 15 additions & 2 deletions internal/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func GenerateDocumentationAndInstrumentation(ctx internal.GeneratorContext, sour
}
}

if !foundAmImport {
if !ctx.RemoveEverything && !foundAmImport {
err = addAutometricsImport(&ctx, fileTree)
if err != nil {
return "", fmt.Errorf("error adding the autometrics import: %w", err)
Expand Down Expand Up @@ -125,7 +125,7 @@ func GenerateDocumentationAndInstrumentation(ctx internal.GeneratorContext, sour

// walkFuncDeclaration uses the context to generate documentation and code if necessary for a function declaration in a file.
func walkFuncDeclaration(ctx *internal.GeneratorContext, funcDeclaration *dst.FuncDecl, moduleName string) error {
if ctx.FuncCtx.ImplImportName == "" {
if !ctx.RemoveEverything && ctx.FuncCtx.ImplImportName == "" {
if ctx.Implementation == autometrics.PROMETHEUS {
return fmt.Errorf("the source file is missing a %v import", AmPromPackage)
} else if ctx.Implementation == autometrics.OTEL {
Expand Down Expand Up @@ -155,6 +155,12 @@ func walkFuncDeclaration(ctx *internal.GeneratorContext, funcDeclaration *dst.Fu
err)
}

// Early exit if we wanted to remove everything
if ctx.RemoveEverything {
funcDeclaration.Decorations().Start.Replace(docComments...)
return nil
}

// Detect autometrics directive
err = parseAutometricsFnContext(ctx, docComments)
if err != nil {
Expand Down Expand Up @@ -250,6 +256,13 @@ func parseAutometricsFnContext(ctx *internal.GeneratorContext, commentGroup []st
}
}

// If the function didn't have any directive, BUT we asked to process all functions, we change the context still
if ctx.InstrumentEverything {
ctx.FuncCtx.CommentIndex = len(commentGroup)
ctx.RuntimeCtx = internal.DefaultRuntimeCtxInfo()
return nil
}

ctx.FuncCtx.CommentIndex = -1
ctx.RuntimeCtx = internal.DefaultRuntimeCtxInfo()
return nil
Expand Down
Loading

0 comments on commit 02f531d

Please sign in to comment.