A Golang implementation of SQLCommenter.
With sqlcommenter
, you can add key-value attributes to a context, and those
attributes will be added to your outgoing SQL queries in the
SQLComment-standardized format:
ctx = sqlcommenter.NewContext(ctx, "endpoint", "ListWidgets")
// ...
db.QueryContext(ctx, "select * from widgets")
The above code would send the following SQL to your database:
select * from widgets /*endpoint='ListWidgets'*/
For the common case that you want to add your OpenTelemetry traces to logs, you can just write:
ctx = otelsqlcommenter.NewContext(ctx)
// ...
db.QueryContext(ctx, "select * from widgets")
Which would produce the de facto standardized fields traceparent
and
tracestate
:
select * from widgets /*traceparent='00-5bd66ef5095369c7b0d1f8f4bd33716a-c532cb4098ac3dd2-01',tracestate='congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7'*/
go get github.com/ucarion/sqlcommenter
To add a key-value attribute pair to your SQL commands, use NewContext
from
this package. You can pass multiple key-value pairs in a single call:
ctx = sqlcommenter.NewContext(ctx, "key1", "value1", "key2", "value2")
And then make sure to pass that same ctx
to your database operations (i.e.
calls to QueryContext
, ExecContext
, PrepareContext
, etc.).
The subpackage otelsqlcommenter
from this module populates the attributes
traceparent
and tracestate
from the current span context (i.e.
trace.SpanContextFromContext
). You can use this to quickly hook up
OpenTelemetry to your SQL logs:
ctx = otelsqlcommenter.NewContext(ctx)
This functionality is in its own package, so if you don't use otelsqlcommenter
then no OpenTelemetry code is included in your build.
sqlcommenter
is implemented as a database driver that wraps on top of your
existing database driver. This section details how you can create and use a
sqlcommenter
database driver.
Typical use of the database/sql
package looks something like:
db, err := sql.Open("xxx", "...")
// ...
rows, err := db.QueryContext(ctx, "select 1 + 1")
To move over to sqlcommenter
, you'll first need to register the sqlcommenter
driver, and then call sql.Open
with that driver instead. The Driver
function
from this package provides that driver, but it requires an underlying
database/sql/driver.Driver
to wrap on top of. You will need to identify what
your underlying driver is.
For the most commonly-used database packages, the drivers are:
- https://github.com/go-sql-driver/mysql:
&mysql.MySQLDriver{}
- https://github.com/lib/pq:
&pq.Driver{}
- https://github.com/mattn/go-sqlite3:
&sqlite3.SQLiteDriver{}
If you're not using one of the packages above, then look for where you're
calling sql.Open("xxx", ...)
in your code. Then look for a call to
sql.Register("xxx", yyy)
in your database driver package. yyy
will be the
driver you need to wrap.
Once you have a driver in hand, then replace:
db, err := sql.Open("xxx", "...")
With:
// mydriver is the driver you identified from before, e.g. &mysql.MySQLDriver{}
sql.Register("sqlcommenter-xxx", sqlcommenter.Driver(mydriver))
db, err := sql.Open("sqlcommenter-xxx", "...")
No other changes are required. Because sqlcommenter will not modify queries if there are no attributes set, this operation will be a no-op. But now if you do:
ctx = sqlcommenter.NewContext("foo", "bar")
rows, err := db.QueryContext(ctx, "select 1 + 1")
You can check your database logs, and see that you actually queried:
select 1 + 1 /*foo='bar'*/