Skip to content

Latest commit

 

History

History
1666 lines (1315 loc) · 55.3 KB

plugin_guide.md

File metadata and controls

1666 lines (1315 loc) · 55.3 KB

Pinpoint Go Agent Plug-ins

plugin package source directory instrumented package
pphttp plugin/http Go standard HTTP package
ppbeego plugin/beego beego/beego/v2 package (https://github.com/beego/beego)
ppchi plugin/chi go-chi/chi package (https://github.com/go-chi/chi)
ppecho plugin/echo labstack/echo package (https://github.com/labstack/echo)
ppechov4 plugin/echov4 labstack/echo/v4 package (https://github.com/labstack/echo)
ppfasthttp plugin/fasthttp valyala/fasthttp package (https://github.com/valyala/fasthttp)
ppfasthttprouter plugin/fasthttprouter fasthttp/router package (https://github.com/fasthttp/router)
ppfiber plugin/fiber gofiber/fiber/v2 package (https://github.com/gofiber/fiber)
ppgin plugin/gin gin-gonic/gin package (https://github.com/gin-gonic/gin)
ppgocql plugin/gocql gocql package (https://github.com/gocql/gocql).
ppgoelastic plugin/goelastic elastic/go-elasticsearch package (https://github.com/elastic/go-elasticsearch)
ppgohbase plugin/gohbase tsuna/gohbase package (https://github.com/tsuna/gohbase).
ppgomemcache plugin/gomemcache bradfitz/gomemcache package (https://github.com/bradfitz/gomemcache)
ppgoredis plugin/goredis go-redis/redis package (https://github.com/go-redis/redis)
ppgoredisv7 plugin/goredisv7 go-redis/redis/v7 package (https://github.com/go-redis/redis)
ppgoredisv8 plugin/goredisv8 go-redis/redis/v8 package (https://github.com/go-redis/redis)
ppgoredisv9 plugin/goredisv9 redis/go-redis/v9 package (https://github.com/redis/go-redis)
ppgorilla plugin/gorilla gorilla/mux package (https://github.com/gorilla/mux).
ppgorm plugin/gorm go-gorm/gorm package (https://github.com/go-gorm/gorm)
ppgrpc plugin/grpc grpc/grpc-go package (https://github.com/grpc/grpc-go)
pphttprouter plugin/httprouter julienschmidt/httprouter package (https://github.com/julienschmidt/httprouter)
ppkratos plugin/kratos go-kratos/kratos/v2 package (https://github.com/go-kratos/kratos)
pplogrus plugin/logrus sirupsen/logrus package (https://github.com/sirupsen/logrus)
ppmongo plugin/mongodriver mongodb/mongo-go-driver package (https://github.com/mongodb/mongo-go-driver)
ppmssql plugin/mssql denisenkom/go-mssqldb package (https://github.com/denisenkom/go-mssqldb)
ppmysql plugin/mysql go-sql-driver/mysql package (https://github.com/go-sql-driver/mysql)
pporacle plugin/oracle sijms/go-ora/v2 package (https://github.com/sijms/go-ora)
pppgsql plugin/pgsql lib/pq package (https://github.com/lib/pq)
ppredigo plugin/redigo gomodule/redigo package (https://github.com/gomodule/redigo)
ppsarama plugin/sarama Shopify/sarama package (https://github.com/Shopify/sarama)

pphttp

http server

This package instruments inbound requests handled by a http.ServeMux. Use NewServeMux to trace all handlers:

mux := pphttp.NewServeMux()
mux.HandleFunc("/bar", outGoing)

Use WrapHandler or WrapHandlerFunc to select the handlers you want to track:

http.HandleFunc("/", pphttp.WrapHandlerFunc(index))

For each request, a pinpoint.Tracer is stored in the request context. By using the pinpoint.FromContext function, this tracer can be obtained in your handler. Alternatively, the context of the request may be propagated where the context that contains the pinpoint.Tracer is required.

package main

import (
    "net/http"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/http"
)

func outGoing(w http.ResponseWriter, r *http.Request) {
    tracer := pinpoint.FromContext(r.Context())
    ctx := pinpoint.NewContext(context.Background(), tracer)
    // or ctx := r.Context()
    
    client := pphttp.WrapClientWithContext(ctx, &http.Client{})
    resp, err := client.Get("http://localhost:9000/async_wrapper?foo=bar&say=goodbye")
    ...
}

func main() {
    //setup agent
    opts := []pinpoint.ConfigOption{
        pinpoint.WithConfigFile(os.Getenv("HOME") + "/tmp/pinpoint-config.yaml"),
    }
    cfg, err := pinpoint.NewConfig(opts...)
    agent, err := pinpoint.NewAgent(cfg)
    defer agent.Shutdown()

    mux := pphttp.NewServeMux()
    mux.HandleFunc("/bar", outGoing)
    http.ListenAndServe("localhost:8000", mux)
}

This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern. But, WrapHandler and WrapHandlerFunc function doesn't support URL Statistics feature.

Config Options

http client

This package instruments outbound requests and add distributed tracing headers. Use WrapClient, WrapClientWithContext or DoClient.

client := pphttp.WrapClient(&http.Client{})
client.Get(external_url)
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
pphttp.DoClient(http.DefaultClient.Do, req)

It is necessary to pass the context containing the pinpoint.Tracer to the http.Request.

import (
    "net/http"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/http"
)

func wrapClient(w http.ResponseWriter, r *http.Request) {
    req, _ := http.NewRequestWithContext(r.Context(), "GET", "http://localhost:9000/async", nil)
    client := pphttp.WrapClient(&http.Client{})
    resp, err := client.Do(req)
    ...
}

func main() {
    ... //setup agent

    http.HandleFunc("/wrapclient", pphttp.WrapHandlerFunc(wrapClient))
    http.ListenAndServe(":8000", nil)
}

Full Example Source

Config Options

ppbeego

server

This package instruments inbound requests handled by a beego instance. Register the ServerFilterChain as the filter chain of the router to trace all handlers:

web.InsertFilterChain("/*", ppbeego.ServerFilterChain())

For each request, a pinpoint.Tracer is stored in the request context. By using the pinpoint.FromContext function, this tracer can be obtained in your handler. Alternatively, the context of the request may be propagated where the context that contains the pinpoint.Tracer is required.

package main

import (
    "github.com/beego/beego/v2/server/web"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/beego"
)

func (m *MainController) Hello() {
    tracer := pinpoint.FromContext(m.Ctx.Request.Context())
    defer tracer.NewSpanEvent("f1").EndSpanEvent()

    m.Ctx.WriteString("hello, world!!")
}

func main() {
    //setup agent
 
	ctrl := &MainController{}
	web.Router("/hello", ctrl, "get:Hello")
	web.InsertFilterChain("/*", ppbeego.ServerFilterChain())
	web.Run("localhost:9000")
}

This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.

Config Options

client

This package instruments outbound requests and add distributed tracing headers. Add the ClientFilterChain as the filter chain of the request.

req := httplib.Get("http://localhost:9090/")
req.AddFilters(ppbeego.ClientFilterChain(tracer))
import (
	"github.com/beego/beego/v2/client/httplib"
	"github.com/pinpoint-apm/pinpoint-go-agent"
	"github.com/pinpoint-apm/pinpoint-go-agent/plugin/beego"
)

func (m *MainController) Get() {
    tracer := pinpoint.FromContext(m.Ctx.Request.Context())

    req := httplib.Get("http://localhost:9090/")
    req.AddFilters(ppbeego.ClientFilterChain(tracer))
    str, _ := req.String()
    m.Ctx.WriteString(str)
}

Full Example Source

Config Options

ppchi

This package instruments inbound requests handled by a chi.Router. Register the Middleware as the middleware of the router to trace all handlers:

r := chi.NewRouter()
r.Use(ppchi.Middleware())

Use WrapHandler or WrapHandlerFunc to select the handlers you want to track:

r.Get("/hello", ppchi.WrapHandlerFunc(hello))

For each request, a pinpoint.Tracer is stored in the request context. By using the pinpoint.FromContext function, this tracer can be obtained in your handler. Alternatively, the context of the request may be propagated where the context that contains the pinpoint.Tracer is required.

import (
    "net/http"

    "github.com/go-chi/chi"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/chi"
)

func hello(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "hello world")
}

func main() {
    ... //setup agent
	
    r := chi.NewRouter()
    r.Use(ppchi.Middleware())
    
    r.Get("/hello", hello)
    http.ListenAndServe(":8000", r)
}

Full Example Source

This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.

Config Options

ppecho

This package instruments inbound requests handled by an echo.Router. Register the Middleware as the middleware of the router to trace all handlers:

e := echo.New()
e.Use(ppecho.Middleware())

Use WrapHandler to select the handlers you want to track:

e.GET("/hello", ppecho.WrapHandler(hello))

For each request, a pinpoint.Tracer is stored in the request context. By using the pinpoint.FromContext function, this tracer can be obtained in your handler. Alternatively, the context of the request may be propagated where the context that contains the pinpoint.Tracer is required.

package main

import (
    "github.com/labstack/echo"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/echo"
)

func hello(c echo.Context) error {
    return c.String(200, "Hello World!!")
}

func main() {
    ... //setup agent
	
    e := echo.New()
    e.Use(ppecho.Middleware())

    e.GET("/hello", hello)
    e.Start(":9000")
}

Full Example Source

This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.

Config Options

ppechov4

This package instruments the echo/v4 package, and the APIs provided is the same as ppecho. Refer the ppecho paragraph.

ppfasthttp

server

This package instruments inbound requests handled by a fasthttp instance. Use WrapHandler to select the handlers you want to track:

fasthttp.ListenAndServe(":9000", ppfasthttp.WrapHandler(requestHandler))

WrapHandler sets the pinpoint.Tracer as a user value of fasthttp handler's context. By using the ppfasthttp.CtxKey, this tracer can be obtained in your handler. Alternatively, the context of the handler may be propagated where the context that contains the pinpoint.Tracer is required.

package main

import (
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/fasthttp"
    "github.com/valyala/fasthttp"
)

func requestHandler(ctx *fasthttp.RequestCtx) {
    tracer := pinpoint.FromContext(ctx.UserValue(ppfasthttp.CtxKey).(context.Context))
    defer tracer.NewSpanEvent("f1").EndSpanEvent()

    fmt.Fprintf(ctx, "Hello, world!\n\n")
}

func main() {
    ... //setup agent
	
    fasthttp.ListenAndServe(":9000", ppfasthttp.WrapHandler(requestHandler))
}

Full Example Source

This package supports URL Statistics feature. It aggregates response times, successes and failures for each pattern given as parameter of a WrapHandler function.

Config Options

client

This package instruments outbound requests and add distributed tracing headers. Use DoClient.

err := ppfasthttp.DoClient(func() error {
    return hc.Do(req, resp)
}, ctx, req, resp)

It is necessary to pass the context containing the pinpoint.Tracer to DoClient.

func client(ctx *fasthttp.RequestCtx) {
    url := fasthttp.AcquireURI()
    url.Parse(nil, []byte("http://localhost:8080/"))

    hc := &fasthttp.HostClient{Addr: "localhost:8080"}
    req := fasthttp.AcquireRequest()
    req.SetURI(url)
    resp := fasthttp.AcquireResponse()

    ctxWithTracer := ctx.UserValue(ppfasthttp.CtxKey).(context.Context)
    err := ppfasthttp.DoClient(func() error {
        return hc.Do(req, resp)
    }, ctxWithTracer, req, resp)

    ...
}

Full Example Source

Config Options

ppfasthttprouter

This package instruments inbound requests handled by a fasthttp/router.Router. Use New() to trace all handlers:

r := ppfasthttprouter.New()
r.GET("/user/{name}", user)

For each request, a pinpoint.Tracer is stored in the request context. By using the pinpoint.FromContext function, this tracer can be obtained in your handler. Alternatively, the context of the request may be propagated where the context that contains the pinpoint.Tracer is required.

import (
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/fasthttp"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/fasthttprouter"
    "github.com/valyala/fasthttp"
)

func user(ctx *fasthttp.RequestCtx) {
    tracer := pinpoint.FromContext(ctx.UserValue(ppfasthttp.CtxKey).(context.Context))
    defer tracer.NewSpanEvent("f1").EndSpanEvent()

    fmt.Fprintf(ctx, "hello, %s!\n", ctx.UserValue("name"))
}

func main() {
    ... //setup agent

    r := ppfasthttprouter.New()
    r.GET("/user/{name}", user)
    log.Fatal(fasthttp.ListenAndServe(":9000", r.Handler))
}

Full Example Source

This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.

Config Options

ppfiber

This package instruments inbound requests handled by a fiber instance. Register the Middleware as the middleware of the router to trace all handlers:

app := fiber.New()
app.Use(ppfiber.Middleware())

Use WrapHandler to select the handlers you want to track:

app.Get("/hello", ppfiber.WrapHandler(hello))

For each request, a pinpoint.Tracer is stored in the user context. By using the pinpoint.FromContext function, this tracer can be obtained in your handler. Alternatively, the context of the request may be propagated where the context that contains the pinpoint.Tracer is required.

package main

import (
    "github.com/gofiber/fiber/v2"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/fiber"
)

func hello(c *fiber.Ctx) error {
    tracer := pinpoint.FromContext(c.UserContext())
    defer tracer.NewSpanEvent("f1").EndSpanEvent()

    return c.SendString("Hello, World !!")
}

func main() {
    ... //setup agent
	
    app := fiber.New()
    app.Use(ppfiber.Middleware())
    log.Fatal(app.Listen(":9000"))
}

Full Example Source

This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.

Config Options

ppgin

This package instruments inbound requests handled by a gin.Engine. Register the Middleware as the middleware of the router to trace all handlers:

r := gin.Default()
r.Use(ppgin.Middleware())

Use WrapHandler to select the handlers you want to track:

r.GET("/external", ppgin.WrapHandler(extCall))

For each request, a pinpoint.Tracer is stored in the request context. By using the pinpoint.FromContext function, this tracer can be obtained in your handler. Alternatively, the context of the request may be propagated where the context that contains the pinpoint.Tracer is required.

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/gin"
)

func endpoint(c *gin.Context) {
    c.Writer.WriteString("endpoint")
}

func main() {
    ... //setup agent
	
    router := gin.Default()
    router.Use(ppgin.Middleware())

    router.GET("/endpoint", endpoint)
    router.Run(":8000")
}

Full Example Source

This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.

Config Options

ppgocql

This package instruments all queries created from gocql session. Use the NewObserver as the gocql.QueryObserver or gocql.BatchObserver:

cluster := gocql.NewCluster("127.0.0.1")

observer := ppgocql.NewObserver()
cluster.QueryObserver = observer
cluster.BatchObserver = observer

It is necessary to pass the context containing the pinpoint.Tracer using the pinpoint.WithContext function.

import (
    "github.com/gocql/gocql"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/gocql"
)

func doCassandra(w http.ResponseWriter, r *http.Request) {
    observer := ppgocql.NewObserver()
    cluster := gocql.NewCluster("127.0.0.1")
    cluster.QueryObserver = observer
    cluster.BatchObserver = observer

    session, _ := cluster.CreateSession()
    query := session.Query(`SELECT id, text FROM tweet WHERE timeline = ? LIMIT 1`, "me")
    err := query.WithContext(r.Context()).Consistency(gocql.One).Scan(&id, &text)
    ...
}

Full Example Source

ppgoelastic

This package instruments the elasticsearch calls. Use the NewTransport function as the elasticsearch.Client's Transport.

es, err := elasticsearch.NewClient(
    elasticsearch.Config{
        Transport: ppgoelastic.NewTransport(nil),
})

It is necessary to pass the context containing the pinpoint.Tracer to elasticsearch.Client.

ctx := pinpoint.NewContext(context.Background(), tracer)
res, err = es.Search(
    es.Search.WithContext(ctx),
    es.Search.WithIndex("test"),
    ...
)
import (
    "github.com/elastic/go-elasticsearch/v8"
    "github.com/elastic/go-elasticsearch/v8/esapi"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/goelastic"
)

func goelastic(w http.ResponseWriter, req *http.Request) {
    ctx := req.Context()
    es, err := elasticsearch.NewClient(
        elasticsearch.Config{Transport: ppgoelastic.NewTransport(nil)}
    )

    ...
    
    res, err = es.Search(
        es.Search.WithContext(ctx),
        es.Search.WithIndex("test"),
        es.Search.WithBody(&buf),
    )

    ...
}

Full Example Source

ppgohbase

This package instruments the gohbase calls. Use the NewClient as the gohbase.NewClient.

client := ppgohbase.NewClient("localhost")

It is necessary to pass the context containing the pinpoint.Tracer to gohbase.Client.

ctx := pinpoint.NewContext(context.Background(), tracer)
putRequest, _ := hrpc.NewPutStr(ctx, "table", "key", values)
client.Put(putRequest)
import (
    "github.com/tsuna/gohbase/hrpc"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/gohbase"
)

func doHbase(w http.ResponseWriter, r *http.Request) {
    client := ppgohbase.NewClient("localhost")

    values := map[string]map[string][]byte{"cf": {"a": []byte{0}}}
    putRequest, err := hrpc.NewPutStr(r.Context(), "table", "key", values)
    _, err = client.Put(putRequest)
	
    ...
}

Full Example Source

ppgomemcache

This package instruments the gomemcache calls. Use the NewClient as the memcache.New.

mc := ppgomemcache.NewClient(addr...)

It is necessary to pass the context containing the pinpoint.Tracer to Client using Client.WithContext.

mc.WithContext(pinpoint.NewContext(context.Background(), tracer))
mc.Get("foo")
import (
    "github.com/bradfitz/gomemcache/memcache"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/gomemcache"
)

func doMemcache(w http.ResponseWriter, r *http.Request) {
    addr := []string{"localhost:11211"}
    mc := ppgomemcache.NewClient(addr...)
    mc.WithContext(r.Context())

    item, err = mc.Get("foo")
	
    ...
}

Full Example Source

ppgoredis

This package instruments the go-redis calls. Use the NewClient as the redis.NewClient and the NewClusterClient as redis.NewClusterClient, respectively. It supports go-redis v6.10.0 and later.

rc = ppgoredis.NewClient(redisOpts)

It is necessary to pass the context containing the pinpoint.Tracer to Client using Client.WithContext.

rc = rc.WithContext(pinpoint.NewContext(context.Background(), tracer))
rc.Pipeline()
package main

import (
    "github.com/go-redis/redis"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/goredis"
)

var redisClient *ppgoredis.Client
var redisClusterClient *ppgoredis.ClusterClient

func redisv6(w http.ResponseWriter, r *http.Request) {
    c := redisClient.WithContext(r.Context())
    redisPipeIncr(c.Pipeline())
}

func redisv6Cluster(w http.ResponseWriter, r *http.Request) {
    c := redisClusterClient.WithContext(r.Context())
    redisPipeIncr(c.Pipeline())
}

func redisPipeIncr(pipe redis.Pipeliner) {
    incr := pipe.Incr("foo")
    pipe.Expire("foo", time.Hour)
    _, er := pipe.Exec()
    fmt.Println(incr.Val(), er)
}

func main() {
    ... //setup agent

    addrs := []string {"localhost:6379", "localhost:6380"}

    //redis client
    redisOpts := &redis.Options{
        Addr: addrs[0],
    }
    redisClient = ppgoredis.NewClient(redisOpts)

    //redis cluster client
    redisClusterOpts := &redis.ClusterOptions{
        Addrs: addrs,
    }
    redisClusterClient = ppgoredis.NewClusterClient(redisClusterOpts)

    http.HandleFunc("/redis", phttp.WrapHandlerFunc(redisv6))
    http.HandleFunc("/rediscluster", phttp.WrapHandlerFunc(redisv6Cluster))

    ...
}

Full Example Source

ppgoredisv7

This package instruments the go-redis/v7 calls. Use the NewHook or NewClusterHook as the redis.Hook. Only available in versions of go-redis with an AddHook() function.

rc = redis.NewClient(redisOpts)
client.AddHook(ppgoredisv7.NewHook(opts))

It is necessary to pass the context containing the pinpoint.Tracer to redis.Client.

rc = rc.WithContext(pinpoint.NewContext(context.Background(), tracer))
rc.Pipeline()
package main

import (
    "github.com/go-redis/redis/v7"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/goredisv7"
)

var redisClient *redis.Client

func redisv7(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    client := redisClient.WithContext(ctx)

    err := client.Set("key", "value", 0).Err()
    val, err := client.Get("key").Result()
    fmt.Println("key", val)
}

func main() {
    ... //setup agent

    addrs := []string{"localhost:6379", "localhost:6380"}

    redisOpts := &redis.Options{
        Addr: addrs[0],
    }
    redisClient = redis.NewClient(redisOpts)
    redisClient.AddHook(ppgoredisv7.NewHook(redisOpts))

    http.HandleFunc("/redis", pphttp.WrapHandlerFunc(redisv7))

    ...
}

Full Example Source

ppgoredisv8

This package instruments the go-redis/v8 calls, and the APIs provided is the same as ppgoredisv7. Refer the ppgoredisv7 paragraph.

ppgoredisv9

This package instruments the go-redis/v9 calls, and the APIs provided is the same as ppgoredisv7. Refer the ppgoredisv7 paragraph.

ppgorilla

This package instruments inbound requests handled by a gorilla mux.Router. Register the Middleware as the middleware of the router to trace all handlers:

r := mux.NewRouter()
r.Use(ppgorilla.Middleware())

Use WrapHandler or WrapHandlerFunc to select the handlers you want to track:

r.HandleFunc("/outgoing", ppgorilla.WrapHandlerFunc(outGoing))

For each request, a pinpoint.Tracer is stored in the request context. By using the pinpoint.FromContext function, this tracer can be obtained in your handler. Alternatively, the context of the request may be propagated where the context that contains the pinpoint.Tracer is required.

package main

import (
    "github.com/gorilla/mux"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/gorilla"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "hello world")
}

func main() {
    ... //setup agent
	
    r := mux.NewRouter()
    r.Use(ppgorilla.Middleware())

    //r.HandleFunc("/", ppgorilla.WrapHandlerFunc(hello)))
    http.ListenAndServe(":8000", r)
}

Full Example Source

This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.

Config Options

ppgorm

This package instruments the go-gorm/gorm calls. Use the Open as the gorm.Open.

g, err := ppgorm.Open(mysql.New(mysql.Config{Conn: db}), &gorm.Config{})

It is necessary to pass the context containing the pinpoint.Tracer to gorm.DB.

g = g.WithContext(pinpoint.NewContext(context.Background(), tracer))
g.Create(&Product{Code: "D42", Price: 100})
package main

import (
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/gorm"
    _ "github.com/pinpoint-apm/pinpoint-go-agent/plugin/mysql"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

func gormQuery(w http.ResponseWriter, r *http.Request) {
    db, err := sql.Open("mysql-pinpoint", "root:p123@tcp(127.0.0.1:3306)/testdb")
    gormdb, err := ppgorm.Open(mysql.New(mysql.Config{Conn: db}), &gorm.Config{})
    gormdb = gormdb.WithContext(r.Context())

    gormdb.AutoMigrate(&Product{})
    gormdb.Create(&Product{Code: "D42", Price: 100})

    var product Product
    gormdb.First(&product, "code = ?", "D42")

    ...
}

Full Example Source

ppgrpc

This package instruments gRPC servers and clients.

server

To instrument a gRPC server, use UnaryServerInterceptor and StreamServerInterceptor.

grpcServer := grpc.NewServer(
    grpc.UnaryInterceptor(ppgrpc.UnaryServerInterceptor()),
    grpc.StreamInterceptor(ppgrpc.StreamServerInterceptor()),
)

ppgrpc's server interceptor adds the pinpoint.Tracer to the gRPC server handler's context. By using the pinpoint.FromContext function, this tracer can be obtained. Alternatively, the context of the handler may be propagated where the context that contains the pinpoint.Tracer is required.

func (s *Server) UnaryCallUnaryReturn(ctx context.Context, msg *testapp.Greeting) (*testapp.Greeting, error) {
    tracer := pinpoint.FromContext(ctx)
    tracer.NewSpanEvent("gRPC Server Handler").EndSpanEvent()
    return "hi", nil
}
package main

import (
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/grpc/example/testapp"
    "google.golang.org/grpc"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/grpc"
)

type Server struct{}
var returnMsg = &testapp.Greeting{Msg: "Hello!!"}

func (s *Server) UnaryCallUnaryReturn(ctx context.Context, msg *testapp.Greeting) (*testapp.Greeting, error) {
    printGreeting(ctx, msg)
    return returnMsg, nil
}

func (s *Server) StreamCallUnaryReturn(stream testapp.Hello_StreamCallUnaryReturnServer) error {
    for {
        in, err := stream.Recv()
        printGreeting(stream.Context(), in)
    }
}

func printGreeting(ctx context.Context, in *testapp.Greeting) {
    defer pinpoint.FromContext(ctx).NewSpanEvent("printGreeting").EndSpanEvent()
    log.Println(in.Msg)
}

func main() {
    ... //setup agent
	
    grpcServer := grpc.NewServer(
        grpc.UnaryInterceptor(ppgrpc.UnaryServerInterceptor()),
        grpc.StreamInterceptor(ppgrpc.StreamServerInterceptor()),
    )
    testapp.RegisterHelloServer(grpcServer, &Server{})
    grpcServer.Serve(listener)
}

Full Example Source

client

To instrument a gRPC client, use UnaryClientInterceptor and StreamClientInterceptor.

conn, err := grpc.Dial(
    "localhost:8080",
    grpc.WithUnaryInterceptor(ppgrpc.UnaryClientInterceptor()),
    grpc.WithStreamInterceptor(ppgrpc.StreamClientInterceptor()),
)

It is necessary to pass the context containing the pinpoint.Tracer to grpc.Client.

client := testapp.NewHelloClient(conn)
client.UnaryCallUnaryReturn(pinpoint.NewContext(context.Background(), tracer), greeting)
import (
    "google.golang.org/grpc"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/grpc"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/grpc/example/testapp"
)

func unaryCallUnaryReturn(ctx context.Context, client testapp.HelloClient) {
    in, err := client.UnaryCallUnaryReturn(ctx, greeting)
    log.Println(in.Msg)
}

func streamCallUnaryReturn(ctx context.Context, client testapp.HelloClient) {
    stream, err := client.StreamCallUnaryReturn(ctx)

    for i := 0; i < numStreamSend; i++ {
        if err := stream.Send(greeting); err != nil {
            break
        }
    }

    ...
}

func doGrpc(w http.ResponseWriter, r *http.Request) {
    conn, err := grpc.Dial(
        "localhost:8080",
        grpc.WithInsecure(),
        grpc.WithUnaryInterceptor(ppgrpc.UnaryClientInterceptor()),
        grpc.WithStreamInterceptor(ppgrpc.StreamClientInterceptor()),
    )
    defer conn.Close()

    ctx := r.Context()
    unaryCallUnaryReturn(ctx, client)
    streamCallUnaryReturn(ctx, client)
}

Full Example Source

pphttprouter

This package instruments inbound requests handled by a httprouter.Router. Use New() to trace all handlers:

r := pphttprouter.New()
r.GET("/", Index)

Use WrapHandle to select the handlers you want to track:

r := httprouter.New()
r.GET("/hello/:name", pphttprouter.WrapHandle(hello))

For each request, a pinpoint.Tracer is stored in the request context. By using the pinpoint.FromContext function, this tracer can be obtained in your handler. Alternatively, the context of the request may be propagated where the context that contains the pinpoint.Tracer is required.

import (
    "github.com/julienschmidt/httprouter"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/httprouter"
)

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    tracer := pinpoint.FromContext(r.Context())
    func() {
        defer tracer.NewSpanEvent("func_1").EndSpanEvent()
        time.Sleep(1 * time.Second)
    }()

    fmt.Fprint(w, "Welcome!\n")
}

func main() {
    ... //setup agent

    router := pphttprouter.New()
    router.GET("/", Index)
    http.ListenAndServe(":8000", router)
}

Full Example Source

This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern. But, WrapHandle function doesn't support URL Statistics feature.

Config Options

ppkratos

This package instruments kratos servers and clients.

server

To instrument a kratos server, use ServerMiddleware.

httpSrv := http.NewServer(
    http.Address(":8000"),
    http.Middleware(ppkratos.ServerMiddleware()),
)

The server middleware adds the pinpoint.Tracer to the kratos server handler's context. By using the pinpoint.FromContext function, this tracer can be obtained. Alternatively, the context of the handler may be propagated where the context that contains the pinpoint.Tracer is required.

func (s *server) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
    tracer := pinpoint.FromContext(ctx)
    defer tracer.NewSpanEvent("f1").EndSpanEvent()
    ...
}
package main

import (
    "github.com/go-kratos/kratos/v2"
    "github.com/go-kratos/kratos/v2/transport"
    "github.com/go-kratos/kratos/v2/transport/http"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/kratos"
)

type server struct {
    helloworld.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
    tracer := pinpoint.FromContext(ctx)
    defer tracer.NewSpanEvent("f1").EndSpanEvent()

    return &helloworld.HelloReply{Message: fmt.Sprintf("Hello %+v", in.Name)}, nil
}

func main() {
    ... //setup agent
	
    s := &server{}
    httpSrv := http.NewServer(
        http.Address(":8000"),
        http.Middleware(
            ppkratos.ServerMiddleware(),
        ),
    )
    ...
}

Full Example Source

client

To instrument a kratos client, use ClientMiddleware.

conn, err := transhttp.NewClient(
    context.Background(),
    transhttp.WithMiddleware(ppkratos.ClientMiddleware()),
    transhttp.WithEndpoint("127.0.0.1:8000"),
)

It is necessary to pass the context containing the pinpoint.Tracer to kratos client.

client := pb.NewGreeterHTTPClient(conn)
reply, err := client.SayHello(pinpoint.NewContext(context.Background(), tracer), &pb.HelloRequest{Name: "kratos"})
import (
    transhttp "github.com/go-kratos/kratos/v2/transport/http"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/kratos"
)

func callHTTP(w http.ResponseWriter, r *http.Request) {
    conn, err := transhttp.NewClient(
        context.Background(),
        transhttp.WithMiddleware(ppkratos.ClientMiddleware()),
        transhttp.WithEndpoint("127.0.0.1:8000"),
    )

    client := pb.NewGreeterHTTPClient(conn)
    reply, err := client.SayHello(r.Context(), &pb.HelloRequest{Name: "kratos"})
    ...
}

Full Example Source

pplogrus

This package allows additional transaction id and span id of the pinpoint span to be printed in the log message. Use the NewField or NewEntry and pass the logrus field back to the logger.

tracer := pinpoint.FromContext(ctx)
logger.WithFields(pplogrus.NewField(tracer)).Fatal("oh, what a wonderful world")
entry := pplogrus.NewEntry(tracer).WithField("foo", "bar")
entry.Error("entry log message")

You can use NewHook as the logrus.Hook. It is necessary to pass the context containing the pinpoint.Tracer to logrus.Logger.

logger.AddHook(pplogrus.NewHook())
entry := logger.WithContext(pinpoint.NewContext(context.Background(), tracer)).WithField("foo", "bar")
entry.Error("hook log message")
import (
    "github.com/sirupsen/logrus"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/logrus"
)

func logging(w http.ResponseWriter, r *http.Request) {
    logger := logrus.New()
    tracer := pinpoint.TracerFromRequestContext(r)
    logger.WithFields(pplogrus.NewField(tracer)).Fatal("ohhh, what a world")
}

Full Example Source

ppmongo

This package instruments the mongo-go-driver calls. Use the NewMonitor as Monitor field of mongo-go-driver's ClientOptions.

opts := options.Client()
opts.Monitor = ppmongo.NewMonitor()
client, err := mongo.Connect(ctx, opts)

It is necessary to pass the context containing the pinpoint.Tracer to mongo.Client.

collection := client.Database("testdb").Collection("example")
ctx := pinpoint.NewContext(context.Background(), tracer)
collection.InsertOne(ctx, bson.M{"foo": "bar", "apm": "pinpoint"})
import (
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/mongodriver"
)

func mongodb(w http.ResponseWriter, r *http.Request) {
    opts := options.Client()
    opts.ApplyURI("mongodb://localhost:27017")
    opts.Monitor = ppmongo.NewMonitor()
    client, err := mongo.Connect(context.Background(), opts)

    collection := client.Database("testdb").Collection("example")
    _, err = collection.InsertOne(r.Context(), bson.M{"foo": "bar", "apm": "pinpoint"})
    ...
}

Full Example Source

ppmssql

This package instruments the MS SQL Server driver calls. Use this package's driver in place of the SQL Server driver.

dsn := "server=localhost;user id=sa;password=TestPass123;port=1433;database=TestDB"
db, err := sql.Open("sqlserver-pinpoint", dsn)

It is necessary to pass the context containing the pinpoint.Tracer to all exec and query methods on SQL driver.

ctx := pinpoint.NewContext(context.Background(), tracer)
row, err := db.QueryContext(ctx, "SELECT * FROM Inventory")
import (
    "database/sql"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    _ "github.com/pinpoint-apm/pinpoint-go-agent/plugin/mssql"
)

func query(w http.ResponseWriter, r *http.Request) {
    dsn := "server=localhost;user id=sa;password=TestPass123;port=1433;database=TestDB"
    db, err := sql.Open("sqlserver-pinpoint", dsn)
    defer db.Close()

    rows, _ := db.QueryContext(r.Context(), "SELECT * FROM Inventory")
    for rows.Next() {
        _ = rows.Scan(&id, &name, &quantity)
        fmt.Printf("user: %d, %s, %d\n", id, name, quantity)
    }
    rows.Close()
}

Full Example Source

ppmysql

This package instruments the mysql driver calls. Use this package's driver in place of the mysql driver.

db, err := sql.Open("mysql-pinpoint", "root:p123@tcp(127.0.0.1:3306)/information_schema")

It is necessary to pass the context containing the pinpoint.Tracer to all exec and query methods on SQL driver.

ctx := pinpoint.NewContext(context.Background(), tracer)
row := db.QueryRowContext(ctx, "SELECT count(*) from tables")
import (
    "database/sql"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    _ "github.com/pinpoint-apm/pinpoint-go-agent/plugin/mysql"
)

func query(w http.ResponseWriter, r *http.Request) {
    db, err := sql.Open("mysql-pinpoint", "root:p123@tcp(127.0.0.1:3306)/information_schema")
    row := db.QueryRowContext(r.Context(), "SELECT count(*) from tables")

    var count int
    row.Scan(&count)
    fmt.Println("number of tables in information_schema", count)
}

Full Example Source

pporacle

This package instruments the Oracle driver calls. Use this package's driver in place of the Oracle driver.

db, err := sql.Open("oracle-pinpoint", "oracle://scott:tiger@localhost:1521/xe")

It is necessary to pass the context containing the pinpoint.Tracer to all exec and query methods on SQL driver.

ctx := pinpoint.NewContext(context.Background(), tracer)
row, err := db.QueryContext(ctx, "SELECT * FROM BONUS")
import (
    "database/sql"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    _ "github.com/pinpoint-apm/pinpoint-go-agent/plugin/oracle"
)

func query(w http.ResponseWriter, r *http.Request) {
    conn, err := sql.Open("oracle-pinpoint", "oracle://scott:tiger@localhost:1521/xe")
    rows, _ := conn.QueryContext(r.Context(), "SELECT * FROM BONUS")

    for rows.Next() {
        err = rows.Scan(&ename, &job, &sal, &comm)
        fmt.Println("ENAME: ", ename, "\tJOB: ", job, "\tSAL: ", sal, "\tCOMM: ", comm)
    }
}

Full Example Source

pppgsql

This package instruments the postgres driver calls. Use this package's driver in place of the postgres driver.

db, err := sql.Open("pq-pinpoint", "postgresql://test:test!@localhost/testdb?sslmode=disable")

It is necessary to pass the context containing the pinpoint.Tracer to all exec and query methods on SQL driver. Spans will be created for queries and other statement executions if the context methods are used, and the context includes a transaction.

ctx := pinpoint.NewContext(context.Background(), tracer)
row := db.QueryRowContext(ctx, "SELECT count(*) FROM pg_catalog.pg_tables")
import (
    pinpoint "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/http"
    _ "github.com/pinpoint-apm/pinpoint-go-agent/plugin/pgsql"
)

func query(w http.ResponseWriter, r *http.Request) {
    db, err := sql.Open("pq-pinpoint", "postgresql://test:test!@localhost/testdb?sslmode=disable")
    row := db.QueryRowContext(r.Context(), "SELECT count(*) FROM pg_catalog.pg_tables")

    var count int
    err = row.Scan(&count)
    fmt.Println("number of entries in pg_catalog.pg_tables", count)
}

Full Example Source

ppredigo

This package instruments the gomodule/redigo calls. Use the Dial, DialContext (or DialURL, DialURLContext) as the redis.Dial.

c, err := ppredigo.Dial("tcp", "127.0.0.1:6379")

It is necessary to propagate the context that contains the pinpoint.Tracer to redis.Conn. You can call WithContext to propagate a context containing a pinpoint.Tracer to the operations:

ppredigo.WithContext(c, pinpoint.NewContext(context.Background(), tracer))
c.Do("SET", "vehicle", "truck")

Also, you can use function taking the context like redis.DoContext.

redis.DoContext(c, pinpoint.NewContext(context.Background(), tracer), "GET", "vehicle")
package main

import (
    "github.com/gomodule/redigo/redis"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/redigo"
)

func redigo_test(w http.ResponseWriter, r *http.Request) {
    c, err := ppredigo.Dial("tcp", "127.0.0.1:6379")
    ppredigo.WithContext(c, r.Context())

    c.Do("SET", "vehicle", "truck")
    redis.DoWithTimeout(c, 1000*time.Millisecond, "GET", "vehicle")
    
    //or 
    //redis.DoContext(c, r.Context(), "GET", "vehicle")

Full Example Source

ppsarama

This package instruments Kafka consumers and producers.

Consumer

To instrument a Kafka consumer, ConsumeMessageContext. In order to display the kafka broker on the pinpoint screen, a context with broker addresses must be created and delivered using NewContext.

ConsumePartition example:

ctx := ppsarama.NewContext(context.Background(), broker)
pc, _ := consumer.ConsumePartition(topic, partition, offset)
for msg := range pc.Messages() {
    ppsarama.ConsumeMessageContext(processMessage, ctx, msg)
}

ConsumerGroupHandler example:

func (h exampleConsumerGroupHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
    ctx := sess.Context()
    for msg := range claim.Messages() {
        _ = ppsarama.ConsumeMessageContext(process, ctx, msg)
    }
}

func main() {     
    ctx := ppsarama.NewContext(context.Background(), broker)
    handler := exampleConsumerGroupHandler{}
    err := group.Consume(ctx, topics, handler)

ConsumeMessageContext passes a context added pinpoint.Tracer to HandlerContextFunc. In HandlerContextFunc, this tracer can be obtained by using the pinpoint.FromContext function. Alternatively, the context may be propagated where the context that contains the pinpoint.Tracer is required.

func process(ctx context.Context, msg *sarama.ConsumerMessage) error {
    tracer := pinpoint.FromContext(ctx)
    defer tracer.NewSpanEvent("process").EndSpanEvent()

    fmt.Printf("Message topic:%q partition:%d offset:%d\n", msg.Topic, msg.Partition, msg.Offset)
package main

import (
    "github.com/Shopify/sarama"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/pinpoint-apm/pinpoint-go-agent/plugin/sarama"
)

func processMessage(ctx context.Context, msg *sarama.ConsumerMessage) error {
    tracer := pinpoint.FromContext(ctx)
    defer tracer.NewSpanEvent("processMessage").EndSpanEvent()
    fmt.Println("retrieving message: ", string(msg.Value))
    ...
}

func subscribe() {
    broker := []string{"localhost:9092"}
    config := sarama.NewConfig()
    consumer, err := sarama.NewConsumer(broker, config)
    ...
    
    ctx := ppsarama.NewContext(context.Background(), broker)
    for _, partition := range partitionList {
        pc, _ := consumer.ConsumePartition(topic, partition, initialOffset)

        go func(pc sarama.PartitionConsumer) {
            for msg := range pc.Messages() {
                ppsarama.ConsumeMessageContext(processMessage, ctx, msg)
            }
        }(pc)
	...	
}

func main() {
    ... //setup agent

    subscribe()
}

Full Example Source

Producer

To instrument a Kafka producer, use NewSyncProducer or NewAsyncProducer.

config := sarama.NewConfig()
producer, err := ppsarama.NewSyncProducer(brokers, config)

It is necessary to pass the context containing the pinpoint.Tracer to sarama.SyncProducer (or sarama.AsyncProducer) using WithContext function.

ppsarama.WithContext(pinpoint.NewContext(context.Background(), tracer), producer)
partition, offset, err := producer.SendMessage(msg)
package main

import (
    "github.com/Shopify/sarama"
    "github.com/pinpoint-apm/pinpoint-go-agent"
    "github.com/github.com/pinpoint-apm/pinpoint-go-agent/plugin/sarama"
)

func prepareMessage(topic, message string) *sarama.ProducerMessage {
    return &sarama.ProducerMessage{
        Topic:     topic,
        Partition: -1,
        Value:     sarama.StringEncoder(message),
    }
}

func save(w http.ResponseWriter, r *http.Request) {
    msg := prepareMessage("topic", "Hello, Kafka!!")
    ppsarama.WithContext(r.Context(), producer)
    partition, offset, err := producer.SendMessage(msg)
    ...
}

var producer sarama.SyncProducer

func main() {
    ... //setup agent
	
    config := sarama.NewConfig()
    ...

    producer, err := ppsarama.NewSyncProducer([]string{"127.0.0.1:9092"}, config)
    http.HandleFunc("/save", pphttp.WrapHandlerFunc(save))
}

Full Example Source