Skip to content

Commit

Permalink
Merge pull request #9 from budougumi0617/update-readme
Browse files Browse the repository at this point in the history
update readme
  • Loading branch information
budougumi0617 authored Jan 11, 2021
2 parents 21f61cb + e54d397 commit b9f0db7
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 13 deletions.
4 changes: 4 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ before:
builds:
-
main: ./cmd/nrseg
ldflags:
- -s -w
- -X nrseg.Version={{.Version}}
- -X nrseg.Revision={{.ShortCommit}}
env:
- CGO_ENABLED=0
archives:
Expand Down
154 changes: 154 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,159 @@ nrseg
[![test](https://github.com/budougumi0617/nrseg/workflows/test/badge.svg)](https://github.com/budougumi0617/nrseg/actions?query=workflow%3Atest)
[![reviewdog](https://github.com/budougumi0617/nrseg/workflows/reviewdog/badge.svg)](https://github.com/budougumi0617/nrseg/actions?query=workflow%3Areviewdog)

## Background
https://docs.newrelic.com/docs/agents/go-agent/instrumentation/instrument-go-segments

NewRelic is excellent o11y service, but if we use Newrelic in Go app, we need to insert `segment` into every function/method to measure the time taken by functions and code blocks.
For example, we can use with `newrelic.FromContext` and `defer` statement.

```go
func SampleFunc(ctx context.Context) {
defer newrelic.FromContext(ctx).StartSegment("sample_func").End()
// do anything...
}
```

If there is your application in production already, you must add a segment into any function/method. It is a very time-consuming and tedious task.

## Description
`nrseg` is cli tool for insert segment into all function/method in specified directory.

Before code is below,
```go
package input

import (
"context"
"fmt"
"net/http"
)

type S struct{}

func (s *S) SampleMethod(ctx context.Context) {
fmt.Println("Hello, playground")
fmt.Println("end function")
}

func SampleFunc(ctx context.Context) {
fmt.Println("Hello, playground")
fmt.Println("end function")
}

func SampleHandler(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Hello, %q", req.URL.Path)
}

// nrseg:ignore you can be ignored if you want to not insert segment.
func IgnoreHandler(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Hello, %q", req.URL.Path)
}
```

After execute `nrseg`, modified code is below.

```go
package input

import (
"context"
"fmt"
"net/http"

"github.com/newrelic/go-agent/v3/newrelic"
)

type S struct{}

func (s *S) SampleMethod(ctx context.Context) {
defer newrelic.FromContext(ctx).StartSegment("sample_method").End()
fmt.Println("Hello, playground")
fmt.Println("end function")
}

func SampleFunc(ctx context.Context) {
defer newrelic.FromContext(ctx).StartSegment("sample_func").End()
fmt.Println("Hello, playground")
fmt.Println("end function")
}

func SampleHandler(w http.ResponseWriter, req *http.Request) {
defer newrelic.FromContext(req.Context()).StartSegment("sample_handler").End()
fmt.Fprintf(w, "Hello, %q", req.URL.Path)
}

// nrseg:ignore you can be ignored if you want to not insert segment.
func IgnoreHandler(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Hello, %q", req.URL.Path)
}
```

### Features
- [x] Insert `Function segments` into the function with following arguments.
- The function/method signature has `context.Context`.
- `defer newrelic.FromContext(ctx).StartSegment("func_name").End()`
- The function/method signature has `*http.Request`.
- `defer newrelic.FromContext(req.Context()).StartSegment("func_name").End()`
- [x] Support any variable name of `context.Context`/`*http.Request`.
- [x] Use function/method name to segment name.
- [x] This processing is recursively repeated.
- [x] Able to ignore function/method by `nrseg:ignore` comment.
- [x] Ignore specified directories with cli option `-i`/`-ignore`.
- [ ] Remove all `Function segments`.

## Synopsis
```
$ nrseg -i testuitl ./
```

## Options

```
$ nrseg -h
Insert function segments into any function/method for Newrelic APM.
Usage of nrseg:
-i string
ignore directory names. ex: foo,bar,baz
(testdata directory is always ignored.)
-ignore string
ignore directory names. ex: foo,bar,baz
(testdata directory is always ignored.)
-v print version information and quit.
-version
print version information and quit.
exit status 1
```

## Installation

```
$ go install github.com/budougumi0617/nrseg/cmd/nrseg
```

Built binaries are available on gihub releases. https://github.com/budougumi0617/nrseg/releases

### MacOS
If you want to install on MacOS, you can use Homebrew.
```
brew install budougumi0617/tap/nrseg
```

## Contribution
1. Fork ([https://github.com/budougumi0617/nrseg/fork](https://github.com/budougumi0617/nrseg/fork))
2. Create a feature branch
3. Commit your changes
4. Rebase your local changes against the master branch
5. Run test suite with the `go test ./...` command and confirm that it passes
6. Run `gofmt -s`
7. Create new Pull Request

## License

[MIT](https://github.com/budougumi0617/nrseg/blob/master/LICENSE)

## Author
[budougumi0617](https://github.com/budougumi0617)

26 changes: 13 additions & 13 deletions nrseg.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ var (
)

var (
version = "dev"
Version = "devel"
Revision = "unset"
)

type nrseg struct {
Expand All @@ -32,22 +33,30 @@ func fill(args []string, outStream, errStream io.Writer) (*nrseg, error) {
cn := args[0]
flags := flag.NewFlagSet(cn, flag.ContinueOnError)
flags.SetOutput(errStream)
flags.Usage = func() {
fmt.Fprintf(
flag.CommandLine.Output(),
"Insert function segments into any function/method for Newrelic APM.\n\nUsage of %s:\n",
os.Args[0],
)
flags.PrintDefaults()
}

var v bool
vdesc := "print version information and quit."
flags.BoolVar(&v, "version", false, vdesc)
flags.BoolVar(&v, "v", false, vdesc)

var ignoreDirs string
idesc := "ignore directory names. ex: foo,bar,baz"
idesc := "ignore directory names. ex: foo,bar,baz\n(testdata directory is always ignored.)"
flags.StringVar(&ignoreDirs, "ignore", "", idesc)
flags.StringVar(&ignoreDirs, "i", "", idesc)

if err := flags.Parse(args[1:]); err != nil {
return nil, err
}
if v {
fmt.Fprintf(errStream, "%s version %s\n", cn, version)
fmt.Fprintf(errStream, "%s version %q, revison %q\n", cn, Version, Revision)
return nil, ErrShowVersion
}

Expand All @@ -59,7 +68,7 @@ func fill(args []string, outStream, errStream io.Writer) (*nrseg, error) {
dir := "./"
nargs := flags.Args()
if len(nargs) > 1 {
msg := "execution path must be only one or no-set(current dirctory)."
msg := "execution path must be only one or no-set(current directory)."
return nil, fmt.Errorf(msg)
}
if len(nargs) == 1 {
Expand Down Expand Up @@ -87,7 +96,6 @@ func (n *nrseg) skipDir(p string) bool {

func (n *nrseg) run() error {
return filepath.Walk(n.in, func(path string, info os.FileInfo, err error) error {
fmt.Fprintf(n.outStream, "walk %q\n", path)
if info.IsDir() && n.skipDir(path) {
return filepath.SkipDir
}
Expand All @@ -102,32 +110,24 @@ func (n *nrseg) run() error {
return nil
}

fmt.Fprintf(n.outStream, "start %q\n", path)
f, err := os.OpenFile(path, os.O_RDWR, 0664)
if err != nil {
fmt.Fprintf(n.errStream, "cannot open %q: %v\n", path, err)
return err
}
defer f.Close()
org, err := ioutil.ReadAll(f)
if err != nil {
fmt.Fprintf(n.errStream, "cannot read %q: %v\n", path, err)
return err
}
got, err := Process(path, org)
if err != nil {
fmt.Fprintf(n.errStream, "process failed %q: %v\n", path, err)
return err
}
fmt.Fprintf(n.outStream, "got %q\n", got)
if !bytes.Equal(org, got) {
fmt.Fprintf(n.outStream, "update!! %q\n", path)
if len(n.dist) != 0 && n.in != n.dist {
fmt.Fprintf(n.outStream, "update!! %q\n", n.dist)
return n.writeOtherPath(n.in, n.dist, path, got)
}
if _, err := f.WriteAt(got, 0); err != nil {
fmt.Fprintf(n.errStream, "file update failed %q: %v\n", path, err)
return err
}
}
Expand Down

0 comments on commit b9f0db7

Please sign in to comment.