func (d *Destination) Close() {
	close(d.Channel)
}
Close closes the channel to the background goroutine.  This is only safe to call if you know
that the destination is no longer in use by any thread; in tests, for example.
package logutils
import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path"
	"runtime"
	"sort"
	"strings"
	"sync"
	"time"
	"github.com/prometheus/client_golang/prometheus"
	log "github.com/sirupsen/logrus"
)
const (
	FieldForceFlush = "__flush__"
	fieldFileName = "__file__"
	fieldLineNumber = "__line__"
)
func FilterLevels(maxLevel log.Level) []log.Level {
	levels := []log.Level{}
	for _, l := range log.AllLevels {
		if l <= maxLevel {
			levels = append(levels, l)
		}
	}
	return levels
}
type Formatter struct {
	Component string
}
func (f *Formatter) Format(entry *log.Entry) ([]byte, error) {
	stamp := entry.Time.Format("2006-01-02 15:04:05.000")
	levelStr := strings.ToUpper(entry.Level.String())
	pid := os.Getpid()
	fileName := entry.Data[fieldFileName]
	lineNo := entry.Data[fieldLineNumber]
	b := entry.Buffer
	if b == nil {
		b = &bytes.Buffer{}
	}
	if f.Component != "" {
		fmt.Fprintf(b, "%s [%s][%d] %s/%v %v: %v", stamp, levelStr, pid, f.Component, fileName, lineNo, entry.Message)
	} else {
		fmt.Fprintf(b, "%s [%s][%d] %v %v: %v", stamp, levelStr, pid, fileName, lineNo, entry.Message)
	}
	appendKVsAndNewLine(b, entry)
	return b.Bytes(), nil
}
func FormatForSyslog(entry *log.Entry) string {
	levelStr := strings.ToUpper(entry.Level.String())
	fileName := entry.Data[fieldFileName]
	lineNo := entry.Data[fieldLineNumber]
	b := entry.Buffer
	if b == nil {
		b = &bytes.Buffer{}
	}
	fmt.Fprintf(b, "%s %v %v: %v", levelStr, fileName, lineNo, entry.Message)
	appendKVsAndNewLine(b, entry)
	return b.String()
}
func appendKVsAndNewLine(b *bytes.Buffer, entry *log.Entry) {
	var keys []string = make([]string, 0, len(entry.Data))
	for k := range entry.Data {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, key := range keys {
		if key == fieldFileName || key == fieldLineNumber || key == FieldForceFlush {
			continue
		}
		var value interface{} = entry.Data[key]
		var stringifiedValue string
		if err, ok := value.(error); ok {
			stringifiedValue = err.Error()
		} else if stringer, ok := value.(fmt.Stringer); ok {
			stringifiedValue = stringer.String()
		} else {
			fmt.Fprintf(b, " %v=%#v", key, value)
			continue
		}
		b.WriteByte(' ')
		b.WriteString(key)
		b.WriteByte('=')
		b.WriteString(stringifiedValue)
	}
	b.WriteByte('\n')
}
type NullWriter struct{}
func (w *NullWriter) Write(p []byte) (int, error) {
	return len(p), nil
}
type ContextHook struct {
}
func (hook ContextHook) Levels() []log.Level {
	return log.AllLevels
}
func (hook ContextHook) Fire(entry *log.Entry) error {
	pcs := make([]uintptr, 10)
	if numEntries := runtime.Callers(1, pcs); numEntries > 0 {
		pcs = pcs[:numEntries]
		frames := runtime.CallersFrames(pcs)
		for {
			frame, more := frames.Next()
			if !shouldSkipFrame(frame) {
				entry.Data[fieldFileName] = path.Base(frame.File)
				entry.Data[fieldLineNumber] = frame.Line
				break
			}
			if !more {
				break
			}
		}
	}
	return nil
}
func shouldSkipFrame(frame runtime.Frame) bool {
	if strings.HasSuffix(frame.File, "/hooks.go") ||
		strings.HasSuffix(frame.File, "/entry.go") ||
		strings.HasSuffix(frame.File, "/logger.go") ||
		strings.HasSuffix(frame.File, "/exported.go") {
		if strings.Contains(frame.File, "/logrus") {
			return true
		}
	}
	if strings.HasSuffix(frame.File, "/lib/logutils/logutils.go") {
		if strings.Contains(frame.File, "/libcalico-go") {
			return true
		}
	}
	if strings.HasSuffix(frame.File, "/lib/logutils/ratelimitedlogger.go") {
		if strings.Contains(frame.File, "/libcalico-go") {
			return true
		}
	}
	return false
}
type QueuedLog struct {
	Level         log.Level
	Message       []byte
	SyslogMessage string
	WaitGroup     *sync.WaitGroup
	NumSkippedLogs uint
}
func (ql QueuedLog) OnLogDone() {
	if ql.WaitGroup != nil {
		ql.WaitGroup.Done()
	}
}
func NewStreamDestination(
	level log.Level,
	writer io.Writer,
	c chan QueuedLog,
	disableLogDropping bool,
	counter prometheus.Counter,
) *Destination {
	return &Destination{
		Level:   level,
		Channel: c,
		writeLog: func(ql QueuedLog) error {
			if ql.NumSkippedLogs > 0 {
				fmt.Fprintf(writer, "... dropped %d logs ...\n",
					ql.NumSkippedLogs)
			}
			_, err := writer.Write(ql.Message)
			return err
		},
		disableLogDropping: disableLogDropping,
		counter:            counter,
	}
}
func NewSyslogDestination(
	level log.Level,
	writer syslogWriter,
	c chan QueuedLog,
	disableLogDropping bool,
	counter prometheus.Counter,
) *Destination {
	return &Destination{
		Level:   level,
		Channel: c,
		writeLog: func(ql QueuedLog) error {
			if ql.NumSkippedLogs > 0 {
				writer.Warning(fmt.Sprintf("... dropped %d logs ...\n",
					ql.NumSkippedLogs))
			}
			err := writeToSyslog(writer, ql)
			return err
		},
		disableLogDropping: disableLogDropping,
		counter:            counter,
	}
}
type Destination struct {
	Level log.Level
	Channel chan QueuedLog
	writeLog func(ql QueuedLog) error
	disableLogDropping bool
	lock           sync.Mutex
	numDroppedLogs uint
	counter prometheus.Counter
}
func (d *Destination) Send(ql QueuedLog) (ok bool) {
	if d.disableLogDropping {
		d.Channel <- ql
		ok = true
		return
	}
	d.lock.Lock()
	ql.NumSkippedLogs = d.numDroppedLogs
	select {
	case d.Channel <- ql:
		d.numDroppedLogs = 0
		ok = true
	default:
		d.numDroppedLogs += 1
	}
	d.lock.Unlock()
	return
}
func (d *Destination) LoopWritingLogs() {
	for ql := range d.Channel {
		err := d.writeLog(ql)
		if err != nil {
			d.counter.Inc()
			fmt.Fprintf(os.Stderr, "Failed to write to log: %v", err)
		}
		ql.OnLogDone()
	}
}