github.com/newrelic/go-agent@v3.26.0+incompatible/_integrations/nrsqlite3/nrsqlite3.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 // +build go1.10 5 6 // Package nrsqlite3 instruments https://github.com/mattn/go-sqlite3. 7 // 8 // Use this package to instrument your SQLite calls without having to manually 9 // create DatastoreSegments. This is done in a two step process: 10 // 11 // 1. Use this package's driver in place of the sqlite3 driver. 12 // 13 // If your code is using sql.Open like this: 14 // 15 // import ( 16 // _ "github.com/mattn/go-sqlite3" 17 // ) 18 // 19 // func main() { 20 // db, err := sql.Open("sqlite3", "./foo.db") 21 // } 22 // 23 // Then change the side-effect import to this package, and open "nrsqlite3" instead: 24 // 25 // import ( 26 // _ "github.com/newrelic/go-agent/_integrations/nrsqlite3" 27 // ) 28 // 29 // func main() { 30 // db, err := sql.Open("nrsqlite3", "./foo.db") 31 // } 32 // 33 // If you are registering a custom sqlite3 driver with special behavior then 34 // you must wrap your driver instance using nrsqlite3.InstrumentSQLDriver. For 35 // example, if your code looks like this: 36 // 37 // func main() { 38 // sql.Register("sqlite3_with_extensions", &sqlite3.SQLiteDriver{ 39 // Extensions: []string{ 40 // "sqlite3_mod_regexp", 41 // }, 42 // }) 43 // db, err := sql.Open("sqlite3_with_extensions", ":memory:") 44 // } 45 // 46 // Then instrument the driver like this: 47 // 48 // func main() { 49 // sql.Register("sqlite3_with_extensions", nrsqlite3.InstrumentSQLDriver(&sqlite3.SQLiteDriver{ 50 // Extensions: []string{ 51 // "sqlite3_mod_regexp", 52 // }, 53 // })) 54 // db, err := sql.Open("sqlite3_with_extensions", ":memory:") 55 // } 56 // 57 // 2. Provide a context containing a newrelic.Transaction to all exec and query 58 // methods on sql.DB, sql.Conn, sql.Tx, and sql.Stmt. This requires using the 59 // context methods ExecContext, QueryContext, and QueryRowContext in place of 60 // Exec, Query, and QueryRow respectively. For example, instead of the 61 // following: 62 // 63 // row := db.QueryRow("SELECT count(*) from tables") 64 // 65 // Do this: 66 // 67 // ctx := newrelic.NewContext(context.Background(), txn) 68 // row := db.QueryRowContext(ctx, "SELECT count(*) from tables") 69 // 70 // A working example is shown here: 71 // https://github.com/newrelic/go-agent/tree/master/_integrations/nrsqlite3/example/main.go 72 package nrsqlite3 73 74 import ( 75 "database/sql" 76 "database/sql/driver" 77 "path/filepath" 78 "strings" 79 80 sqlite3 "github.com/mattn/go-sqlite3" 81 newrelic "github.com/newrelic/go-agent" 82 "github.com/newrelic/go-agent/internal" 83 "github.com/newrelic/go-agent/internal/sqlparse" 84 ) 85 86 var ( 87 baseBuilder = newrelic.SQLDriverSegmentBuilder{ 88 BaseSegment: newrelic.DatastoreSegment{ 89 Product: newrelic.DatastoreSQLite, 90 }, 91 ParseQuery: sqlparse.ParseQuery, 92 ParseDSN: parseDSN, 93 } 94 ) 95 96 func init() { 97 sql.Register("nrsqlite3", InstrumentSQLDriver(&sqlite3.SQLiteDriver{})) 98 internal.TrackUsage("integration", "driver", "sqlite3") 99 } 100 101 // InstrumentSQLDriver wraps an sqlite3.SQLiteDriver to add instrumentation. 102 // For example, if you are registering a custom SQLiteDriver like this: 103 // 104 // sql.Register("sqlite3_with_extensions", 105 // &sqlite3.SQLiteDriver{ 106 // Extensions: []string{ 107 // "sqlite3_mod_regexp", 108 // }, 109 // }) 110 // 111 // Then add instrumentation like this: 112 // 113 // sql.Register("sqlite3_with_extensions", 114 // nrsqlite3.InstrumentSQLDriver(&sqlite3.SQLiteDriver{ 115 // Extensions: []string{ 116 // "sqlite3_mod_regexp", 117 // }, 118 // })) 119 // 120 func InstrumentSQLDriver(d *sqlite3.SQLiteDriver) driver.Driver { 121 return newrelic.InstrumentSQLDriver(d, baseBuilder) 122 } 123 124 func getPortPathOrID(dsn string) (ppoid string) { 125 ppoid = strings.Split(dsn, "?")[0] 126 ppoid = strings.TrimPrefix(ppoid, "file:") 127 128 if ":memory:" != ppoid && "" != ppoid { 129 if abs, err := filepath.Abs(ppoid); nil == err { 130 ppoid = abs 131 } 132 } 133 134 return 135 } 136 137 // ParseDSN accepts a DSN string and sets the Host, PortPathOrID, and 138 // DatabaseName fields on a newrelic.DatastoreSegment. 139 func parseDSN(s *newrelic.DatastoreSegment, dsn string) { 140 // See https://godoc.org/github.com/mattn/go-sqlite3#SQLiteDriver.Open 141 s.Host = "localhost" 142 s.PortPathOrID = getPortPathOrID(dsn) 143 s.DatabaseName = "" 144 }