func (b *Builder) Clone() *Builder {
	newB := &Builder{
		WriteKey:   b.WriteKey,
		Dataset:    b.Dataset,
		SampleRate: b.SampleRate,
		APIHost:    b.APIHost,
		dynFields:  make([]dynamicField, 0, len(b.dynFields)),
		client:     b.client,
	}
	newB.data = make(map[string]interface{})
	b.lock.RLock()
	defer b.lock.RUnlock()
	for k, v := range b.data {
		newB.data[k] = v
	}
	b.dynFieldsLock.RLock()
	defer b.dynFieldsLock.RUnlock()
	for _, dynFd := range b.dynFields {
		newB.dynFields = append(newB.dynFields, dynFd)
	}
	return newB
}
Clone creates a new builder that inherits all traits of this builder and
creates its own scope in which to add additional static and dynamic fields.
package libhoney
import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"path"
	"reflect"
	"sort"
	"strings"
	"sync"
	"time"
	"github.com/honeycombio/libhoney-go/transmission"
	statsd "gopkg.in/alexcesaro/statsd.v2"
)
func init() {
	rand.Seed(time.Now().UnixNano())
	transmission.Version = version
}
const (
	defaultSampleRate = 1
	defaultAPIHost    = "https://api.honeycomb.io/"
	defaultDataset    = "libhoney-go dataset"
	version           = "1.12.0"
	DefaultMaxBatchSize = 50
	DefaultBatchTimeout = 100 * time.Millisecond
	DefaultMaxConcurrentBatches = 80
	DefaultPendingWorkCapacity = 10000
)
var (
	ptrKinds = []reflect.Kind{reflect.Ptr, reflect.Slice, reflect.Map}
)
var (
	dc = &Client{}
	transitionResponses chan Response
	oneResp sync.Once
)
var sd, _ = statsd.New(statsd.Mute(true))
var UserAgentAddition string
type Config struct {
	APIKey string
	WriteKey string
	Dataset string
	SampleRate uint
	APIHost string
	BlockOnSend bool
	BlockOnResponse bool
	Output Output
	Transmission transmission.Sender
	MaxBatchSize         uint           
	SendFrequency        time.Duration  
	MaxConcurrentBatches uint           
	PendingWorkCapacity  uint           
	Transport http.RoundTripper
	Logger Logger
}
func Init(conf Config) error {
	clientConf := ClientConfig{}
	switch {
	case conf.APIKey != "":
		clientConf.APIKey = conf.APIKey
	case conf.WriteKey != "":
		clientConf.APIKey = conf.WriteKey
	default:
	}
	clientConf.Dataset = conf.Dataset
	clientConf.SampleRate = conf.SampleRate
	clientConf.APIHost = conf.APIHost
	if conf.Logger == nil {
		conf.Logger = &nullLogger{}
	}
	clientConf.Logger = conf.Logger
	if conf.MaxBatchSize == 0 {
		conf.MaxBatchSize = DefaultMaxBatchSize
	}
	if conf.SendFrequency == 0 {
		conf.SendFrequency = DefaultBatchTimeout
	}
	if conf.MaxConcurrentBatches == 0 {
		conf.MaxConcurrentBatches = DefaultMaxConcurrentBatches
	}
	if conf.PendingWorkCapacity == 0 {
		conf.PendingWorkCapacity = DefaultPendingWorkCapacity
	}
	var t transmission.Sender
	switch {
	case conf.Transmission != nil:
		t = conf.Transmission
	case conf.Output != nil:
		t = &transitionOutput{
			Output:          conf.Output,
			blockOnResponse: conf.BlockOnResponse,
			responses:       make(chan transmission.Response, 2*conf.PendingWorkCapacity),
		}
	default:
		t = &transmission.Honeycomb{
			MaxBatchSize:         conf.MaxBatchSize,
			BatchTimeout:         conf.SendFrequency,
			MaxConcurrentBatches: conf.MaxConcurrentBatches,
			PendingWorkCapacity:  conf.PendingWorkCapacity,
			BlockOnSend:          conf.BlockOnSend,
			BlockOnResponse:      conf.BlockOnResponse,
			Transport:            conf.Transport,
			UserAgentAddition:    UserAgentAddition,
			Logger:               clientConf.Logger,
			Metrics:              sd,
		}
	}
	clientConf.Transmission = t
	var err error
	dc, err = NewClient(clientConf)
	return err
}
type Output interface {
	Add(ev *Event)
	Start() error
	Stop() error
}
type transitionOutput struct {
	Output
	blockOnResponse bool
	responses       chan transmission.Response
}
func (to *transitionOutput) Add(ev *transmission.Event) {
	origEvent := &Event{
		APIHost:     ev.APIHost,
		WriteKey:    ev.APIKey,
		Dataset:     ev.Dataset,
		SampleRate:  ev.SampleRate,
		Timestamp:   ev.Timestamp,
		Metadata:    ev.Metadata,
		fieldHolder: fieldHolder{data: ev.Data},
	}
	to.Output.Add(origEvent)
}
func (to *transitionOutput) TxResponses() chan transmission.Response {
	return to.responses
}
func (to *transitionOutput) SendResponse(r transmission.Response) bool {
	if to.blockOnResponse {
		to.responses <- r
	} else {
		select {
		case to.responses <- r:
		default:
			return true
		}
	}
	return false
}
func VerifyWriteKey(config Config) (team string, err error) {
	return VerifyAPIKey(config)
}
func VerifyAPIKey(config Config) (team string, err error) {
	dc.ensureLogger()
	defer func() { dc.logger.Printf("verify write key got back %s with err=%s", team, err) }()
	if config.APIKey == "" {
		if config.WriteKey == "" {
			return team, errors.New("config.APIKey and config.WriteKey are both empty; can't verify empty key")
		}
		config.APIKey = config.WriteKey
	}
	if config.APIHost == "" {
		config.APIHost = defaultAPIHost
	}
	u, err := url.Parse(config.APIHost)
	if err != nil {
		return team, fmt.Errorf("Error parsing API URL: %s", err)
	}
	u.Path = path.Join(u.Path, "1", "team_slug")
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return team, err
	}
	req.Header.Set("User-Agent", UserAgentAddition)
	req.Header.Add("X-Honeycomb-Team", config.APIKey)
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return team, err
	}
	defer resp.Body.Close()
	if resp.StatusCode == http.StatusUnauthorized {
		return team, errors.New("Write key provided is invalid")
	}
	body, _ := ioutil.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		return team, fmt.Errorf(`Abnormal non-200 response verifying Honeycomb write key: %d
Response body: %s`, resp.StatusCode, string(body))
	}
	ret := map[string]string{}
	if err := json.Unmarshal(body, &ret); err != nil {
		return team, err
	}
	return ret["team_slug"], nil
}
type Response struct {
	transmission.Response
}
type Event struct {
	WriteKey string
	Dataset string
	SampleRate uint
	APIHost string
	Timestamp time.Time
	Metadata interface{}
	fieldHolder
	client *Client
	sent     bool
	sendLock sync.Mutex
}
type Builder struct {
	WriteKey string
	Dataset string
	SampleRate uint
	APIHost string
	fieldHolder
	dynFields     []dynamicField
	dynFieldsLock sync.RWMutex
	client *Client
}
type fieldHolder struct {
	data marshallableMap
	lock sync.RWMutex
}
type marshallableMap map[string]interface{}
func (m marshallableMap) MarshalJSON() ([]byte, error) {
	keys := make([]string, len(m))
	i := 0
	for k := range m {
		keys[i] = k
		i++
	}
	sort.Strings(keys)
	out := bytes.NewBufferString("{")
	first := true
	for _, k := range keys {
		b, ok := maybeMarshalValue(m[k])
		if ok {
			if first {
				first = false
			} else {
				out.WriteByte(',')
			}
			out.WriteByte('"')
			out.Write([]byte(k))
			out.WriteByte('"')
			out.WriteByte(':')
			out.Write(b)
		}
	}
	out.WriteByte('}')
	return out.Bytes(), nil
}
func maybeMarshalValue(v interface{}) ([]byte, bool) {
	if v == nil {
		return nil, false
	}
	val := reflect.ValueOf(v)
	kind := val.Type().Kind()
	for _, ptrKind := range ptrKinds {
		if kind == ptrKind && val.IsNil() {
			return nil, false
		}
	}
	b, err := json.Marshal(v)
	if err != nil {
		return nil, false
	}
	return b, true
}
type dynamicField struct {
	name string
	fn   func() interface{}
}
func Close() {
	dc.Close()
}
func Flush() {
	dc.Flush()
}
func SendNow(data interface{}) error {
	dc.ensureLogger()
	ev := NewEvent()
	if err := ev.Add(data); err != nil {
		return err
	}
	err := ev.Send()
	dc.logger.Printf("SendNow enqueued event, err=%v", err)
	return err
}
func Responses() chan Response {
	oneResp.Do(func() {
		if transitionResponses == nil {
			txResponses := dc.TxResponses()
			transitionResponses = make(chan Response, cap(txResponses))
			go func() {
				for txResp := range txResponses {
					resp := Response{}
					resp.Response = txResp
					transitionResponses <- resp
				}
				close(transitionResponses)
			}()
		}
	})
	return transitionResponses
}
func TxResponses() chan transmission.Response {
	return dc.TxResponses()
}
func AddDynamicField(name string, fn func() interface{}) error {
	return dc.AddDynamicField(name, fn)
}
func AddField(name string, val interface{}) {
	dc.AddField(name, val)
}
func Add(data interface{}) error {
	return dc.Add(data)
}
func NewEvent() *Event {
	return dc.NewEvent()
}
func NewBuilder() *Builder {
	return dc.NewBuilder()
}
func (f *fieldHolder) AddField(key string, val interface{}) {
	f.lock.Lock()
	defer f.lock.Unlock()
	f.data[key] = val
}
func (f *fieldHolder) Add(data interface{}) error {
	switch reflect.TypeOf(data).Kind() {
	case reflect.Struct:
		return f.addStruct(data)
	case reflect.Map:
		return f.addMap(data)
	case reflect.Ptr:
		return f.Add(reflect.ValueOf(data).Elem().Interface())
	}
	return fmt.Errorf(
		"Couldn't add type %s content %+v",
		reflect.TypeOf(data).Kind(), data,
	)
}
func (f *fieldHolder) addStruct(s interface{}) error {
	f.lock.Lock()
	defer f.lock.Unlock()
	sType := reflect.TypeOf(s)
	sVal := reflect.ValueOf(s)
	for i := 0; i < sType.NumField(); i++ {
		fieldInfo := sType.Field(i)
		if fieldInfo.PkgPath != "" {
			continue
		}
		var fName string
		fTag := fieldInfo.Tag.Get("json")
		if fTag != "" {
			if fTag == "-" {
				continue
			}
			if idx := strings.Index(fTag, ","); idx != -1 {
				options := fTag[idx:]
				fTag = fTag[:idx]
				if strings.Contains(options, "omitempty") && isEmptyValue(sVal.Field(i)) {
					continue
				}
			}
			fName = fTag
		} else {
			fName = fieldInfo.Name
		}
		f.data[fName] = sVal.Field(i).Interface()
	}
	return nil
}
func (f *fieldHolder) addMap(m interface{}) error {
	f.lock.Lock()
	defer f.lock.Unlock()
	mVal := reflect.ValueOf(m)
	mKeys := mVal.MapKeys()
	for _, key := range mKeys {
		var keyStr string
		switch key.Type().Kind() {
		case reflect.String:
			keyStr = key.String()
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
			reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
			reflect.Uint64, reflect.Float32, reflect.Float64, reflect.Complex64,
			reflect.Complex128:
			keyStr = fmt.Sprintf("%v", key.Interface())
		default:
			return fmt.Errorf("failed to add map: key type %s unaccepted", key.Type().Kind())
		}
		f.data[keyStr] = mVal.MapIndex(key).Interface()
	}
	return nil
}
func (f *fieldHolder) AddFunc(fn func() (string, interface{}, error)) error {
	for {
		key, rawVal, err := fn()
		if err != nil {
			break
		}
		f.AddField(key, rawVal)
	}
	return nil
}
func (f *fieldHolder) Fields() map[string]interface{} {
	return f.data
}
func (e *Event) AddField(key string, val interface{}) {
	e.sendLock.Lock()
	defer e.sendLock.Unlock()
	if e.sent == true {
		return
	}
	e.fieldHolder.AddField(key, val)
}
func (e *Event) Add(data interface{}) error {
	e.sendLock.Lock()
	defer e.sendLock.Unlock()
	if e.sent == true {
		return nil
	}
	return e.fieldHolder.Add(data)
}
func (e *Event) AddFunc(fn func() (string, interface{}, error)) error {
	e.sendLock.Lock()
	defer e.sendLock.Unlock()
	if e.sent == true {
		return nil
	}
	return e.fieldHolder.AddFunc(fn)
}
func (e *Event) Send() error {
	if e.client == nil {
		e.client = &Client{}
	}
	e.client.ensureLogger()
	if shouldDrop(e.SampleRate) {
		e.client.logger.Printf("dropping event due to sampling")
		sd.Increment("sampled")
		e.client.sendDroppedResponse(e, "event dropped due to sampling")
		return nil
	}
	return e.SendPresampled()
}
func (e *Event) SendPresampled() (err error) {
	if e.client == nil {
		e.client = &Client{}
	}
	e.client.ensureLogger()
	defer func() {
		if err != nil {
			e.client.logger.Printf("Failed to send event. err: %s, event: %+v", err, e)
		} else {
			e.client.logger.Printf("Send enqueued event: %+v", e)
		}
	}()
	e.sendLock.Lock()
	defer e.sendLock.Unlock()
	e.fieldHolder.lock.RLock()
	defer e.fieldHolder.lock.RUnlock()
	if len(e.data) == 0 {
		return errors.New("No metrics added to event. Won't send empty event.")
	}
	senderType := reflect.TypeOf(e.client.transmission).String()
	isHoneycombSender := strings.HasSuffix(senderType, "transmission.Honeycomb")
	isMockSender := strings.HasSuffix(senderType, "transmission.MockSender")
	if isHoneycombSender || isMockSender {
		if e.APIHost == "" {
			return errors.New("No APIHost for Honeycomb. Can't send to the Great Unknown.")
		}
		if e.WriteKey == "" {
			return errors.New("No WriteKey specified. Can't send event.")
		}
	}
	if e.Dataset == "" {
		return errors.New("No Dataset for Honeycomb. Can't send datasetless.")
	}
	e.sent = true
	e.client.ensureTransmission()
	txEvent := &transmission.Event{
		APIHost:    e.APIHost,
		APIKey:     e.WriteKey,
		Dataset:    e.Dataset,
		SampleRate: e.SampleRate,
		Timestamp:  e.Timestamp,
		Metadata:   e.Metadata,
		Data:       e.data,
	}
	e.client.transmission.Add(txEvent)
	return nil
}
func shouldDrop(rate uint) bool {
	if rate <= 1 {
		return false
	}
	return rand.Intn(int(rate)) != 0
}
func (b *Builder) AddDynamicField(name string, fn func() interface{}) error {
	b.dynFieldsLock.Lock()
	defer b.dynFieldsLock.Unlock()
	dynFn := dynamicField{
		name: name,
		fn:   fn,
	}
	b.dynFields = append(b.dynFields, dynFn)
	return nil
}
func (b *Builder) SendNow(data interface{}) error {
	ev := b.NewEvent()
	if err := ev.Add(data); err != nil {
		return err
	}
	err := ev.Send()
	return err
}
func (b *Builder) NewEvent() *Event {
	e := &Event{
		WriteKey:   b.WriteKey,
		Dataset:    b.Dataset,
		SampleRate: b.SampleRate,
		APIHost:    b.APIHost,
		Timestamp:  time.Now(),
		client:     b.client,
	}
	e.data = make(map[string]interface{})
	b.lock.RLock()
	defer b.lock.RUnlock()
	for k, v := range b.data {
		e.data[k] = v
	}
	b.dynFieldsLock.RLock()
	defer b.dynFieldsLock.RUnlock()
	for _, dynField := range b.dynFields {
		e.AddField(dynField.name, dynField.fn())
	}
	return e
}