func (e *Exporter) send(buf []byte) error {
	for len(buf) != 0 {
		n, err := e.writer.Write(buf)
		if err != nil {
			return err
		}
		buf = buf[n:]
	}
	return nil
}
send writes a complete buffer to the writer as a blocking call.
package statsd
import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net"
	"net/url"
	"strconv"
	"go.opentelemetry.io/otel/metric"
	"go.opentelemetry.io/otel/metric/number"
	"go.opentelemetry.io/otel/metric/unit"
	export "go.opentelemetry.io/otel/sdk/export/metric"
	"go.opentelemetry.io/otel/sdk/export/metric/aggregation"
	"go.opentelemetry.io/otel/sdk/instrumentation"
	"go.opentelemetry.io/otel/sdk/resource"
)
type (
	Config struct {
		URL string
		Writer io.Writer
		MaxPacketSize int
		Prefix string
	}
	Exporter struct {
		adapter Adapter
		config  Config
		conn    net.Conn
		writer  io.Writer
		buffer  bytes.Buffer
	}
	Adapter interface {
		AppendName(export.Record, *bytes.Buffer)
		AppendTags(export.Record, *resource.Resource, *bytes.Buffer)
	}
)
const (
	formatCounter   = "c"
	formatHistogram = "h"
	formatGauge     = "g"
	formatTiming    = "ms"
	MaxPacketSize = 1432
)
var (
	_ export.Exporter = &Exporter{}
	ErrInvalidScheme = fmt.Errorf("invalid statsd transport")
)
func NewExporter(config Config, adapter Adapter) (*Exporter, error) {
	if config.MaxPacketSize <= 0 {
		config.MaxPacketSize = MaxPacketSize
	}
	var writer io.Writer
	var conn net.Conn
	var err error
	if config.Writer != nil {
		writer = config.Writer
	} else {
		conn, err = dial(config.URL)
		if conn != nil {
			writer = conn
		}
	}
	return &Exporter{
		adapter: adapter,
		config:  config,
		conn:    conn,
		writer:  writer,
	}, err
}
func dial(endpoint string) (net.Conn, error) {
	dest, err := url.Parse(endpoint)
	if err != nil {
		return nil, err
	}
	scheme := dest.Scheme
	switch scheme {
	case "udp", "udp4", "udp6":
		udpAddr, err := net.ResolveUDPAddr(scheme, dest.Host)
		locAddr := &net.UDPAddr{}
		if err != nil {
			return nil, err
		}
		conn, err := net.DialUDP(scheme, locAddr, udpAddr)
		if err != nil {
			return nil, err
		}
		return conn, err
	case "unix", "unixgram":
		scheme = "unixgram"
		locAddr := &net.UnixAddr{}
		sockAddr, err := net.ResolveUnixAddr(scheme, dest.Path)
		if err != nil {
			return nil, err
		}
		conn, err := net.DialUnix(scheme, locAddr, sockAddr)
		if err != nil {
			return nil, err
		}
		return conn, err
	}
	return nil, ErrInvalidScheme
}
func (e *Exporter) ExportKindFor(*metric.Descriptor, aggregation.Kind) export.ExportKind {
	return export.DeltaExportKind
}
func (e *Exporter) Export(_ context.Context, res *resource.Resource, ilReader export.InstrumentationLibraryReader) error {
	buf := &e.buffer
	buf.Reset()
	var aggErr error
	var sendErr error
	aggErr = ilReader.ForEach(func(_ instrumentation.Library, reader export.Reader) error {
		return reader.ForEach(e, func(rec export.Record) error {
			pts, err := e.countPoints(rec)
			if err != nil {
				return err
			}
			for pt := 0; pt < pts; pt++ {
				before := buf.Len()
				if err := e.formatMetric(rec, res, pt, buf); err != nil {
					return err
				}
				if buf.Len() < e.config.MaxPacketSize {
					continue
				}
				if before == 0 {
					if err := e.send(buf.Bytes()); err != nil && sendErr == nil {
						sendErr = err
					}
					buf.Reset()
					continue
				}
				if err := e.send(buf.Bytes()[:before]); err != nil && sendErr == nil {
					sendErr = err
				}
				leftover := buf.Len() - before
				copy(buf.Bytes()[0:leftover], buf.Bytes()[before:])
				buf.Truncate(leftover)
			}
			return nil
		})
	})
	if err := e.send(buf.Bytes()); err != nil && sendErr == nil {
		sendErr = err
	}
	if sendErr != nil {
		return sendErr
	}
	return aggErr
}