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.
- Http.Server.StatusCodeErrors
- Http.Server.ExcludeUrl
- Http.Server.ExcludeMethod
- Http.Server.RecordRequestHeader
- Http.Server.RecordResponseHeader
- Http.Server.RecordRequestCookie
- Http.UrlStat.Enable
- Http.UrlStat.LimitSize
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)
}
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.
- Http.Server.StatusCodeErrors
- Http.Server.ExcludeUrl
- Http.Server.ExcludeMethod
- Http.Server.RecordRequestHeader
- Http.Server.RecordResponseHeader
- Http.Server.RecordRequestCookie
- Http.UrlStat.Enable
- Http.UrlStat.LimitSize
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)
}
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)
}
This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.
- Http.Server.StatusCodeErrors
- Http.Server.ExcludeUrl
- Http.Server.ExcludeMethod
- Http.Server.RecordRequestHeader
- Http.Server.RecordResponseHeader
- Http.Server.RecordRequestCookie
- Http.UrlStat.Enable
- Http.UrlStat.LimitSize
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")
}
This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.
- Http.Server.StatusCodeErrors
- Http.Server.ExcludeUrl
- Http.Server.ExcludeMethod
- Http.Server.RecordRequestHeader
- Http.Server.RecordResponseHeader
- Http.Server.RecordRequestCookie
- Http.UrlStat.Enable
- Http.UrlStat.LimitSize
This package instruments the echo/v4 package, and the APIs provided is the same as ppecho. Refer the ppecho paragraph.
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))
}
This package supports URL Statistics feature. It aggregates response times, successes and failures for each pattern given as parameter of a WrapHandler function.
- Http.Server.StatusCodeErrors
- Http.Server.ExcludeUrl
- Http.Server.ExcludeMethod
- Http.Server.RecordRequestHeader
- Http.Server.RecordResponseHeader
- Http.Server.RecordRequestCookie
- Http.UrlStat.Enable
- Http.UrlStat.LimitSize
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)
...
}
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))
}
This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.
- Http.Server.StatusCodeErrors
- Http.Server.ExcludeUrl
- Http.Server.ExcludeMethod
- Http.Server.RecordRequestHeader
- Http.Server.RecordResponseHeader
- Http.Server.RecordRequestCookie
- Http.UrlStat.Enable
- Http.UrlStat.LimitSize
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"))
}
This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.
- Http.Server.StatusCodeErrors
- Http.Server.ExcludeUrl
- Http.Server.ExcludeMethod
- Http.Server.RecordRequestHeader
- Http.Server.RecordResponseHeader
- Http.Server.RecordRequestCookie
- Http.UrlStat.Enable
- Http.UrlStat.LimitSize
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")
}
This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.
- Http.Server.StatusCodeErrors
- Http.Server.ExcludeUrl
- Http.Server.ExcludeMethod
- Http.Server.RecordRequestHeader
- Http.Server.RecordResponseHeader
- Http.Server.RecordRequestCookie
- Http.UrlStat.Enable
- Http.UrlStat.LimitSize
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)
...
}
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),
)
...
}
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)
...
}
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")
...
}
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))
...
}
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))
...
}
This package instruments the go-redis/v8 calls, and the APIs provided is the same as ppgoredisv7. Refer the ppgoredisv7 paragraph.
This package instruments the go-redis/v9 calls, and the APIs provided is the same as ppgoredisv7. Refer the ppgoredisv7 paragraph.
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)
}
This package supports URL Statistics feature. It aggregates response times, successes and failures for each router pattern.
- Http.Server.StatusCodeErrors
- Http.Server.ExcludeUrl
- Http.Server.ExcludeMethod
- Http.Server.RecordRequestHeader
- Http.Server.RecordResponseHeader
- Http.Server.RecordRequestCookie
- Http.UrlStat.Enable
- Http.UrlStat.LimitSize
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")
...
}
This package instruments gRPC servers and clients.
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)
}
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)
}
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)
}
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.
- Http.Server.StatusCodeErrors
- Http.Server.ExcludeUrl
- Http.Server.ExcludeMethod
- Http.Server.RecordRequestHeader
- Http.Server.RecordResponseHeader
- Http.Server.RecordRequestCookie
- Http.UrlStat.Enable
- Http.UrlStat.LimitSize
This package instruments kratos servers and clients.
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(),
),
)
...
}
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"})
...
}
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")
}
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"})
...
}
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()
}
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)
}
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)
}
}
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)
}
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")
This package instruments Kafka consumers and producers.
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()
}
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))
}