Skip to content

Commit e1a1229

Browse files
committed
feat: add nrgorm apm integration
1 parent 5867ad9 commit e1a1229

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"time"
6+
7+
"github.com/newrelic/go-agent/v3/integrations/nrgorm"
8+
"gorm.io/driver/postgres"
9+
"gorm.io/gorm"
10+
)
11+
12+
func main() {
13+
dsn := "host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai"
14+
_, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
15+
NowFunc: func() time.Time {
16+
return time.Now().UTC()
17+
},
18+
Plugins: map[string]gorm.Plugin{
19+
nrgorm.APMPlugin{}.Name(): nrgorm.APMPlugin{},
20+
},
21+
})
22+
if err != nil {
23+
log.Printf("gorm open failed: %v", err)
24+
}
25+
}

v3/integrations/nrgorm/go.mod

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module github.com/newrelic/go-agent/v3/integrations/nrgorm
2+
3+
go 1.21
4+
5+
require (
6+
gorm.io/gorm v1.25.12
7+
github.com/newrelic/go-agent/v3 v3.36.0
8+
)
9+
10+
11+
replace github.com/newrelic/go-agent/v3 => ../..

v3/integrations/nrgorm/nrgorm.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package nrgorm
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"strconv"
7+
8+
"github.com/newrelic/go-agent/v3/newrelic"
9+
"gorm.io/gorm"
10+
)
11+
12+
const startTimeKey = "newrelic_start_time"
13+
14+
type APMPlugin struct{}
15+
16+
var _ gorm.Plugin = (*APMPlugin)(nil)
17+
18+
func (p APMPlugin) Name() string {
19+
return "newrelic"
20+
}
21+
22+
func (p APMPlugin) Initialize(db *gorm.DB) error {
23+
err := db.Callback().Create().Before("gorm:create").Register("newrelic:before_create", beforeCallback)
24+
if err != nil {
25+
return err
26+
}
27+
err = db.Callback().Query().Before("gorm:query").Register("newrelic:before_query", beforeCallback)
28+
if err != nil {
29+
return err
30+
}
31+
err = db.Callback().Update().Before("gorm:update").Register("newrelic:before_update", beforeCallback)
32+
if err != nil {
33+
return err
34+
}
35+
err = db.Callback().Delete().Before("gorm:delete").Register("newrelic:before_delete", beforeCallback)
36+
if err != nil {
37+
return err
38+
}
39+
40+
err = db.Callback().Create().After("gorm:create").Register("newrelic:after_create", afterCallback("INSERT"))
41+
if err != nil {
42+
return err
43+
}
44+
err = db.Callback().Query().After("gorm:query").Register("newrelic:after_query", afterCallback("SELECT"))
45+
if err != nil {
46+
return err
47+
}
48+
err = db.Callback().Update().After("gorm:update").Register("newrelic:after_update", afterCallback("UPDATE"))
49+
if err != nil {
50+
return err
51+
}
52+
err = db.Callback().Delete().After("gorm:delete").Register("newrelic:after_delete", afterCallback("DELETE"))
53+
if err != nil {
54+
return err
55+
}
56+
57+
return nil
58+
}
59+
60+
func beforeCallback(db *gorm.DB) {
61+
if txn := newrelic.FromContext(db.Statement.Context); txn != nil {
62+
db.Set(startTimeKey, txn.StartSegmentNow())
63+
}
64+
}
65+
66+
func afterCallback(operation string) func(db *gorm.DB) {
67+
return func(db *gorm.DB) {
68+
if startTime, ok := db.Get(startTimeKey); ok {
69+
segment := newrelic.DatastoreSegment{
70+
Product: newrelic.DatastorePostgres,
71+
Collection: db.Statement.Table,
72+
Operation: operation,
73+
StartTime: startTime.(newrelic.SegmentStartTime),
74+
ParameterizedQuery: db.Statement.SQL.String(),
75+
QueryParameters: parseVars(db.Statement.Vars),
76+
}
77+
segment.End()
78+
}
79+
}
80+
}
81+
82+
func parseVars(vars []interface{}) map[string]interface{} {
83+
queryParameters := make(map[string]interface{})
84+
for i, v := range vars {
85+
i := i
86+
v := v
87+
val := reflect.ValueOf(v)
88+
if val.Kind() == reflect.Ptr {
89+
val = val.Elem()
90+
}
91+
queryParameters[fmt.Sprintf("$%v", strconv.Itoa(i+1))] = fmt.Sprintf("%v", val.Interface())
92+
}
93+
return queryParameters
94+
}

0 commit comments

Comments
 (0)