func NewWrappedLogCloser(logger Logger, delegate Logger) *WrappedLogCloser {
	return &WrappedLogCloser{logger, delegate}
}
NewWrappedLogCloser returns a new instance of WrappedLogCloser
package log
import (
	"bytes"
	"encoding"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"
	"github.com/fatih/color"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/pinpt/go-common/v10/fileutil"
	pjson "github.com/pinpt/go-common/v10/json"
	"github.com/pinpt/go-common/v10/number"
	pos "github.com/pinpt/go-common/v10/os"
	pstring "github.com/pinpt/go-common/v10/strings"
	"github.com/pinpt/go-common/v10/term"
)
type Logger interface {
	Log(keyvals ...interface{}) error
}
var ErrMissingValue = errors.New("(MISSING)")
func With(logger Logger, keyvals ...interface{}) Logger {
	return log.With(logger, keyvals...)
}
func Info(logger Logger, msg string, kv ...interface{}) error {
	a := []interface{}{msgKey, msg}
	if kv != nil {
		a = append(a, kv...)
	}
	return level.Info(logger).Log(a...)
}
func Debug(logger Logger, msg string, kv ...interface{}) error {
	a := []interface{}{msgKey, msg}
	if kv != nil {
		a = append(a, kv...)
	}
	return level.Debug(logger).Log(a...)
}
func Warn(logger Logger, msg string, kv ...interface{}) error {
	a := []interface{}{msgKey, msg}
	if kv != nil {
		a = append(a, kv...)
	}
	return level.Warn(logger).Log(a...)
}
func Error(logger Logger, msg string, kv ...interface{}) error {
	a := []interface{}{msgKey, msg}
	if kv != nil {
		a = append(a, kv...)
	}
	return level.Error(logger).Log(a...)
}
var (
	TerminationLogPath = "/dev/termination-log"
	terminateOnFatal = true
)
func Fatal(logger Logger, msg string, kv ...interface{}) {
	a := []interface{}{msgKey, msg}
	if kv != nil {
		a = append(a, kv...)
	}
	level.Error(logger).Log(a...)
	os.Stderr.Sync()
	os.Stdout.Sync()
	if isContainer && TerminationLogPath != "" {
		var m strings.Builder
		m.WriteString("FATAL: ")
		m.WriteString(msg)
		for i, val := range kv {
			if i == 0 {
				m.WriteString(" ")
			}
			m.WriteString(fmt.Sprintf("%v", val))
			if i%2 == 0 {
				m.WriteString("=")
			} else if i+1 < len(kv) {
				m.WriteString(" ")
			}
		}
		ioutil.WriteFile(TerminationLogPath, []byte(m.String()), 0600)
	}
	if terminateOnFatal {
		pos.Exit(1)
	}
}
type consoleLogger struct {
	w     io.Writer
	pkg   string
	theme ColorTheme
}
var (
	infoColor     = color.New(color.FgGreen)
	warnColor     = color.New(color.FgRed)
	errColor      = color.New(color.FgRed, color.Bold)
	debugColor    = color.New(color.FgBlue)
	pkgColor      = color.New(color.FgHiMagenta)
	msgColor      = color.New(color.FgWhite, color.Bold)
	msgLightColor = color.New(color.FgBlack, color.Bold)
	kvColor       = color.New(color.FgYellow)
	tsColor       = color.New(color.FgCyan)
	termMu       sync.RWMutex
	termWidth    = term.GetTerminalWidth()
	ansiStripper = regexp.MustCompile("\\x1b\\[[0-9;]*m")
)
const (
	pkgKey = "pkg"
	msgKey = "message"
	tsKey  = "ts"
)
var (
	levelKey      = fmt.Sprintf("%v", level.Key())
	debugLevel    = level.DebugValue().String()
	warnLevel     = level.WarnValue().String()
	errLevel      = level.ErrorValue().String()
	infoLevel     = level.InfoValue().String()
	customerIDKey = "customer_id"
	onprem        = os.Getenv("PP_CUSTOMER_ID") != ""
)
func (l *consoleLogger) Log(keyvals ...interface{}) error {
	n := (len(keyvals) + 1) / 2  
	m := make(map[string]interface{}, n)
	m[pkgKey] = l.pkg
	keys := make([]string, 0)
	for i := 0; i < len(keyvals); i += 2 {
		k := keyvals[i]
		if s, ok := k.(string); ok {
			if s[0:1] == "$" {
				continue
			}
			if onprem && s == customerIDKey {
				continue
			}
		}
		var v interface{} = ErrMissingValue
		if i+1 < len(keyvals) {
			v = keyvals[i+1]
		}
		merge(m, k, v)
		keys = append(keys, fmt.Sprintf("%v", k))
	}
	hasColors := !color.NoColor && l.theme != NoColorTheme
	lvl := fmt.Sprintf("%v", m[levelKey])
	var c *color.Color
	if hasColors {
		switch lvl {
		case debugLevel:
			c = debugColor
		case warnLevel:
			c = warnColor
		case errLevel:
			c = errColor
		case infoLevel:
			c = infoColor
		default:
			lvl = debugLevel
			c = debugColor
		}
	}
	pkg := m[pkgKey].(string)
	if len(pkg) > 7 {
		pkg = pkg[0:7]
	}
	kv := make([]string, 0)
	var msg string
	if ms, ok := m[msgKey].(string); ok {
		msg = ansiStripper.ReplaceAllString(ms, "")
	} else {
		msg = fmt.Sprintf("%v", m[msgKey])
	}
	left := len(msg) + 7 + 10
	slen := 0
	scnt := 0
	sort.Strings(keys)
	var ts string
	for _, k := range keys {
		switch k {
		case tsKey:
			ts = fmt.Sprintf("%v ", m[k])
			if color.NoColor {
				left += 10
			} else {
				left += 20
			}
			continue
		case levelKey, pkgKey, msgKey:
			{
				continue
			}
		}
		v := m[k]
		k = ansiStripper.ReplaceAllString(k, "")
		val := possiblyTruncate(ansiStripper.ReplaceAllString(strings.TrimSpace(fmt.Sprintf("%v", v)), ""))
		slen += 1 + len(k) + len(val)
		scnt++
		if hasColors {
			kv = append(kv, fmt.Sprintf("%s=%s", kvColor.Sprint(k), kvColor.Sprint(val)))
		} else {
			kv = append(kv, fmt.Sprintf("%s=%s", k, val))
		}
	}
	var kvs string
	termMu.RLock()
	pad := int(termWidth) - left
	termMu.RUnlock()
	slen += scnt - 1
	if len(kv) > 0 {
		kvs = strings.Join(kv, " ")
		var buf bytes.Buffer
		for i := 0; i < pad-slen; i++ {
			buf.WriteByte(' ')
		}
		buf.WriteString(kvs)
		kvs = buf.String()
	}
	o := l.w
	if l.w == os.Stdout && !color.NoColor {
		o = color.Output
	}
	if color.NoColor {
		fmt.Fprintf(o, "%s%s %s %s %s\n", ts, fmt.Sprintf("%-6s", strings.ToUpper(lvl)), fmt.Sprintf("%-8s", pkg), msg, kvs)
	} else {
		mc := msgColor
		if l.theme == LightLogColorTheme {
			mc = msgLightColor
		}
		fmt.Fprintf(o, "%s%s %s %s %s\n", tsColor.Sprint(ts), c.Sprintf("%-6s", strings.ToUpper(lvl)), pkgColor.Sprintf("%-8s", pkg), mc.Sprint(msg), kvs)
	}
	return nil
}
func merge(dst map[string]interface{}, k, v interface{}) {
	var key string
	switch x := k.(type) {
	case string:
		key = x
	case fmt.Stringer:
		key = safeString(x)
	default:
		key = fmt.Sprint(x)
	}
	switch x := v.(type) {
	case json.Marshaler:
	case encoding.TextMarshaler:
	case error:
		v = safeError(x)
	case string:
		v = possiblyTruncate(x)
	case fmt.Stringer:
		v = safeString(x)
	}
	dst[key] = v
}
func possiblyTruncate(s string) string {
	if len(s) > MaxStringValueLength {
		s = s[0:MaxStringValueLength] + fmt.Sprintf("(...+%v)", number.ToBytesSize(int64(len(s)-MaxStringValueLength)))
	}
	return s
}
var MaxStringValueLength = 1024 * 10  
func safeString(str fmt.Stringer) (s string) {
	defer func() {
		if panicVal := recover(); panicVal != nil {
			if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
				s = "NULL"
			} else {
				panic(panicVal)
			}
		}
	}()
	s = possiblyTruncate(str.String())
	return
}
func safeError(err error) (s interface{}) {
	defer func() {
		if panicVal := recover(); panicVal != nil {
			if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
				s = nil
			} else {
				panic(panicVal)
			}
		}
	}()
	s = err.Error()
	return
}
type OutputFormat byte
const (
	JSONLogFormat OutputFormat = 1 << iota
	LogFmtLogFormat
	ConsoleLogFormat
)
type ColorTheme byte
const (
	DarkLogColorTheme ColorTheme = 1 << iota
	LightLogColorTheme
	NoColorTheme
)
type Level byte
const (
	InfoLevel Level = 1 << iota
	DebugLevel
	WarnLevel
	ErrorLevel
	NoneLevel
)
func LevelFromString(level string) Level {
	switch level {
	case "info", "INFO", "":
		{
			return InfoLevel
		}
	case "debug", "DEBUG":
		{
			return DebugLevel
		}
	case "warn", "WARN", "warning", "WARNING":
		{
			return WarnLevel
		}
	case "error", "ERROR", "fatal", "FATAL":
		{
			return ErrorLevel
		}
	}
	return NoneLevel
}
type LoggerCloser interface {
	Logger
	Close() error
}
type logcloser struct {
	w       io.WriteCloser
	l       Logger
	o       sync.Once
	loggers []Logger
}
func (l *logcloser) Log(kv ...interface{}) error {
	return l.l.Log(kv...)
}
func (l *logcloser) Close() error {
	l.o.Do(func() {
		if w, ok := l.l.(LoggerCloser); ok {
			w.Close()
		}
		if l.loggers != nil && len(l.loggers) > 0 {
			for _, logger := range l.loggers {
				if w, ok := logger.(LoggerCloser); ok {
					w.Close()
				}
			}
		}
		if l.w == os.Stdout || l.w == os.Stderr {
			return
		}
		l.w.Close()
	})
	return nil
}
type nocloselog struct {
	l Logger
}
func (l *nocloselog) Log(kv ...interface{}) error {
	return l.l.Log(kv...)
}
func (l *nocloselog) Close() error {
	if w, ok := l.l.(LoggerCloser); ok {
		return w.Close()
	}
	return nil
}
type WithLogOptions func(logger Logger) Logger
func WithDefaultTimestampLogOption(layout ...string) WithLogOptions {
	return func(logger Logger) Logger {
		if len(layout) == 0 {
			return log.With(logger, tsKey, log.TimestampFormat(time.Now, time.StampMilli))
		}
		return log.With(logger, tsKey, log.TimestampFormat(time.Now, layout[0]))
	}
}
type BackgroundLoggerCallback func(fn string)
type crashlogger struct {
	next     Logger
	ch       chan string
	callback BackgroundLoggerCallback
	wg       sync.WaitGroup
	once     sync.Once
	started  bool
}
func (l *crashlogger) Close() error {
	l.once.Do(func() {
		close(l.ch)
		if l.started {
			l.started = false
			l.wg.Wait()
		}
		if l.callback != nil {
			l.callback(os.Getenv("PP_LOGFILE"))
		}
		l.ch = nil
	})
	return nil
}
func (l *crashlogger) Log(keyvals ...interface{}) error {
	l.next.Log(keyvals...)
	if l.ch != nil {
		return pumpLogsIntoChan(l.ch, keyvals...)
	}
	return nil
}
var ErrLogQueueFull = errors.New("error: log queue full, dropping log message")
func pumpLogsIntoChan(pipe chan string, keyvals ...interface{}) error {
	n := (len(keyvals) + 1) / 2  
	m := make(map[string]interface{}, n)
	for i := 0; i < len(keyvals); i += 2 {
		k := keyvals[i]
		var v interface{} = log.ErrMissingValue
		if i+1 < len(keyvals) {
			v = keyvals[i+1]
		}
		merge(m, k, v)
	}
	select {
	case pipe <- pjson.Stringify(m):
		return nil
	default:
		return ErrLogQueueFull
	}
}
func (l *crashlogger) run() {
	l.started = true
	l.wg.Add(1)
	go func() {
		defer l.wg.Done()
		f := getLogFd()
		defer f.Close()
		ch := l.ch
		for buf := range ch {
			n, err := fmt.Fprintln(f, buf)
			if n < 0 || err != nil {
				break
			}
		}
		f.Sync()
		f.Close()
	}()
}
func WithBackgroundLogger(callback BackgroundLoggerCallback) WithLogOptions {
	return func(logger Logger) Logger {
		l := &crashlogger{
			next:     logger,
			ch:       make(chan string, 1000),
			callback: callback,
		}
		l.run()
		return l
	}
}
func WithCrashLogger() WithLogOptions {
	pos.OnExit(func(_ int) {
		DeleteLogFile()
	})
	return func(logger Logger) Logger {
		l := &crashlogger{
			next: logger,
			ch:   make(chan string, 1000),
		}
		l.run()
		return l
	}
}
func getLogFd() *os.File {
	path := os.Getenv("PP_LOGFILE")
	if path == "" {
		path = NewRandomLogPath()
		os.Setenv("PP_LOGFILE", path)
	}
	if fileutil.FileExists(path) {
		f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_EXCL, 0600)
		if err != nil {
			panic(fmt.Errorf("Error opening log file %v", err))
		}
		return f
	}
	f, err := os.Create(path)
	if err != nil {
		panic(fmt.Errorf("Error creating crash log file at %s. %v", path, err))
	}
	return f
}
func NewRandomLogPath() string {
	tmp := os.TempDir()
	fn := filepath.Join(tmp, fmt.Sprintf("pinpt-%d%d", time.Now().Unix(), os.Getpid()))
	os.Setenv("PP_LOGFILE", fn)
	return fn
}
func DeleteLogFile() error {
	path := os.Getenv("PP_LOGFILE")
	return os.Remove(path)
}
type WrappedLogCloser struct {
	logger   Logger
	delegate Logger
}
func (l *WrappedLogCloser) Close() error {
	if w, ok := l.logger.(LoggerCloser); ok {
		w.Close()
	}
	if w, ok := l.delegate.(LoggerCloser); ok {
		w.Close()
	}
	return nil
}
func (l *WrappedLogCloser) Log(keyvals ...interface{}) error {
	return l.delegate.Log(keyvals...)
}