func (lgr *Logr) TargetInfos() []TargetInfo {
	infos := make([]TargetInfo, 0)
	lgr.tmux.RLock()
	defer lgr.tmux.RUnlock()
	for _, host := range lgr.targetHosts {
		inf := TargetInfo{
			Name: host.String(),
			Type: fmt.Sprintf("%T", host.target),
		}
		infos = append(infos, inf)
	}
	return infos
}
TargetInfos enumerates all the targets added to this lgr.
The resulting slice represents a snapshot at time of calling.
package logr
import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"os"
	"sync"
	"sync/atomic"
	"time"
	"github.com/wiggin77/merror"
)
type Logr struct {
	tmux        sync.RWMutex  
	targetHosts []*TargetHost
	in         chan *LogRec
	quit       chan struct{}  
	done       chan struct{}  
	lvlCache   levelCache
	bufferPool sync.Pool
	options    *options
	metricsMux sync.RWMutex
	metrics    *metrics
	shutdown int32
}
func New(opts ...Option) (*Logr, error) {
	options := &options{
		maxQueueSize:    DefaultMaxQueueSize,
		enqueueTimeout:  DefaultEnqueueTimeout,
		shutdownTimeout: DefaultShutdownTimeout,
		flushTimeout:    DefaultFlushTimeout,
		maxPooledBuffer: DefaultMaxPooledBuffer,
	}
	lgr := &Logr{options: options}
	for _, opt := range opts {
		if err := opt(lgr); err != nil {
			return nil, err
		}
	}
	pkgName := GetLogrPackageName()
	if pkgName != "" {
		opt := StackFilter(pkgName, pkgName+"/targets", pkgName+"/formatters")
		_ = opt(lgr)
	}
	lgr.in = make(chan *LogRec, lgr.options.maxQueueSize)
	lgr.quit = make(chan struct{})
	lgr.done = make(chan struct{})
	if lgr.options.useSyncMapLevelCache {
		lgr.lvlCache = &syncMapLevelCache{}
	} else {
		lgr.lvlCache = &arrayLevelCache{}
	}
	lgr.lvlCache.setup()
	lgr.bufferPool = sync.Pool{
		New: func() interface{} {
			return new(bytes.Buffer)
		},
	}
	lgr.initMetrics(lgr.options.metricsCollector, lgr.options.metricsUpdateFreqMillis)
	go lgr.start()
	return lgr, nil
}
func (lgr *Logr) AddTarget(target Target, name string, filter Filter, formatter Formatter, maxQueueSize int) error {
	if lgr.IsShutdown() {
		return fmt.Errorf("AddTarget called after Logr shut down")
	}
	lgr.metricsMux.RLock()
	metrics := lgr.metrics
	lgr.metricsMux.RUnlock()
	hostOpts := targetHostOptions{
		name:         name,
		filter:       filter,
		formatter:    formatter,
		maxQueueSize: maxQueueSize,
		metrics:      metrics,
	}
	host, err := newTargetHost(target, hostOpts)
	if err != nil {
		return err
	}
	lgr.tmux.Lock()
	defer lgr.tmux.Unlock()
	lgr.targetHosts = append(lgr.targetHosts, host)
	lgr.ResetLevelCache()
	return nil
}
func (lgr *Logr) NewLogger() Logger {
	logger := Logger{lgr: lgr}
	return logger
}
var levelStatusDisabled = LevelStatus{}
func (lgr *Logr) IsLevelEnabled(lvl Level) LevelStatus {
	if atomic.LoadInt32(&lgr.shutdown) != 0 {
		return levelStatusDisabled
	}
	status, ok := lgr.lvlCache.get(lvl.ID)
	if ok {
		return status
	}
	status = LevelStatus{}
	lgr.tmux.RLock()
	defer lgr.tmux.RUnlock()
	for _, host := range lgr.targetHosts {
		enabled, level := host.IsLevelEnabled(lvl)
		if enabled {
			status.Enabled = true
			if level.Stacktrace || host.formatter.IsStacktraceNeeded() {
				status.Stacktrace = true
				break  
			}
		}
	}
	if err := lgr.lvlCache.put(lvl.ID, status); err != nil {
		lgr.ReportError(err)
		return LevelStatus{}
	}
	return status
}
func (lgr *Logr) HasTargets() bool {
	lgr.tmux.RLock()
	defer lgr.tmux.RUnlock()
	return len(lgr.targetHosts) > 0
}
type TargetInfo struct {
	Name string
	Type string
}