PageRenderTime 162ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/github.com/getsentry/raven-go/client.go

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