Sqlx: Logging interface

Created on 18 Mar 2014  路  8Comments  路  Source: jmoiron/sqlx

Do you think it could be useful to use log.Logger and allow the ability to set a custom logger instead of using log.Println? Execv and friends are nice, but it would be great if those logs could be sent somewhere other than STDOUT.

Most helpful comment

Just thought I'd chime in with interesting idea I saw suggested on StackOverflow: https://stackoverflow.com/a/33042034/718180

sqlx has a very interesting abstraction in the form of the following interfaces:

They are used all through the library as the interfaces representing the functionality of using strings as SQL queries.

...

So if you are willing not to use the DB type directly but instead use the underlying free functions of the library, you can use the following structure to wrap your db into objects that perform logging:

type QueryLogger struct {
    queryer sqlx.Queryer
    logger  *log.Logger
}

func (p *QueryLogger) Query(query string, args ...interface{}) (*sql.Rows, error) {
    p.logger.Print(query, args...)
    return p.queryer.Query(query, args...)
}

func (p *QueryLogger) Queryx(query string, args ...interface{}) (*Rows, error) {
    p.logger.Print(query, args...)
    return p.queryer.Queryx(query, args...)
}

func (p *QueryLogger) QueryRowx(query string, args ...interface{}) *Row {
    p.logger.Print(query, args...)
    return p.queryer.QueryRowx(query, args...)
}

And when connecting to your database:

db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable")
if err != nil {
    log.Fatalln(err)
}

ql := &QueryLogger{db, yourLogger}

sqlx.Select(ql, &people, "SELECT * FROM person ORDER BY first_name ASC")

All 8 comments

You can get the standard logger to point to any io.Writer by calling log.SetOutput. Hopefully this will suffice for your needs.

Obviously, sqlx isn't a logging package. Sadly, the log package is rather simple and not really easily extended by custom loggers.

I'll have to look into what other packages do for logging. I feel like I see the generic standard logger in use pretty often. There are locking issues with trying to use multiple loggers on a single output file, so if I abandon the standard logger, that might lead to some unpredictable results for some people. In the worst case, they might start to lose messages they are currently getting.

Oh wow, I didn't know about log.SetOutput, I don't know how I missed that. Thanks!

It seems like using log.Logger and allowing custom loggers would be the right way to do it, but I don't know for sure. For example, see the log/syslog package: http://golang.org/pkg/log/syslog/

The logging variants are no longer part of sqlx because I felt their convenience was not worth the extra noise in the API documentation. They are trivially implemented in any codebase. Because of that this is not really an issue anymore.

I really like the way logrus is handling this.
Hooks can be registered, and will receive a copy of the log entry. Maybe Exec, Select, etc. could act the same. Adding logging / performance measurement would be easily achived, without creating a new type and wrap all the methods (which I has to do). Wrapping is weak and doesn't support API changes very well :(

(weak -> New methods are not wrapped until we write the code)
(changes -> Need to update all wrappers where API changed)

Just thought I'd chime in with interesting idea I saw suggested on StackOverflow: https://stackoverflow.com/a/33042034/718180

sqlx has a very interesting abstraction in the form of the following interfaces:

They are used all through the library as the interfaces representing the functionality of using strings as SQL queries.

...

So if you are willing not to use the DB type directly but instead use the underlying free functions of the library, you can use the following structure to wrap your db into objects that perform logging:

type QueryLogger struct {
    queryer sqlx.Queryer
    logger  *log.Logger
}

func (p *QueryLogger) Query(query string, args ...interface{}) (*sql.Rows, error) {
    p.logger.Print(query, args...)
    return p.queryer.Query(query, args...)
}

func (p *QueryLogger) Queryx(query string, args ...interface{}) (*Rows, error) {
    p.logger.Print(query, args...)
    return p.queryer.Queryx(query, args...)
}

func (p *QueryLogger) QueryRowx(query string, args ...interface{}) *Row {
    p.logger.Print(query, args...)
    return p.queryer.QueryRowx(query, args...)
}

And when connecting to your database:

db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable")
if err != nil {
    log.Fatalln(err)
}

ql := &QueryLogger{db, yourLogger}

sqlx.Select(ql, &people, "SELECT * FROM person ORDER BY first_name ASC")

How does one 'trivially implement this in any codebase'? Where is the hook to get at logging every query?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

huygn picture huygn  路  6Comments

ARolek picture ARolek  路  5Comments

resal81 picture resal81  路  5Comments

MichaelHipp picture MichaelHipp  路  5Comments

fpolli picture fpolli  路  5Comments