Copyright 2019 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package database

import (
	
	
	
	
	
	
	

	
	
)
QueryLoggingDisabled stops logging of queries when true. For use in tests only: not concurrency-safe.
var QueryLoggingDisabled bool

var queryCounter int64 // atomic: per-process counter for unique query IDs

type queryEndLogEntry struct {
	ID              string
	Query           string
	Args            string
	DurationSeconds float64
	Error           string `json:",omitempty"`
}

func ( context.Context,  string,  []interface{},  string,  bool) func(*error) {
	if QueryLoggingDisabled {
		return func(*error) {}
	}
	const  = 300 // maximum length of displayed query
To make the query more compact and readable, replace newlines with spaces and collapse adjacent whitespace.
	var  []rune
	for ,  := range  {
		if  == '\n' {
			 = ' '
		}
		if len() == 0 || !unicode.IsSpace([len()-1]) || !unicode.IsSpace() {
			 = append(, )
		}
	}
	 = string()
	if len() >  {
		 = [:] + "..."
	}

	 := generateLoggingID()
Construct a short string of the args.
	const (
		   = 20
		 = 50
	)
	var  []string
	for  := 0;  < len() &&  < ; ++ {
		 := fmt.Sprint([])
		if len() >  {
			 = [:] + "..."
		}
		 = append(, )
	}
	if len() >  {
		 = append(, "...")
	}
	 := strings.Join(, ", ")

	log.Debugf(, "%s %s args=%s", , , )
	 := time.Now()
	return func( *error) {
		 := time.Since()
		if  == nil { // happens with queryRow
			log.Debugf(, "%s done", )
		} else {
			derrors.Wrap(, "DB running query %s", )
			 := queryEndLogEntry{
				ID:              ,
				Query:           ,
				Args:            ,
				DurationSeconds: .Seconds(),
			}
			if * == nil {
				log.Debug(, )
			} else {
There are many places in our logs when a query will be canceled, because all unfinished search queries for a given request are canceled: https://github.com/golang/pkgsite/blob/03662129627796aa387a26b8f4f9251caf5d57fd/internal/postgres/search.go#L178 We don't want to log these as errors, because it makes the logs very noisy. Based on https://github.com/lib/pq/issues/577#issuecomment-298341053 it seems that ctx.Err() could return nil because this error is coming from postgres. github.com/lib/pq currently handles errors like these in their tests by hardcoding the string: https://github.com/lib/pq/blob/e53edc9b26000fec4c4e357122d56b0f66ace6ea/go18_test.go#L89
				 := log.Error
				if errors.Is(.Err(), context.Canceled) ||
					strings.Contains(.Error, "pq: canceling statement due to user request") {
					 = log.Debug
If the transaction is retryable and this is a serialization error, then it's not really an error at all. Log it as a warning, so if we get a "failed due to max retries" error, we can find these easily.
				if  && isSerializationFailure(*) {
					 = log.Warning
				}
				(, )
			}
		}
	}
}

func ( *DB) ( context.Context) func(*error) {
	if QueryLoggingDisabled {
		return func(*error) {}
	}
	 := generateLoggingID(.instanceID)
	log.Debugf(, "%s transaction (isolation %s) started", , .opts.Isolation)
	 := time.Now()
	return func( *error) {
		log.Debugf(, "%s transaction (isolation %s) finished in %s with error %v",
			, .opts.Isolation, time.Since(), *)
	}
}

func ( string) string {
	if  == "" {
		 = "local"
Instance IDs are long strings. The low-order part seems quite random, so shortening the ID will still likely result in something unique.
		 = [len()-4:]
	}
	 := atomic.AddInt64(&queryCounter, 1)
	return fmt.Sprintf("%s-%d", , )