client.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  1. // Package raven implements a client for the Sentry error logging service.
  2. package raven
  3. import (
  4. "bytes"
  5. "compress/zlib"
  6. "crypto/rand"
  7. "crypto/tls"
  8. "encoding/base64"
  9. "encoding/hex"
  10. "encoding/json"
  11. "errors"
  12. "fmt"
  13. "io"
  14. "io/ioutil"
  15. "log"
  16. mrand "math/rand"
  17. "net/http"
  18. "net/url"
  19. "os"
  20. "regexp"
  21. "runtime"
  22. "strings"
  23. "sync"
  24. "time"
  25. "github.com/certifi/gocertifi"
  26. pkgErrors "github.com/pkg/errors"
  27. )
  28. const (
  29. userAgent = "raven-go/1.0"
  30. timestampFormat = `"2006-01-02T15:04:05.00"`
  31. )
  32. var (
  33. ErrPacketDropped = errors.New("raven: packet dropped")
  34. ErrUnableToUnmarshalJSON = errors.New("raven: unable to unmarshal JSON")
  35. ErrMissingUser = errors.New("raven: dsn missing public key and/or password")
  36. ErrMissingProjectID = errors.New("raven: dsn missing project id")
  37. ErrInvalidSampleRate = errors.New("raven: sample rate should be between 0 and 1")
  38. )
  39. type Severity string
  40. // http://docs.python.org/2/howto/logging.html#logging-levels
  41. const (
  42. DEBUG = Severity("debug")
  43. INFO = Severity("info")
  44. WARNING = Severity("warning")
  45. ERROR = Severity("error")
  46. FATAL = Severity("fatal")
  47. )
  48. type Timestamp time.Time
  49. func (t Timestamp) MarshalJSON() ([]byte, error) {
  50. return []byte(time.Time(t).UTC().Format(timestampFormat)), nil
  51. }
  52. func (timestamp *Timestamp) UnmarshalJSON(data []byte) error {
  53. t, err := time.Parse(timestampFormat, string(data))
  54. if err != nil {
  55. return err
  56. }
  57. *timestamp = Timestamp(t)
  58. return nil
  59. }
  60. func (timestamp Timestamp) Format(format string) string {
  61. t := time.Time(timestamp)
  62. return t.Format(format)
  63. }
  64. // An Interface is a Sentry interface that will be serialized as JSON.
  65. // It must implement json.Marshaler or use json struct tags.
  66. type Interface interface {
  67. // The Sentry class name. Example: sentry.interfaces.Stacktrace
  68. Class() string
  69. }
  70. type Culpriter interface {
  71. Culprit() string
  72. }
  73. type Transport interface {
  74. Send(url, authHeader string, packet *Packet) error
  75. }
  76. type Extra map[string]interface{}
  77. type outgoingPacket struct {
  78. packet *Packet
  79. ch chan error
  80. }
  81. type Tag struct {
  82. Key string
  83. Value string
  84. }
  85. type Tags []Tag
  86. func (tag *Tag) MarshalJSON() ([]byte, error) {
  87. return json.Marshal([2]string{tag.Key, tag.Value})
  88. }
  89. func (t *Tag) UnmarshalJSON(data []byte) error {
  90. var tag [2]string
  91. if err := json.Unmarshal(data, &tag); err != nil {
  92. return err
  93. }
  94. *t = Tag{tag[0], tag[1]}
  95. return nil
  96. }
  97. func (t *Tags) UnmarshalJSON(data []byte) error {
  98. var tags []Tag
  99. switch data[0] {
  100. case '[':
  101. // Unmarshal into []Tag
  102. if err := json.Unmarshal(data, &tags); err != nil {
  103. return err
  104. }
  105. case '{':
  106. // Unmarshal into map[string]string
  107. tagMap := make(map[string]string)
  108. if err := json.Unmarshal(data, &tagMap); err != nil {
  109. return err
  110. }
  111. // Convert to []Tag
  112. for k, v := range tagMap {
  113. tags = append(tags, Tag{k, v})
  114. }
  115. default:
  116. return ErrUnableToUnmarshalJSON
  117. }
  118. *t = tags
  119. return nil
  120. }
  121. // https://docs.getsentry.com/hosted/clientdev/#building-the-json-packet
  122. type Packet struct {
  123. // Required
  124. Message string `json:"message"`
  125. // Required, set automatically by Client.Send/Report via Packet.Init if blank
  126. EventID string `json:"event_id"`
  127. Project string `json:"project"`
  128. Timestamp Timestamp `json:"timestamp"`
  129. Level Severity `json:"level"`
  130. Logger string `json:"logger"`
  131. // Optional
  132. Platform string `json:"platform,omitempty"`
  133. Culprit string `json:"culprit,omitempty"`
  134. ServerName string `json:"server_name,omitempty"`
  135. Release string `json:"release,omitempty"`
  136. Environment string `json:"environment,omitempty"`
  137. Tags Tags `json:"tags,omitempty"`
  138. Modules map[string]string `json:"modules,omitempty"`
  139. Fingerprint []string `json:"fingerprint,omitempty"`
  140. Extra Extra `json:"extra,omitempty"`
  141. Interfaces []Interface `json:"-"`
  142. }
  143. // NewPacket constructs a packet with the specified message and interfaces.
  144. func NewPacket(message string, interfaces ...Interface) *Packet {
  145. extra := Extra{}
  146. setExtraDefaults(extra)
  147. return &Packet{
  148. Message: message,
  149. Interfaces: interfaces,
  150. Extra: extra,
  151. }
  152. }
  153. // NewPacketWithExtra constructs a packet with the specified message, extra information, and interfaces.
  154. func NewPacketWithExtra(message string, extra Extra, interfaces ...Interface) *Packet {
  155. if extra == nil {
  156. extra = Extra{}
  157. }
  158. setExtraDefaults(extra)
  159. return &Packet{
  160. Message: message,
  161. Interfaces: interfaces,
  162. Extra: extra,
  163. }
  164. }
  165. func setExtraDefaults(extra Extra) Extra {
  166. extra["runtime.Version"] = runtime.Version()
  167. extra["runtime.NumCPU"] = runtime.NumCPU()
  168. extra["runtime.GOMAXPROCS"] = runtime.GOMAXPROCS(0) // 0 just returns the current value
  169. extra["runtime.NumGoroutine"] = runtime.NumGoroutine()
  170. return extra
  171. }
  172. // Init initializes required fields in a packet. It is typically called by
  173. // Client.Send/Report automatically.
  174. func (packet *Packet) Init(project string) error {
  175. if packet.Project == "" {
  176. packet.Project = project
  177. }
  178. if packet.EventID == "" {
  179. var err error
  180. packet.EventID, err = uuid()
  181. if err != nil {
  182. return err
  183. }
  184. }
  185. if time.Time(packet.Timestamp).IsZero() {
  186. packet.Timestamp = Timestamp(time.Now())
  187. }
  188. if packet.Level == "" {
  189. packet.Level = ERROR
  190. }
  191. if packet.Logger == "" {
  192. packet.Logger = "root"
  193. }
  194. if packet.ServerName == "" {
  195. packet.ServerName = hostname
  196. }
  197. if packet.Platform == "" {
  198. packet.Platform = "go"
  199. }
  200. if packet.Culprit == "" {
  201. for _, inter := range packet.Interfaces {
  202. if c, ok := inter.(Culpriter); ok {
  203. packet.Culprit = c.Culprit()
  204. if packet.Culprit != "" {
  205. break
  206. }
  207. }
  208. }
  209. }
  210. return nil
  211. }
  212. func (packet *Packet) AddTags(tags map[string]string) {
  213. for k, v := range tags {
  214. packet.Tags = append(packet.Tags, Tag{k, v})
  215. }
  216. }
  217. func uuid() (string, error) {
  218. id := make([]byte, 16)
  219. _, err := io.ReadFull(rand.Reader, id)
  220. if err != nil {
  221. return "", err
  222. }
  223. id[6] &= 0x0F // clear version
  224. id[6] |= 0x40 // set version to 4 (random uuid)
  225. id[8] &= 0x3F // clear variant
  226. id[8] |= 0x80 // set to IETF variant
  227. return hex.EncodeToString(id), nil
  228. }
  229. func (packet *Packet) JSON() ([]byte, error) {
  230. packetJSON, err := json.Marshal(packet)
  231. if err != nil {
  232. return nil, err
  233. }
  234. interfaces := make(map[string]Interface, len(packet.Interfaces))
  235. for _, inter := range packet.Interfaces {
  236. if inter != nil {
  237. interfaces[inter.Class()] = inter
  238. }
  239. }
  240. if len(interfaces) > 0 {
  241. interfaceJSON, err := json.Marshal(interfaces)
  242. if err != nil {
  243. return nil, err
  244. }
  245. packetJSON[len(packetJSON)-1] = ','
  246. packetJSON = append(packetJSON, interfaceJSON[1:]...)
  247. }
  248. return packetJSON, nil
  249. }
  250. type context struct {
  251. user *User
  252. http *Http
  253. tags map[string]string
  254. }
  255. func (c *context) setUser(u *User) { c.user = u }
  256. func (c *context) setHttp(h *Http) { c.http = h }
  257. func (c *context) setTags(t map[string]string) {
  258. if c.tags == nil {
  259. c.tags = make(map[string]string)
  260. }
  261. for k, v := range t {
  262. c.tags[k] = v
  263. }
  264. }
  265. func (c *context) clear() {
  266. c.user = nil
  267. c.http = nil
  268. c.tags = nil
  269. }
  270. // Return a list of interfaces to be used in appending with the rest
  271. func (c *context) interfaces() []Interface {
  272. len, i := 0, 0
  273. if c.user != nil {
  274. len++
  275. }
  276. if c.http != nil {
  277. len++
  278. }
  279. interfaces := make([]Interface, len)
  280. if c.user != nil {
  281. interfaces[i] = c.user
  282. i++
  283. }
  284. if c.http != nil {
  285. interfaces[i] = c.http
  286. i++
  287. }
  288. return interfaces
  289. }
  290. // The maximum number of packets that will be buffered waiting to be delivered.
  291. // Packets will be dropped if the buffer is full. Used by NewClient.
  292. var MaxQueueBuffer = 100
  293. func newTransport() Transport {
  294. t := &HTTPTransport{}
  295. rootCAs, err := gocertifi.CACerts()
  296. if err != nil {
  297. log.Println("raven: failed to load root TLS certificates:", err)
  298. } else {
  299. t.Client = &http.Client{
  300. Transport: &http.Transport{
  301. Proxy: http.ProxyFromEnvironment,
  302. TLSClientConfig: &tls.Config{RootCAs: rootCAs},
  303. },
  304. }
  305. }
  306. return t
  307. }
  308. func newClient(tags map[string]string) *Client {
  309. client := &Client{
  310. Transport: newTransport(),
  311. Tags: tags,
  312. context: &context{},
  313. sampleRate: 1.0,
  314. queue: make(chan *outgoingPacket, MaxQueueBuffer),
  315. }
  316. client.SetDSN(os.Getenv("SENTRY_DSN"))
  317. client.SetRelease(os.Getenv("SENTRY_RELEASE"))
  318. client.SetEnvironment(os.Getenv("SENTRY_ENVIRONMENT"))
  319. return client
  320. }
  321. // New constructs a new Sentry client instance
  322. func New(dsn string) (*Client, error) {
  323. client := newClient(nil)
  324. return client, client.SetDSN(dsn)
  325. }
  326. // NewWithTags constructs a new Sentry client instance with default tags.
  327. func NewWithTags(dsn string, tags map[string]string) (*Client, error) {
  328. client := newClient(tags)
  329. return client, client.SetDSN(dsn)
  330. }
  331. // NewClient constructs a Sentry client and spawns a background goroutine to
  332. // handle packets sent by Client.Report.
  333. //
  334. // Deprecated: use New and NewWithTags instead
  335. func NewClient(dsn string, tags map[string]string) (*Client, error) {
  336. client := newClient(tags)
  337. return client, client.SetDSN(dsn)
  338. }
  339. // Client encapsulates a connection to a Sentry server. It must be initialized
  340. // by calling NewClient. Modification of fields concurrently with Send or after
  341. // calling Report for the first time is not thread-safe.
  342. type Client struct {
  343. Tags map[string]string
  344. Transport Transport
  345. // DropHandler is called when a packet is dropped because the buffer is full.
  346. DropHandler func(*Packet)
  347. // Context that will get appending to all packets
  348. context *context
  349. mu sync.RWMutex
  350. url string
  351. projectID string
  352. authHeader string
  353. release string
  354. environment string
  355. sampleRate float32
  356. // default logger name (leave empty for 'root')
  357. defaultLoggerName string
  358. includePaths []string
  359. ignoreErrorsRegexp *regexp.Regexp
  360. queue chan *outgoingPacket
  361. // A WaitGroup to keep track of all currently in-progress captures
  362. // This is intended to be used with Client.Wait() to assure that
  363. // all messages have been transported before exiting the process.
  364. wg sync.WaitGroup
  365. // A Once to track only starting up the background worker once
  366. start sync.Once
  367. }
  368. // Initialize a default *Client instance
  369. var DefaultClient = newClient(nil)
  370. func (c *Client) SetIgnoreErrors(errs []string) error {
  371. joinedRegexp := strings.Join(errs, "|")
  372. r, err := regexp.Compile(joinedRegexp)
  373. if err != nil {
  374. return fmt.Errorf("failed to compile regexp %q for %q: %v", joinedRegexp, errs, err)
  375. }
  376. c.mu.Lock()
  377. c.ignoreErrorsRegexp = r
  378. c.mu.Unlock()
  379. return nil
  380. }
  381. func (c *Client) shouldExcludeErr(errStr string) bool {
  382. c.mu.RLock()
  383. defer c.mu.RUnlock()
  384. return c.ignoreErrorsRegexp != nil && c.ignoreErrorsRegexp.MatchString(errStr)
  385. }
  386. func SetIgnoreErrors(errs ...string) error {
  387. return DefaultClient.SetIgnoreErrors(errs)
  388. }
  389. // SetDSN updates a client with a new DSN. It safe to call after and
  390. // concurrently with calls to Report and Send.
  391. func (client *Client) SetDSN(dsn string) error {
  392. if dsn == "" {
  393. return nil
  394. }
  395. client.mu.Lock()
  396. defer client.mu.Unlock()
  397. uri, err := url.Parse(dsn)
  398. if err != nil {
  399. return err
  400. }
  401. if uri.User == nil {
  402. return ErrMissingUser
  403. }
  404. publicKey := uri.User.Username()
  405. secretKey, hasSecretKey := uri.User.Password()
  406. uri.User = nil
  407. if idx := strings.LastIndex(uri.Path, "/"); idx != -1 {
  408. client.projectID = uri.Path[idx+1:]
  409. uri.Path = uri.Path[:idx+1] + "api/" + client.projectID + "/store/"
  410. }
  411. if client.projectID == "" {
  412. return ErrMissingProjectID
  413. }
  414. client.url = uri.String()
  415. if hasSecretKey {
  416. client.authHeader = fmt.Sprintf("Sentry sentry_version=4, sentry_key=%s, sentry_secret=%s", publicKey, secretKey)
  417. } else {
  418. client.authHeader = fmt.Sprintf("Sentry sentry_version=4, sentry_key=%s", publicKey)
  419. }
  420. return nil
  421. }
  422. // Sets the DSN for the default *Client instance
  423. func SetDSN(dsn string) error { return DefaultClient.SetDSN(dsn) }
  424. // SetRelease sets the "release" tag.
  425. func (client *Client) SetRelease(release string) {
  426. client.mu.Lock()
  427. defer client.mu.Unlock()
  428. client.release = release
  429. }
  430. // SetEnvironment sets the "environment" tag.
  431. func (client *Client) SetEnvironment(environment string) {
  432. client.mu.Lock()
  433. defer client.mu.Unlock()
  434. client.environment = environment
  435. }
  436. // SetDefaultLoggerName sets the default logger name.
  437. func (client *Client) SetDefaultLoggerName(name string) {
  438. client.mu.Lock()
  439. defer client.mu.Unlock()
  440. client.defaultLoggerName = name
  441. }
  442. // SetSampleRate sets how much sampling we want on client side
  443. func (client *Client) SetSampleRate(rate float32) error {
  444. client.mu.Lock()
  445. defer client.mu.Unlock()
  446. if rate < 0 || rate > 1 {
  447. return ErrInvalidSampleRate
  448. }
  449. client.sampleRate = rate
  450. return nil
  451. }
  452. // SetRelease sets the "release" tag on the default *Client
  453. func SetRelease(release string) { DefaultClient.SetRelease(release) }
  454. // SetEnvironment sets the "environment" tag on the default *Client
  455. func SetEnvironment(environment string) { DefaultClient.SetEnvironment(environment) }
  456. // SetDefaultLoggerName sets the "defaultLoggerName" on the default *Client
  457. func SetDefaultLoggerName(name string) {
  458. DefaultClient.SetDefaultLoggerName(name)
  459. }
  460. // SetSampleRate sets the "sample rate" on the degault *Client
  461. func SetSampleRate(rate float32) error { return DefaultClient.SetSampleRate(rate) }
  462. func (client *Client) worker() {
  463. for outgoingPacket := range client.queue {
  464. client.mu.RLock()
  465. url, authHeader := client.url, client.authHeader
  466. client.mu.RUnlock()
  467. outgoingPacket.ch <- client.Transport.Send(url, authHeader, outgoingPacket.packet)
  468. client.wg.Done()
  469. }
  470. }
  471. // Capture asynchronously delivers a packet to the Sentry server. It is a no-op
  472. // when client is nil. A channel is provided if it is important to check for a
  473. // send's success.
  474. func (client *Client) Capture(packet *Packet, captureTags map[string]string) (eventID string, ch chan error) {
  475. ch = make(chan error, 1)
  476. if client == nil {
  477. // return a chan that always returns nil when the caller receives from it
  478. close(ch)
  479. return
  480. }
  481. if client.sampleRate < 1.0 && mrand.Float32() > client.sampleRate {
  482. return
  483. }
  484. if packet == nil {
  485. close(ch)
  486. return
  487. }
  488. if client.shouldExcludeErr(packet.Message) {
  489. return
  490. }
  491. // Keep track of all running Captures so that we can wait for them all to finish
  492. // *Must* call client.wg.Done() on any path that indicates that an event was
  493. // finished being acted upon, whether success or failure
  494. client.wg.Add(1)
  495. // Merge capture tags and client tags
  496. packet.AddTags(captureTags)
  497. packet.AddTags(client.Tags)
  498. // Initialize any required packet fields
  499. client.mu.RLock()
  500. packet.AddTags(client.context.tags)
  501. projectID := client.projectID
  502. release := client.release
  503. environment := client.environment
  504. defaultLoggerName := client.defaultLoggerName
  505. client.mu.RUnlock()
  506. // set the global logger name on the packet if we must
  507. if packet.Logger == "" && defaultLoggerName != "" {
  508. packet.Logger = defaultLoggerName
  509. }
  510. err := packet.Init(projectID)
  511. if err != nil {
  512. ch <- err
  513. client.wg.Done()
  514. return
  515. }
  516. if packet.Release == "" {
  517. packet.Release = release
  518. }
  519. if packet.Environment == "" {
  520. packet.Environment = environment
  521. }
  522. outgoingPacket := &outgoingPacket{packet, ch}
  523. // Lazily start background worker until we
  524. // do our first write into the queue.
  525. client.start.Do(func() {
  526. go client.worker()
  527. })
  528. select {
  529. case client.queue <- outgoingPacket:
  530. default:
  531. // Send would block, drop the packet
  532. if client.DropHandler != nil {
  533. client.DropHandler(packet)
  534. }
  535. ch <- ErrPacketDropped
  536. client.wg.Done()
  537. }
  538. return packet.EventID, ch
  539. }
  540. // Capture asynchronously delivers a packet to the Sentry server with the default *Client.
  541. // It is a no-op when client is nil. A channel is provided if it is important to check for a
  542. // send's success.
  543. func Capture(packet *Packet, captureTags map[string]string) (eventID string, ch chan error) {
  544. return DefaultClient.Capture(packet, captureTags)
  545. }
  546. // CaptureMessage formats and delivers a string message to the Sentry server.
  547. func (client *Client) CaptureMessage(message string, tags map[string]string, interfaces ...Interface) string {
  548. if client == nil {
  549. return ""
  550. }
  551. if client.shouldExcludeErr(message) {
  552. return ""
  553. }
  554. packet := NewPacket(message, append(append(interfaces, client.context.interfaces()...), &Message{message, nil})...)
  555. eventID, _ := client.Capture(packet, tags)
  556. return eventID
  557. }
  558. // CaptureMessage formats and delivers a string message to the Sentry server with the default *Client
  559. func CaptureMessage(message string, tags map[string]string, interfaces ...Interface) string {
  560. return DefaultClient.CaptureMessage(message, tags, interfaces...)
  561. }
  562. // CaptureMessageAndWait is identical to CaptureMessage except it blocks and waits for the message to be sent.
  563. func (client *Client) CaptureMessageAndWait(message string, tags map[string]string, interfaces ...Interface) string {
  564. if client == nil {
  565. return ""
  566. }
  567. if client.shouldExcludeErr(message) {
  568. return ""
  569. }
  570. packet := NewPacket(message, append(append(interfaces, client.context.interfaces()...), &Message{message, nil})...)
  571. eventID, ch := client.Capture(packet, tags)
  572. if eventID != "" {
  573. <-ch
  574. }
  575. return eventID
  576. }
  577. // CaptureMessageAndWait is identical to CaptureMessage except it blocks and waits for the message to be sent.
  578. func CaptureMessageAndWait(message string, tags map[string]string, interfaces ...Interface) string {
  579. return DefaultClient.CaptureMessageAndWait(message, tags, interfaces...)
  580. }
  581. // CaptureErrors formats and delivers an error to the Sentry server.
  582. // Adds a stacktrace to the packet, excluding the call to this method.
  583. func (client *Client) CaptureError(err error, tags map[string]string, interfaces ...Interface) string {
  584. if client == nil {
  585. return ""
  586. }
  587. if err == nil {
  588. return ""
  589. }
  590. if client.shouldExcludeErr(err.Error()) {
  591. return ""
  592. }
  593. extra := extractExtra(err)
  594. cause := pkgErrors.Cause(err)
  595. packet := NewPacketWithExtra(err.Error(), extra, append(append(interfaces, client.context.interfaces()...), NewException(cause, GetOrNewStacktrace(cause, 1, 3, client.includePaths)))...)
  596. eventID, _ := client.Capture(packet, tags)
  597. return eventID
  598. }
  599. // CaptureErrors formats and delivers an error to the Sentry server using the default *Client.
  600. // Adds a stacktrace to the packet, excluding the call to this method.
  601. func CaptureError(err error, tags map[string]string, interfaces ...Interface) string {
  602. return DefaultClient.CaptureError(err, tags, interfaces...)
  603. }
  604. // CaptureErrorAndWait is identical to CaptureError, except it blocks and assures that the event was sent
  605. func (client *Client) CaptureErrorAndWait(err error, tags map[string]string, interfaces ...Interface) string {
  606. if client == nil {
  607. return ""
  608. }
  609. if client.shouldExcludeErr(err.Error()) {
  610. return ""
  611. }
  612. extra := extractExtra(err)
  613. cause := pkgErrors.Cause(err)
  614. packet := NewPacketWithExtra(err.Error(), extra, append(append(interfaces, client.context.interfaces()...), NewException(cause, GetOrNewStacktrace(cause, 1, 3, client.includePaths)))...)
  615. eventID, ch := client.Capture(packet, tags)
  616. if eventID != "" {
  617. <-ch
  618. }
  619. return eventID
  620. }
  621. // CaptureErrorAndWait is identical to CaptureError, except it blocks and assures that the event was sent
  622. func CaptureErrorAndWait(err error, tags map[string]string, interfaces ...Interface) string {
  623. return DefaultClient.CaptureErrorAndWait(err, tags, interfaces...)
  624. }
  625. // CapturePanic calls f and then recovers and reports a panic to the Sentry server if it occurs.
  626. // If an error is captured, both the error and the reported Sentry error ID are returned.
  627. func (client *Client) CapturePanic(f func(), tags map[string]string, interfaces ...Interface) (err interface{}, errorID string) {
  628. // Note: This doesn't need to check for client, because we still want to go through the defer/recover path
  629. // Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the
  630. // *Packet just to be thrown away, this should not be the normal case. Could be refactored to
  631. // be completely noop though if we cared.
  632. defer func() {
  633. var packet *Packet
  634. err = recover()
  635. switch rval := err.(type) {
  636. case nil:
  637. return
  638. case error:
  639. if client.shouldExcludeErr(rval.Error()) {
  640. return
  641. }
  642. packet = NewPacket(rval.Error(), append(append(interfaces, client.context.interfaces()...), NewException(rval, NewStacktrace(2, 3, client.includePaths)))...)
  643. default:
  644. rvalStr := fmt.Sprint(rval)
  645. if client.shouldExcludeErr(rvalStr) {
  646. return
  647. }
  648. packet = NewPacket(rvalStr, append(append(interfaces, client.context.interfaces()...), NewException(errors.New(rvalStr), NewStacktrace(2, 3, client.includePaths)))...)
  649. }
  650. errorID, _ = client.Capture(packet, tags)
  651. }()
  652. f()
  653. return
  654. }
  655. // CapturePanic calls f and then recovers and reports a panic to the Sentry server if it occurs.
  656. // If an error is captured, both the error and the reported Sentry error ID are returned.
  657. func CapturePanic(f func(), tags map[string]string, interfaces ...Interface) (interface{}, string) {
  658. return DefaultClient.CapturePanic(f, tags, interfaces...)
  659. }
  660. // CapturePanicAndWait is identical to CaptureError, except it blocks and assures that the event was sent
  661. func (client *Client) CapturePanicAndWait(f func(), tags map[string]string, interfaces ...Interface) (err interface{}, errorID string) {
  662. // Note: This doesn't need to check for client, because we still want to go through the defer/recover path
  663. // Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the
  664. // *Packet just to be thrown away, this should not be the normal case. Could be refactored to
  665. // be completely noop though if we cared.
  666. defer func() {
  667. var packet *Packet
  668. err = recover()
  669. switch rval := err.(type) {
  670. case nil:
  671. return
  672. case error:
  673. if client.shouldExcludeErr(rval.Error()) {
  674. return
  675. }
  676. packet = NewPacket(rval.Error(), append(append(interfaces, client.context.interfaces()...), NewException(rval, NewStacktrace(2, 3, client.includePaths)))...)
  677. default:
  678. rvalStr := fmt.Sprint(rval)
  679. if client.shouldExcludeErr(rvalStr) {
  680. return
  681. }
  682. packet = NewPacket(rvalStr, append(append(interfaces, client.context.interfaces()...), NewException(errors.New(rvalStr), NewStacktrace(2, 3, client.includePaths)))...)
  683. }
  684. var ch chan error
  685. errorID, ch = client.Capture(packet, tags)
  686. if errorID != "" {
  687. <-ch
  688. }
  689. }()
  690. f()
  691. return
  692. }
  693. // CapturePanicAndWait is identical to CaptureError, except it blocks and assures that the event was sent
  694. func CapturePanicAndWait(f func(), tags map[string]string, interfaces ...Interface) (interface{}, string) {
  695. return DefaultClient.CapturePanicAndWait(f, tags, interfaces...)
  696. }
  697. func (client *Client) Close() {
  698. close(client.queue)
  699. }
  700. func Close() { DefaultClient.Close() }
  701. // Wait blocks and waits for all events to finish being sent to Sentry server
  702. func (client *Client) Wait() {
  703. client.wg.Wait()
  704. }
  705. // Wait blocks and waits for all events to finish being sent to Sentry server
  706. func Wait() { DefaultClient.Wait() }
  707. func (client *Client) URL() string {
  708. client.mu.RLock()
  709. defer client.mu.RUnlock()
  710. return client.url
  711. }
  712. func URL() string { return DefaultClient.URL() }
  713. func (client *Client) ProjectID() string {
  714. client.mu.RLock()
  715. defer client.mu.RUnlock()
  716. return client.projectID
  717. }
  718. func ProjectID() string { return DefaultClient.ProjectID() }
  719. func (client *Client) Release() string {
  720. client.mu.RLock()
  721. defer client.mu.RUnlock()
  722. return client.release
  723. }
  724. func Release() string { return DefaultClient.Release() }
  725. func IncludePaths() []string { return DefaultClient.IncludePaths() }
  726. func (client *Client) IncludePaths() []string {
  727. client.mu.RLock()
  728. defer client.mu.RUnlock()
  729. return client.includePaths
  730. }
  731. func SetIncludePaths(p []string) { DefaultClient.SetIncludePaths(p) }
  732. func (client *Client) SetIncludePaths(p []string) {
  733. client.mu.Lock()
  734. defer client.mu.Unlock()
  735. client.includePaths = p
  736. }
  737. func (c *Client) SetUserContext(u *User) {
  738. c.mu.Lock()
  739. defer c.mu.Unlock()
  740. c.context.setUser(u)
  741. }
  742. func (c *Client) SetHttpContext(h *Http) {
  743. c.mu.Lock()
  744. defer c.mu.Unlock()
  745. c.context.setHttp(h)
  746. }
  747. func (c *Client) SetTagsContext(t map[string]string) {
  748. c.mu.Lock()
  749. defer c.mu.Unlock()
  750. c.context.setTags(t)
  751. }
  752. func (c *Client) ClearContext() {
  753. c.mu.Lock()
  754. defer c.mu.Unlock()
  755. c.context.clear()
  756. }
  757. func SetUserContext(u *User) { DefaultClient.SetUserContext(u) }
  758. func SetHttpContext(h *Http) { DefaultClient.SetHttpContext(h) }
  759. func SetTagsContext(t map[string]string) { DefaultClient.SetTagsContext(t) }
  760. func ClearContext() { DefaultClient.ClearContext() }
  761. // HTTPTransport is the default transport, delivering packets to Sentry via the
  762. // HTTP API.
  763. type HTTPTransport struct {
  764. *http.Client
  765. }
  766. func (t *HTTPTransport) Send(url, authHeader string, packet *Packet) error {
  767. if url == "" {
  768. return nil
  769. }
  770. body, contentType, err := serializedPacket(packet)
  771. if err != nil {
  772. return fmt.Errorf("error serializing packet: %v", err)
  773. }
  774. req, err := http.NewRequest("POST", url, body)
  775. if err != nil {
  776. return fmt.Errorf("can't create new request: %v", err)
  777. }
  778. req.Header.Set("X-Sentry-Auth", authHeader)
  779. req.Header.Set("User-Agent", userAgent)
  780. req.Header.Set("Content-Type", contentType)
  781. res, err := t.Do(req)
  782. if err != nil {
  783. return err
  784. }
  785. io.Copy(ioutil.Discard, res.Body)
  786. res.Body.Close()
  787. if res.StatusCode != 200 {
  788. return fmt.Errorf("raven: got http status %d - x-sentry-error: %s", res.StatusCode, res.Header.Get("X-Sentry-Error"))
  789. }
  790. return nil
  791. }
  792. func serializedPacket(packet *Packet) (io.Reader, string, error) {
  793. packetJSON, err := packet.JSON()
  794. if err != nil {
  795. return nil, "", fmt.Errorf("error marshaling packet %+v to JSON: %v", packet, err)
  796. }
  797. // Only deflate/base64 the packet if it is bigger than 1KB, as there is
  798. // overhead.
  799. if len(packetJSON) > 1000 {
  800. buf := &bytes.Buffer{}
  801. b64 := base64.NewEncoder(base64.StdEncoding, buf)
  802. deflate, _ := zlib.NewWriterLevel(b64, zlib.BestCompression)
  803. deflate.Write(packetJSON)
  804. deflate.Close()
  805. b64.Close()
  806. return buf, "application/octet-stream", nil
  807. }
  808. return bytes.NewReader(packetJSON), "application/json", nil
  809. }
  810. var hostname string
  811. func init() {
  812. hostname, _ = os.Hostname()
  813. }