func CaptureMessageAndWait(message string, tags map[string]string, interfaces ...Interface) string {
	return DefaultClient.CaptureMessageAndWait(message, tags, interfaces...)
}
CaptureMessageAndWait is identical to CaptureMessage except it blocks and waits for the message to be sent.
package raven
import (
	"bytes"
	"compress/zlib"
	"crypto/rand"
	"crypto/tls"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	mrand "math/rand"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"runtime"
	"strings"
	"sync"
	"time"
	"github.com/certifi/gocertifi"
	pkgErrors "github.com/pkg/errors"
)
const (
	userAgent       = "raven-go/1.0"
	timestampFormat = `"2006-01-02T15:04:05.00"`
)
var (
	ErrPacketDropped         = errors.New("raven: packet dropped")
	ErrUnableToUnmarshalJSON = errors.New("raven: unable to unmarshal JSON")
	ErrMissingUser           = errors.New("raven: dsn missing public key and/or password")
	ErrMissingPrivateKey     = errors.New("raven: dsn missing private key")
	ErrMissingProjectID      = errors.New("raven: dsn missing project id")
	ErrInvalidSampleRate     = errors.New("raven: sample rate should be between 0 and 1")
)
type Severity string
const (
	DEBUG   = Severity("debug")
	INFO    = Severity("info")
	WARNING = Severity("warning")
	ERROR   = Severity("error")
	FATAL   = Severity("fatal")
)
type Timestamp time.Time
func (t Timestamp) MarshalJSON() ([]byte, error) {
	return []byte(time.Time(t).UTC().Format(timestampFormat)), nil
}
func (timestamp *Timestamp) UnmarshalJSON(data []byte) error {
	t, err := time.Parse(timestampFormat, string(data))
	if err != nil {
		return err
	}
	*timestamp = Timestamp(t)
	return nil
}
type Interface interface {
	Class() string
}
type Culpriter interface {
	Culprit() string
}
type Transport interface {
	Send(url, authHeader string, packet *Packet) error
}
type outgoingPacket struct {
	packet *Packet
	ch     chan error
}
type Tag struct {
	Key   string
	Value string
}
type Tags []Tag
func (tag *Tag) MarshalJSON() ([]byte, error) {
	return json.Marshal([2]string{tag.Key, tag.Value})
}
func (t *Tag) UnmarshalJSON(data []byte) error {
	var tag [2]string
	if err := json.Unmarshal(data, &tag); err != nil {
		return err
	}
	*t = Tag{tag[0], tag[1]}
	return nil
}
func (t *Tags) UnmarshalJSON(data []byte) error {
	var tags []Tag
	switch data[0] {
	case '[':
		if err := json.Unmarshal(data, &tags); err != nil {
			return err
		}
	case '{':
		tagMap := make(map[string]string)
		if err := json.Unmarshal(data, &tagMap); err != nil {
			return err
		}
		for k, v := range tagMap {
			tags = append(tags, Tag{k, v})
		}
	default:
		return ErrUnableToUnmarshalJSON
	}
	*t = tags
	return nil
}
type Packet struct {
	Message string `json:"message"`
	EventID   string    `json:"event_id"`
	Project   string    `json:"project"`
	Timestamp Timestamp `json:"timestamp"`
	Level     Severity  `json:"level"`
	Logger    string    `json:"logger"`
	Platform    string                 `json:"platform,omitempty"`
	Culprit     string                 `json:"culprit,omitempty"`
	ServerName  string                 `json:"server_name,omitempty"`
	Release     string                 `json:"release,omitempty"`
	Environment string                 `json:"environment,omitempty"`
	Tags        Tags                   `json:"tags,omitempty"`
	Modules     map[string]string      `json:"modules,omitempty"`
	Fingerprint []string               `json:"fingerprint,omitempty"`
	Extra       map[string]interface{} `json:"extra,omitempty"`
	Interfaces []Interface `json:"-"`
}
func NewPacket(message string, interfaces ...Interface) *Packet {
	extra := map[string]interface{}{
		"runtime.Version":      runtime.Version(),
		"runtime.NumCPU":       runtime.NumCPU(),
		"runtime.GOMAXPROCS":   runtime.GOMAXPROCS(0),  
		"runtime.NumGoroutine": runtime.NumGoroutine(),
	}
	return &Packet{
		Message:    message,
		Interfaces: interfaces,
		Extra:      extra,
	}
}
func (packet *Packet) Init(project string) error {
	if packet.Project == "" {
		packet.Project = project
	}
	if packet.EventID == "" {
		var err error
		packet.EventID, err = uuid()
		if err != nil {
			return err
		}
	}
	if time.Time(packet.Timestamp).IsZero() {
		packet.Timestamp = Timestamp(time.Now())
	}
	if packet.Level == "" {
		packet.Level = ERROR
	}
	if packet.Logger == "" {
		packet.Logger = "root"
	}
	if packet.ServerName == "" {
		packet.ServerName = hostname
	}
	if packet.Platform == "" {
		packet.Platform = "go"
	}
	if packet.Culprit == "" {
		for _, inter := range packet.Interfaces {
			if c, ok := inter.(Culpriter); ok {
				packet.Culprit = c.Culprit()
				if packet.Culprit != "" {
					break
				}
			}
		}
	}
	return nil
}
func (packet *Packet) AddTags(tags map[string]string) {
	for k, v := range tags {
		packet.Tags = append(packet.Tags, Tag{k, v})
	}
}
func uuid() (string, error) {
	id := make([]byte, 16)
	_, err := io.ReadFull(rand.Reader, id)
	if err != nil {
		return "", err
	}
	id[6] &= 0x0F  
	id[6] |= 0x40  
	id[8] &= 0x3F  
	id[8] |= 0x80  
	return hex.EncodeToString(id), nil
}
func (packet *Packet) JSON() ([]byte, error) {
	packetJSON, err := json.Marshal(packet)
	if err != nil {
		return nil, err
	}
	interfaces := make(map[string]Interface, len(packet.Interfaces))
	for _, inter := range packet.Interfaces {
		if inter != nil {
			interfaces[inter.Class()] = inter
		}
	}
	if len(interfaces) > 0 {
		interfaceJSON, err := json.Marshal(interfaces)
		if err != nil {
			return nil, err
		}
		packetJSON[len(packetJSON)-1] = ','
		packetJSON = append(packetJSON, interfaceJSON[1:]...)
	}
	return packetJSON, nil
}
type context struct {
	user *User
	http *Http
	tags map[string]string
}
func (c *context) setUser(u *User) { c.user = u }
func (c *context) setHttp(h *Http) { c.http = h }
func (c *context) setTags(t map[string]string) {
	if c.tags == nil {
		c.tags = make(map[string]string)
	}
	for k, v := range t {
		c.tags[k] = v
	}
}
func (c *context) clear() {
	c.user = nil
	c.http = nil
	c.tags = nil
}
func (c *context) interfaces() []Interface {
	len, i := 0, 0
	if c.user != nil {
		len++
	}
	if c.http != nil {
		len++
	}
	interfaces := make([]Interface, len)
	if c.user != nil {
		interfaces[i] = c.user
		i++
	}
	if c.http != nil {
		interfaces[i] = c.http
		i++
	}
	return interfaces
}
var MaxQueueBuffer = 100
func newTransport() Transport {
	t := &HTTPTransport{}
	rootCAs, err := gocertifi.CACerts()
	if err != nil {
		log.Println("raven: failed to load root TLS certificates:", err)
	} else {
		t.Client = &http.Client{
			Transport: &http.Transport{
				Proxy:           http.ProxyFromEnvironment,
				TLSClientConfig: &tls.Config{RootCAs: rootCAs},
			},
		}
	}
	return t
}
func newClient(tags map[string]string) *Client {
	client := &Client{
		Transport:  newTransport(),
		Tags:       tags,
		context:    &context{},
		sampleRate: 1.0,
		queue:      make(chan *outgoingPacket, MaxQueueBuffer),
	}
	client.SetDSN(os.Getenv("SENTRY_DSN"))
	return client
}
func New(dsn string) (*Client, error) {
	client := newClient(nil)
	return client, client.SetDSN(dsn)
}
func NewWithTags(dsn string, tags map[string]string) (*Client, error) {
	client := newClient(tags)
	return client, client.SetDSN(dsn)
}
func NewClient(dsn string, tags map[string]string) (*Client, error) {
	client := newClient(tags)
	return client, client.SetDSN(dsn)
}
type Client struct {
	Tags map[string]string
	Transport Transport
	DropHandler func(*Packet)
	context *context
	mu          sync.RWMutex
	url         string
	projectID   string
	authHeader  string
	release     string
	environment string
	sampleRate  float32
	defaultLoggerName string
	includePaths       []string
	ignoreErrorsRegexp *regexp.Regexp
	queue              chan *outgoingPacket
	wg sync.WaitGroup
	start sync.Once
}
var DefaultClient = newClient(nil)
func (c *Client) SetIgnoreErrors(errs []string) error {
	joinedRegexp := strings.Join(errs, "|")
	r, err := regexp.Compile(joinedRegexp)
	if err != nil {
		return fmt.Errorf("failed to compile regexp %q for %q: %v", joinedRegexp, errs, err)
	}
	c.mu.Lock()
	c.ignoreErrorsRegexp = r
	c.mu.Unlock()
	return nil
}
func (c *Client) shouldExcludeErr(errStr string) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.ignoreErrorsRegexp != nil && c.ignoreErrorsRegexp.MatchString(errStr)
}
func SetIgnoreErrors(errs ...string) error {
	return DefaultClient.SetIgnoreErrors(errs)
}
func (client *Client) SetDSN(dsn string) error {
	if dsn == "" {
		return nil
	}
	client.mu.Lock()
	defer client.mu.Unlock()
	uri, err := url.Parse(dsn)
	if err != nil {
		return err
	}
	if uri.User == nil {
		return ErrMissingUser
	}
	publicKey := uri.User.Username()
	secretKey, ok := uri.User.Password()
	if !ok {
		return ErrMissingPrivateKey
	}
	uri.User = nil
	if idx := strings.LastIndex(uri.Path, "/"); idx != -1 {
		client.projectID = uri.Path[idx+1:]
		uri.Path = uri.Path[:idx+1] + "api/" + client.projectID + "/store/"
	}
	if client.projectID == "" {
		return ErrMissingProjectID
	}
	client.url = uri.String()
	client.authHeader = fmt.Sprintf("Sentry sentry_version=4, sentry_key=%s, sentry_secret=%s", publicKey, secretKey)
	return nil
}
func SetDSN(dsn string) error { return DefaultClient.SetDSN(dsn) }
func (client *Client) SetRelease(release string) {
	client.mu.Lock()
	defer client.mu.Unlock()
	client.release = release
}
func (client *Client) SetEnvironment(environment string) {
	client.mu.Lock()
	defer client.mu.Unlock()
	client.environment = environment
}
func (client *Client) SetDefaultLoggerName(name string) {
	client.mu.Lock()
	defer client.mu.Unlock()
	client.defaultLoggerName = name
}
func (client *Client) SetSampleRate(rate float32) error {
	client.mu.Lock()
	defer client.mu.Unlock()
	if rate < 0 || rate > 1 {
		return ErrInvalidSampleRate
	}
	client.sampleRate = rate
	return nil
}
func SetRelease(release string) { DefaultClient.SetRelease(release) }
func SetEnvironment(environment string) { DefaultClient.SetEnvironment(environment) }
func SetDefaultLoggerName(name string) {
	DefaultClient.SetDefaultLoggerName(name)
}
func SetSampleRate(rate float32) error { return DefaultClient.SetSampleRate(rate) }
func (client *Client) worker() {
	for outgoingPacket := range client.queue {
		client.mu.RLock()
		url, authHeader := client.url, client.authHeader
		client.mu.RUnlock()
		outgoingPacket.ch <- client.Transport.Send(url, authHeader, outgoingPacket.packet)
		client.wg.Done()
	}
}
func (client *Client) Capture(packet *Packet, captureTags map[string]string) (eventID string, ch chan error) {
	ch = make(chan error, 1)
	if client == nil {
		close(ch)
		return
	}
	if client.sampleRate < 1.0 && mrand.Float32() > client.sampleRate {
		return
	}
	if packet == nil {
		close(ch)
		return
	}
	if client.shouldExcludeErr(packet.Message) {
		return
	}
	client.wg.Add(1)
	packet.AddTags(captureTags)
	packet.AddTags(client.Tags)
	client.mu.RLock()
	packet.AddTags(client.context.tags)
	projectID := client.projectID
	release := client.release
	environment := client.environment
	defaultLoggerName := client.defaultLoggerName
	client.mu.RUnlock()
	if packet.Logger == "" && defaultLoggerName != "" {
		packet.Logger = defaultLoggerName
	}
	err := packet.Init(projectID)
	if err != nil {
		ch <- err
		client.wg.Done()
		return
	}
	if packet.Release == "" {
		packet.Release = release
	}
	if packet.Environment == "" {
		packet.Environment = environment
	}
	outgoingPacket := &outgoingPacket{packet, ch}
	client.start.Do(func() {
		go client.worker()
	})
	select {
	case client.queue <- outgoingPacket:
	default:
		if client.DropHandler != nil {
			client.DropHandler(packet)
		}
		ch <- ErrPacketDropped
		client.wg.Done()
	}
	return packet.EventID, ch
}
func Capture(packet *Packet, captureTags map[string]string) (eventID string, ch chan error) {
	return DefaultClient.Capture(packet, captureTags)
}
func (client *Client) CaptureMessage(message string, tags map[string]string, interfaces ...Interface) string {
	if client == nil {
		return ""
	}
	if client.shouldExcludeErr(message) {
		return ""
	}
	packet := NewPacket(message, append(append(interfaces, client.context.interfaces()...), &Message{message, nil})...)
	eventID, _ := client.Capture(packet, tags)
	return eventID
}
func CaptureMessage(message string, tags map[string]string, interfaces ...Interface) string {
	return DefaultClient.CaptureMessage(message, tags, interfaces...)
}
func (client *Client) CaptureMessageAndWait(message string, tags map[string]string, interfaces ...Interface) string {
	if client == nil {
		return ""
	}
	if client.shouldExcludeErr(message) {
		return ""
	}
	packet := NewPacket(message, append(append(interfaces, client.context.interfaces()...), &Message{message, nil})...)
	eventID, ch := client.Capture(packet, tags)
	if eventID != "" {
		<-ch
	}
	return eventID
}