aws-xray-sdk-go
aws-xray-sdk-go copied to clipboard
How to use aws-xray-sdk-go with gorm
I'm trying to use aws-xray-sdk-go to capture SQL query issued by gorm, famous ORM in Go community.
It seems that I need to create a sub-segment before issuing SQL. Is there any better way in aws-xray-sdk-go?
Hi @momotaro98 ,
I'm not very much familiar with gorm. Looks like X-Ray Go SDK does not support gorm so it won't create automatic subsegment for any calls. So, you will have to manually create subsegment for this in order to trace any calls. However, If this a major use case for you and if you're interested then I can work with you to add a support for this. We love to review/comments/suggestions pull requests on any new framework which X-Ray SDK doesn't support.
It might actually be possible now with Gorm v2
released today (2020-08-28
)!
To my understanding the issue with X-Ray + Gorm v1
was that X-Ray requires access to sql.Open
(or rather that the database needs to be opened with xray.SQLContext
) which is not possible with Gorm v1
as it does not support context. Related issues:
- https://github.com/go-gorm/gorm/issues/2288
- https://github.com/go-gorm/gorm/issues/1807
Luckily Gorm v2
is now released and according to the main developer it supports context and so it seems to be according to v2
release notes and documentation.
I haven't yet tested this out, but according to Gorm v2
docs, you can use existing connection:
import (
"database/sql"
"gorm.io/gorm"
)
sqlDB, err := sql.Open("mysql", "mydb_dsn")
gormDB, err := gorm.Open(mysql.New(mysql.Config{
Conn: sqlDB,
}), &gorm.Config{})
Then with X-Ray it theoretically should work with:
instrumentedDB, err := xray.SQLContext("postgres", "postgres://user:password@host:port/db")
// Handle err
db, err := gorm.Open(postgres.New(postgres.Config{
Conn: instrumentedDB,
}), &gorm.Config{})
… if so, I think this should be verified by folks at AWS (ping @bhautikpip) and documented into the X-Ray SDK Go website (under “Tracing SQL queries”-section).
Hi @aripalo ,
Thanks for this suggestions. I looked into the code you posted. I believe we can use the below code for opening up a connection and that looks fine to me.
instrumentedDB, err := xray.SQLContext("postgres", "postgres://user:password@host:port/db")
// Handle err
db, err := gorm.Open(postgres.New(postgres.Config{
Conn: instrumentedDB,
}), &gorm.Config{})
The only issue I'm concerned about is how to propagate context for any subsequent gorm API calls. I am assuming that we will be using db
to call different gorm APIs right? So, In order to trace all the calls made with gorm I believe we must propagate the context. Here's an example of how we're propagating context. Let me know your thoughts.
Thanks for the hint to configure Gorm using the instrumentedDB
.
FYI: As for propagating context, the following works for me. Maybe this code helps others, too.
// FindSomeObject loads some object from the database.
func FindSomeObject(ctx context.Context, id string) (*SomeObject, error) {
var item SomeObject
err := myGormClient.Perform(ctx, "my subsegment name, e.g. FindSomeObject", func(ctx context.Context, db *gorm.DB) error {
err := db.Where("...").Take(&item).Error
// error handling
return nil
}
return &SomeObject{}, nil
}
// My Gorm Client Wrapper (with a `DB *gorm.DB` field):
func (c *GormClient) Perform(ctx context.Context, name string, fn func(ctx context.Context, db *gorm.DB) error) error {
return xray.Capture(ctx, name, func(ctx2 context.Context) error {
return fn(ctx2, c.DB.WithContext(ctx2))
})
}
Calling FindSomeObject
(with a ctx
that contains an already opened segment) results in a subsegment for FindSomeObject
and nested subsegments for SQL related things.
The SQL details are viewable in the X-Ray console, but not in the CloudWatch ServiceLens Traces console, though.
Hi there, i got in stuck with the problem to connect with MySQL.
- go.mod
go 1.16
require (
github.com/aws/aws-xray-sdk-go v1.6.0
gorm.io/driver/mysql v1.1.1
gorm.io/gorm v1.21.12
)
- My code
// xray SDK says following the format: `<schema>://<user>:<password>@<host>:<port>/<database>`
// it's different with https://github.com/go-sql-driver/mysql#dsn-data-source-name
// my DSN setting equals "dbuser:dbpass@tcp(localhost:3306)/app?parseTime=true"
instrumentedDB, err := xray.SQLContext("mysql", DSN)
if err != nil {
fmt.Println("Error: xray.SQLContext. ", DSN)
}
dialector := mysql.New(mysql.Config{
Conn: instrumentedDB,
})
db, err := gorm.Open(dialector, &gorm.Config{})
if err != nil {
fmt.Println("Error: No database connection established.", DSN)
}
When app called gorm.Open
, following error occurred.
panic: failed to begin subsegment named 'app': segment cannot be found.
I guess I should use context got from xray.BeginSegment
defined as BeginSegment(ctx context.Context, name string) (context.Context, *Segment)
.
Tracing error stack, got it mysql driver uses new context in their Initialize
func like below.
https://github.com/go-gorm/mysql/blob/master/mysql.go#L63
Does anyone know how to resolve this problem or is there better way to use AWS xray w/ GORM?
Thank u.
Hi @horsewin AWS X-Ray Go SDK does not support gorm instrumentation officially. I think this thread has some workarounds to trace calls with gorm. Looks like you're having issues with propagating the trace context and hence resulted in segment can not be found
error. Unfortunately, there are not other workarounds AFAIK. Have you tried @thomasritz suggestion?
Any ETA on this ? GORM is one of the most widely used libraries in Go Eco-system, so it would be valuable to have a connector for X-Ray
any news for this bug?
Hi everyone,
There are currently no plans to support GORM in the X-Ray SDK. OpenTelemetry Go supports instrumenting GORM with a 3rd party instrumentation package: https://github.com/uptrace/opentelemetry-go-extra/tree/main/otelgorm
You can see OpenTelemetry-approved instrumentation packages here: https://opentelemetry.io/registry/?s=gorm&component=&language=
X-Ray provides first-class support for OpenTelemetry instrumentation, you can read getting started with OpenTelemetry Go for X-Ray here: https://aws-otel.github.io/docs/getting-started/go-sdk
I managed to solve this issue with adding
`func openDbConnection(dsn string) *gorm.DB { instrumentedDB, err := xray.SQLContext("postgres", dsn)
if err != nil {
log.Printf("Can not create instrumented db %+v", err.Error())
}
db, err := gorm.Open(postgres.New(postgres.Config{
DriverName: "lib/pq",
Conn: instrumentedDB,
}), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
panic("failed to connect database")
}
return db
}`
and executing the query with, where ctx is a param in the handler function
err := repo.db. WithContext(ctx). Model(&SomeModel{}). Find(&array). Limit(100). Error
any news on this one?
Thanks for the hint to configure Gorm using the
instrumentedDB
.FYI: As for propagating context, the following works for me. Maybe this code helps others, too.
// FindSomeObject loads some object from the database. func FindSomeObject(ctx context.Context, id string) (*SomeObject, error) { var item SomeObject err := myGormClient.Perform(ctx, "my subsegment name, e.g. FindSomeObject", func(ctx context.Context, db *gorm.DB) error { err := db.Where("...").Take(&item).Error // error handling return nil } return &SomeObject{}, nil } // My Gorm Client Wrapper (with a `DB *gorm.DB` field): func (c *GormClient) Perform(ctx context.Context, name string, fn func(ctx context.Context, db *gorm.DB) error) error { return xray.Capture(ctx, name, func(ctx2 context.Context) error { return fn(ctx2, c.DB.WithContext(ctx2)) }) }
Calling
FindSomeObject
(with actx
that contains an already opened segment) results in a subsegment forFindSomeObject
and nested subsegments for SQL related things.The SQL details are viewable in the X-Ray console, but not in the CloudWatch ServiceLens Traces console, though.
@thomasritz have you found any better way to implement this on GORM v2?
@hatrixdamjan solution actually works for me. Thanks :)