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  }