// Package zapadapter provides a logger that writes to a go.uber.org/zap.Logger.
package zapadapter

import (
	"github.com/jackc/pgx"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

type Logger struct {
	logger *zap.Logger
}

func NewLogger(logger *zap.Logger) *Logger {
	return &Logger{logger: logger.WithOptions(zap.AddCallerSkip(1))}
}

func (pl *Logger) Log(level pgx.LogLevel, msg string, data map[string]interface{}) {
	fields := make([]zapcore.Field, len(data))
	i := 0
	for k, v := range data {
		fields[i] = zap.Reflect(k, v)
		i++
	}

	switch level {
	case pgx.LogLevelTrace:
		pl.logger.Debug(msg, append(fields, zap.Stringer("PGX_LOG_LEVEL", level))...)
	case pgx.LogLevelDebug:
		pl.logger.Debug(msg, fields...)
	case pgx.LogLevelInfo:
		pl.logger.Info(msg, fields...)
	case pgx.LogLevelWarn:
		pl.logger.Warn(msg, fields...)
	case pgx.LogLevelError:
		pl.logger.Error(msg, fields...)
	default:
		pl.logger.Error(msg, append(fields, zap.Stringer("PGX_LOG_LEVEL", level))...)
	}
}

// Package zapadapter provides a logger that writes to a go.uber.org/zap.Logger.
package zapadapter

import (
	"github.com/jackc/pgx"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

type Logger struct {
	logger *zap.Logger
}

func NewLogger(logger *zap.Logger) *Logger {
	return &Logger{logger: logger.WithOptions(zap.AddCallerSkip(1))}
}

func (pl *Logger) Log(level pgx.LogLevel, msg string, data map[string]interface{}) {
	fields := make([]zapcore.Field, len(data))
	i := 0
	for k, v := range data {
		fields[i] = zap.Any(k, v)
		i++
	}

	switch level {
	case pgx.LogLevelTrace:
		pl.logger.Debug(msg, append(fields, zap.Stringer("PGX_LOG_LEVEL", level))...)
	case pgx.LogLevelDebug:
		pl.logger.Debug(msg, fields...)
	case pgx.LogLevelInfo:
		pl.logger.Info(msg, fields...)
	case pgx.LogLevelWarn:
		pl.logger.Warn(msg, fields...)
	case pgx.LogLevelError:
		pl.logger.Error(msg, fields...)
	default:
		pl.logger.Error(msg, append(fields, zap.Stringer("PGX_LOG_LEVEL", level))...)
	}
}

Use zap.Any for handling interface{} -> zap.Field conversion
Use zap.Any for handling interface{} -> zap.Field conversion

zap.Any falls back to zap.Reflect, but is better for this case, because
it first checks for the types that zap handles specially.  For example,
time.Duration, or error, which zap.Reflect will just treat as untyped
int64 or struct objects, but zap.Any is able to detect these types and
print them properly.
