TUN-7551: Complete removal of raven-go to sentry-go
Removes the final usage of raven-go and removes the dependency.
This commit is contained in:
		
							parent
							
								
									6c0dd59701
								
							
						
					
					
						commit
						a1419a73a5
					
				
							
								
								
									
										4
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										4
									
								
								go.mod
								
								
								
								
							|  | @ -10,9 +10,9 @@ require ( | |||
| 	github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf | ||||
| 	github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434 | ||||
| 	github.com/fsnotify/fsnotify v1.4.9 | ||||
| 	github.com/getsentry/raven-go v0.2.0 | ||||
| 	github.com/getsentry/sentry-go v0.16.0 | ||||
| 	github.com/go-chi/chi/v5 v5.0.8 | ||||
| 	github.com/go-chi/cors v1.2.1 | ||||
| 	github.com/go-jose/go-jose/v3 v3.0.0 | ||||
| 	github.com/gobwas/ws v1.0.4 | ||||
| 	github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 | ||||
|  | @ -55,7 +55,6 @@ require ( | |||
| 	github.com/BurntSushi/toml v1.2.0 // indirect | ||||
| 	github.com/apparentlymart/go-cidr v1.1.0 // indirect | ||||
| 	github.com/beorn7/perks v1.0.1 // indirect | ||||
| 	github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect | ||||
| 	github.com/cespare/xxhash/v2 v2.1.2 // indirect | ||||
| 	github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47 // indirect | ||||
| 	github.com/coredns/caddy v1.1.1 // indirect | ||||
|  | @ -66,7 +65,6 @@ require ( | |||
| 	github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect | ||||
| 	github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect | ||||
| 	github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect | ||||
| 	github.com/go-chi/cors v1.2.1 // indirect | ||||
| 	github.com/go-logr/logr v1.2.3 // indirect | ||||
| 	github.com/go-logr/stdr v1.2.2 // indirect | ||||
| 	github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect | ||||
|  |  | |||
							
								
								
									
										4
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										4
									
								
								go.sum
								
								
								
								
							|  | @ -74,8 +74,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r | |||
| github.com/bwesterb/go-ristretto v1.2.2/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= | ||||
| github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= | ||||
| github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | ||||
| github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s= | ||||
| github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= | ||||
| github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= | ||||
| github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||||
| github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= | ||||
|  | @ -145,8 +143,6 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjr | |||
| github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= | ||||
| github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= | ||||
| github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= | ||||
| github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= | ||||
| github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= | ||||
| github.com/getsentry/sentry-go v0.16.0 h1:owk+S+5XcgJLlGR/3+3s6N4d+uKwqYvh/eS0AIMjPWo= | ||||
| github.com/getsentry/sentry-go v0.16.0/go.mod h1:ZXCloQLj0pG7mja5NK6NPf2V4A88YJ4pNlc2mOHwh6Y= | ||||
| github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ import ( | |||
| 	"sync/atomic" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/getsentry/raven-go" | ||||
| 	"github.com/getsentry/sentry-go" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"github.com/rs/zerolog" | ||||
| 
 | ||||
|  | @ -58,24 +58,15 @@ func unidirectionalStream(dst io.Writer, src io.Reader, dir string, status *bidi | |||
| 		// server/origin listens forever until closure), it may read/write from the underlying ReadWriter (backed by
 | ||||
| 		// the Edge<->cloudflared transport) in an unexpected state.
 | ||||
| 		// Because of this, we set this recover() logic.
 | ||||
| 		if r := recover(); r != nil { | ||||
| 		if err := recover(); err != nil { | ||||
| 			if status.isAnyDone() { | ||||
| 				// We handle such unexpected errors only when we detect that one side of the streaming is done.
 | ||||
| 				log.Debug().Msgf("Gracefully handled error %v in Streaming for %s, error %s", r, dir, debug.Stack()) | ||||
| 				log.Debug().Msgf("recovered from panic in stream.Pipe for %s, error %s, %s", dir, err, debug.Stack()) | ||||
| 			} else { | ||||
| 				// Otherwise, this is unexpected, but we prevent the program from crashing anyway.
 | ||||
| 				log.Warn().Msgf("Gracefully handled unexpected error %v in Streaming for %s, error %s", r, dir, debug.Stack()) | ||||
| 
 | ||||
| 				tags := make(map[string]string) | ||||
| 				tags["root"] = "websocket.stream" | ||||
| 				tags["dir"] = dir | ||||
| 				switch rval := r.(type) { | ||||
| 				case error: | ||||
| 					raven.CaptureError(rval, tags) | ||||
| 				default: | ||||
| 					rvalStr := fmt.Sprint(rval) | ||||
| 					raven.CaptureMessage(rvalStr, tags) | ||||
| 				} | ||||
| 				log.Warn().Msgf("recovered from panic in stream.Pipe for %s, error %s, %s", dir, err, debug.Stack()) | ||||
| 				sentry.CurrentHub().Recover(err) | ||||
| 				sentry.Flush(time.Second * 5) | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  |  | |||
|  | @ -1,373 +0,0 @@ | |||
| Mozilla Public License Version 2.0 | ||||
| ================================== | ||||
| 
 | ||||
| 1. Definitions | ||||
| -------------- | ||||
| 
 | ||||
| 1.1. "Contributor" | ||||
|     means each individual or legal entity that creates, contributes to | ||||
|     the creation of, or owns Covered Software. | ||||
| 
 | ||||
| 1.2. "Contributor Version" | ||||
|     means the combination of the Contributions of others (if any) used | ||||
|     by a Contributor and that particular Contributor's Contribution. | ||||
| 
 | ||||
| 1.3. "Contribution" | ||||
|     means Covered Software of a particular Contributor. | ||||
| 
 | ||||
| 1.4. "Covered Software" | ||||
|     means Source Code Form to which the initial Contributor has attached | ||||
|     the notice in Exhibit A, the Executable Form of such Source Code | ||||
|     Form, and Modifications of such Source Code Form, in each case | ||||
|     including portions thereof. | ||||
| 
 | ||||
| 1.5. "Incompatible With Secondary Licenses" | ||||
|     means | ||||
| 
 | ||||
|     (a) that the initial Contributor has attached the notice described | ||||
|         in Exhibit B to the Covered Software; or | ||||
| 
 | ||||
|     (b) that the Covered Software was made available under the terms of | ||||
|         version 1.1 or earlier of the License, but not also under the | ||||
|         terms of a Secondary License. | ||||
| 
 | ||||
| 1.6. "Executable Form" | ||||
|     means any form of the work other than Source Code Form. | ||||
| 
 | ||||
| 1.7. "Larger Work" | ||||
|     means a work that combines Covered Software with other material, in  | ||||
|     a separate file or files, that is not Covered Software. | ||||
| 
 | ||||
| 1.8. "License" | ||||
|     means this document. | ||||
| 
 | ||||
| 1.9. "Licensable" | ||||
|     means having the right to grant, to the maximum extent possible, | ||||
|     whether at the time of the initial grant or subsequently, any and | ||||
|     all of the rights conveyed by this License. | ||||
| 
 | ||||
| 1.10. "Modifications" | ||||
|     means any of the following: | ||||
| 
 | ||||
|     (a) any file in Source Code Form that results from an addition to, | ||||
|         deletion from, or modification of the contents of Covered | ||||
|         Software; or | ||||
| 
 | ||||
|     (b) any new file in Source Code Form that contains any Covered | ||||
|         Software. | ||||
| 
 | ||||
| 1.11. "Patent Claims" of a Contributor | ||||
|     means any patent claim(s), including without limitation, method, | ||||
|     process, and apparatus claims, in any patent Licensable by such | ||||
|     Contributor that would be infringed, but for the grant of the | ||||
|     License, by the making, using, selling, offering for sale, having | ||||
|     made, import, or transfer of either its Contributions or its | ||||
|     Contributor Version. | ||||
| 
 | ||||
| 1.12. "Secondary License" | ||||
|     means either the GNU General Public License, Version 2.0, the GNU | ||||
|     Lesser General Public License, Version 2.1, the GNU Affero General | ||||
|     Public License, Version 3.0, or any later versions of those | ||||
|     licenses. | ||||
| 
 | ||||
| 1.13. "Source Code Form" | ||||
|     means the form of the work preferred for making modifications. | ||||
| 
 | ||||
| 1.14. "You" (or "Your") | ||||
|     means an individual or a legal entity exercising rights under this | ||||
|     License. For legal entities, "You" includes any entity that | ||||
|     controls, is controlled by, or is under common control with You. For | ||||
|     purposes of this definition, "control" means (a) the power, direct | ||||
|     or indirect, to cause the direction or management of such entity, | ||||
|     whether by contract or otherwise, or (b) ownership of more than | ||||
|     fifty percent (50%) of the outstanding shares or beneficial | ||||
|     ownership of such entity. | ||||
| 
 | ||||
| 2. License Grants and Conditions | ||||
| -------------------------------- | ||||
| 
 | ||||
| 2.1. Grants | ||||
| 
 | ||||
| Each Contributor hereby grants You a world-wide, royalty-free, | ||||
| non-exclusive license: | ||||
| 
 | ||||
| (a) under intellectual property rights (other than patent or trademark) | ||||
|     Licensable by such Contributor to use, reproduce, make available, | ||||
|     modify, display, perform, distribute, and otherwise exploit its | ||||
|     Contributions, either on an unmodified basis, with Modifications, or | ||||
|     as part of a Larger Work; and | ||||
| 
 | ||||
| (b) under Patent Claims of such Contributor to make, use, sell, offer | ||||
|     for sale, have made, import, and otherwise transfer either its | ||||
|     Contributions or its Contributor Version. | ||||
| 
 | ||||
| 2.2. Effective Date | ||||
| 
 | ||||
| The licenses granted in Section 2.1 with respect to any Contribution | ||||
| become effective for each Contribution on the date the Contributor first | ||||
| distributes such Contribution. | ||||
| 
 | ||||
| 2.3. Limitations on Grant Scope | ||||
| 
 | ||||
| The licenses granted in this Section 2 are the only rights granted under | ||||
| this License. No additional rights or licenses will be implied from the | ||||
| distribution or licensing of Covered Software under this License. | ||||
| Notwithstanding Section 2.1(b) above, no patent license is granted by a | ||||
| Contributor: | ||||
| 
 | ||||
| (a) for any code that a Contributor has removed from Covered Software; | ||||
|     or | ||||
| 
 | ||||
| (b) for infringements caused by: (i) Your and any other third party's | ||||
|     modifications of Covered Software, or (ii) the combination of its | ||||
|     Contributions with other software (except as part of its Contributor | ||||
|     Version); or | ||||
| 
 | ||||
| (c) under Patent Claims infringed by Covered Software in the absence of | ||||
|     its Contributions. | ||||
| 
 | ||||
| This License does not grant any rights in the trademarks, service marks, | ||||
| or logos of any Contributor (except as may be necessary to comply with | ||||
| the notice requirements in Section 3.4). | ||||
| 
 | ||||
| 2.4. Subsequent Licenses | ||||
| 
 | ||||
| No Contributor makes additional grants as a result of Your choice to | ||||
| distribute the Covered Software under a subsequent version of this | ||||
| License (see Section 10.2) or under the terms of a Secondary License (if | ||||
| permitted under the terms of Section 3.3). | ||||
| 
 | ||||
| 2.5. Representation | ||||
| 
 | ||||
| Each Contributor represents that the Contributor believes its | ||||
| Contributions are its original creation(s) or it has sufficient rights | ||||
| to grant the rights to its Contributions conveyed by this License. | ||||
| 
 | ||||
| 2.6. Fair Use | ||||
| 
 | ||||
| This License is not intended to limit any rights You have under | ||||
| applicable copyright doctrines of fair use, fair dealing, or other | ||||
| equivalents. | ||||
| 
 | ||||
| 2.7. Conditions | ||||
| 
 | ||||
| Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted | ||||
| in Section 2.1. | ||||
| 
 | ||||
| 3. Responsibilities | ||||
| ------------------- | ||||
| 
 | ||||
| 3.1. Distribution of Source Form | ||||
| 
 | ||||
| All distribution of Covered Software in Source Code Form, including any | ||||
| Modifications that You create or to which You contribute, must be under | ||||
| the terms of this License. You must inform recipients that the Source | ||||
| Code Form of the Covered Software is governed by the terms of this | ||||
| License, and how they can obtain a copy of this License. You may not | ||||
| attempt to alter or restrict the recipients' rights in the Source Code | ||||
| Form. | ||||
| 
 | ||||
| 3.2. Distribution of Executable Form | ||||
| 
 | ||||
| If You distribute Covered Software in Executable Form then: | ||||
| 
 | ||||
| (a) such Covered Software must also be made available in Source Code | ||||
|     Form, as described in Section 3.1, and You must inform recipients of | ||||
|     the Executable Form how they can obtain a copy of such Source Code | ||||
|     Form by reasonable means in a timely manner, at a charge no more | ||||
|     than the cost of distribution to the recipient; and | ||||
| 
 | ||||
| (b) You may distribute such Executable Form under the terms of this | ||||
|     License, or sublicense it under different terms, provided that the | ||||
|     license for the Executable Form does not attempt to limit or alter | ||||
|     the recipients' rights in the Source Code Form under this License. | ||||
| 
 | ||||
| 3.3. Distribution of a Larger Work | ||||
| 
 | ||||
| You may create and distribute a Larger Work under terms of Your choice, | ||||
| provided that You also comply with the requirements of this License for | ||||
| the Covered Software. If the Larger Work is a combination of Covered | ||||
| Software with a work governed by one or more Secondary Licenses, and the | ||||
| Covered Software is not Incompatible With Secondary Licenses, this | ||||
| License permits You to additionally distribute such Covered Software | ||||
| under the terms of such Secondary License(s), so that the recipient of | ||||
| the Larger Work may, at their option, further distribute the Covered | ||||
| Software under the terms of either this License or such Secondary | ||||
| License(s). | ||||
| 
 | ||||
| 3.4. Notices | ||||
| 
 | ||||
| You may not remove or alter the substance of any license notices | ||||
| (including copyright notices, patent notices, disclaimers of warranty, | ||||
| or limitations of liability) contained within the Source Code Form of | ||||
| the Covered Software, except that You may alter any license notices to | ||||
| the extent required to remedy known factual inaccuracies. | ||||
| 
 | ||||
| 3.5. Application of Additional Terms | ||||
| 
 | ||||
| You may choose to offer, and to charge a fee for, warranty, support, | ||||
| indemnity or liability obligations to one or more recipients of Covered | ||||
| Software. However, You may do so only on Your own behalf, and not on | ||||
| behalf of any Contributor. You must make it absolutely clear that any | ||||
| such warranty, support, indemnity, or liability obligation is offered by | ||||
| You alone, and You hereby agree to indemnify every Contributor for any | ||||
| liability incurred by such Contributor as a result of warranty, support, | ||||
| indemnity or liability terms You offer. You may include additional | ||||
| disclaimers of warranty and limitations of liability specific to any | ||||
| jurisdiction. | ||||
| 
 | ||||
| 4. Inability to Comply Due to Statute or Regulation | ||||
| --------------------------------------------------- | ||||
| 
 | ||||
| If it is impossible for You to comply with any of the terms of this | ||||
| License with respect to some or all of the Covered Software due to | ||||
| statute, judicial order, or regulation then You must: (a) comply with | ||||
| the terms of this License to the maximum extent possible; and (b) | ||||
| describe the limitations and the code they affect. Such description must | ||||
| be placed in a text file included with all distributions of the Covered | ||||
| Software under this License. Except to the extent prohibited by statute | ||||
| or regulation, such description must be sufficiently detailed for a | ||||
| recipient of ordinary skill to be able to understand it. | ||||
| 
 | ||||
| 5. Termination | ||||
| -------------- | ||||
| 
 | ||||
| 5.1. The rights granted under this License will terminate automatically | ||||
| if You fail to comply with any of its terms. However, if You become | ||||
| compliant, then the rights granted under this License from a particular | ||||
| Contributor are reinstated (a) provisionally, unless and until such | ||||
| Contributor explicitly and finally terminates Your grants, and (b) on an | ||||
| ongoing basis, if such Contributor fails to notify You of the | ||||
| non-compliance by some reasonable means prior to 60 days after You have | ||||
| come back into compliance. Moreover, Your grants from a particular | ||||
| Contributor are reinstated on an ongoing basis if such Contributor | ||||
| notifies You of the non-compliance by some reasonable means, this is the | ||||
| first time You have received notice of non-compliance with this License | ||||
| from such Contributor, and You become compliant prior to 30 days after | ||||
| Your receipt of the notice. | ||||
| 
 | ||||
| 5.2. If You initiate litigation against any entity by asserting a patent | ||||
| infringement claim (excluding declaratory judgment actions, | ||||
| counter-claims, and cross-claims) alleging that a Contributor Version | ||||
| directly or indirectly infringes any patent, then the rights granted to | ||||
| You by any and all Contributors for the Covered Software under Section | ||||
| 2.1 of this License shall terminate. | ||||
| 
 | ||||
| 5.3. In the event of termination under Sections 5.1 or 5.2 above, all | ||||
| end user license agreements (excluding distributors and resellers) which | ||||
| have been validly granted by You or Your distributors under this License | ||||
| prior to termination shall survive termination. | ||||
| 
 | ||||
| ************************************************************************ | ||||
| *                                                                      * | ||||
| *  6. Disclaimer of Warranty                                           * | ||||
| *  -------------------------                                           * | ||||
| *                                                                      * | ||||
| *  Covered Software is provided under this License on an "as is"       * | ||||
| *  basis, without warranty of any kind, either expressed, implied, or  * | ||||
| *  statutory, including, without limitation, warranties that the       * | ||||
| *  Covered Software is free of defects, merchantable, fit for a        * | ||||
| *  particular purpose or non-infringing. The entire risk as to the     * | ||||
| *  quality and performance of the Covered Software is with You.        * | ||||
| *  Should any Covered Software prove defective in any respect, You     * | ||||
| *  (not any Contributor) assume the cost of any necessary servicing,   * | ||||
| *  repair, or correction. This disclaimer of warranty constitutes an   * | ||||
| *  essential part of this License. No use of any Covered Software is   * | ||||
| *  authorized under this License except under this disclaimer.         * | ||||
| *                                                                      * | ||||
| ************************************************************************ | ||||
| 
 | ||||
| ************************************************************************ | ||||
| *                                                                      * | ||||
| *  7. Limitation of Liability                                          * | ||||
| *  --------------------------                                          * | ||||
| *                                                                      * | ||||
| *  Under no circumstances and under no legal theory, whether tort      * | ||||
| *  (including negligence), contract, or otherwise, shall any           * | ||||
| *  Contributor, or anyone who distributes Covered Software as          * | ||||
| *  permitted above, be liable to You for any direct, indirect,         * | ||||
| *  special, incidental, or consequential damages of any character      * | ||||
| *  including, without limitation, damages for lost profits, loss of    * | ||||
| *  goodwill, work stoppage, computer failure or malfunction, or any    * | ||||
| *  and all other commercial damages or losses, even if such party      * | ||||
| *  shall have been informed of the possibility of such damages. This   * | ||||
| *  limitation of liability shall not apply to liability for death or   * | ||||
| *  personal injury resulting from such party's negligence to the       * | ||||
| *  extent applicable law prohibits such limitation. Some               * | ||||
| *  jurisdictions do not allow the exclusion or limitation of           * | ||||
| *  incidental or consequential damages, so this exclusion and          * | ||||
| *  limitation may not apply to You.                                    * | ||||
| *                                                                      * | ||||
| ************************************************************************ | ||||
| 
 | ||||
| 8. Litigation | ||||
| ------------- | ||||
| 
 | ||||
| Any litigation relating to this License may be brought only in the | ||||
| courts of a jurisdiction where the defendant maintains its principal | ||||
| place of business and such litigation shall be governed by laws of that | ||||
| jurisdiction, without reference to its conflict-of-law provisions. | ||||
| Nothing in this Section shall prevent a party's ability to bring | ||||
| cross-claims or counter-claims. | ||||
| 
 | ||||
| 9. Miscellaneous | ||||
| ---------------- | ||||
| 
 | ||||
| This License represents the complete agreement concerning the subject | ||||
| matter hereof. If any provision of this License is held to be | ||||
| unenforceable, such provision shall be reformed only to the extent | ||||
| necessary to make it enforceable. Any law or regulation which provides | ||||
| that the language of a contract shall be construed against the drafter | ||||
| shall not be used to construe this License against a Contributor. | ||||
| 
 | ||||
| 10. Versions of the License | ||||
| --------------------------- | ||||
| 
 | ||||
| 10.1. New Versions | ||||
| 
 | ||||
| Mozilla Foundation is the license steward. Except as provided in Section | ||||
| 10.3, no one other than the license steward has the right to modify or | ||||
| publish new versions of this License. Each version will be given a | ||||
| distinguishing version number. | ||||
| 
 | ||||
| 10.2. Effect of New Versions | ||||
| 
 | ||||
| You may distribute the Covered Software under the terms of the version | ||||
| of the License under which You originally received the Covered Software, | ||||
| or under the terms of any subsequent version published by the license | ||||
| steward. | ||||
| 
 | ||||
| 10.3. Modified Versions | ||||
| 
 | ||||
| If you create software not governed by this License, and you want to | ||||
| create a new license for such software, you may create and use a | ||||
| modified version of this License if you rename the license and remove | ||||
| any references to the name of the license steward (except to note that | ||||
| such modified license differs from this License). | ||||
| 
 | ||||
| 10.4. Distributing Source Code Form that is Incompatible With Secondary | ||||
| Licenses | ||||
| 
 | ||||
| If You choose to distribute Source Code Form that is Incompatible With | ||||
| Secondary Licenses under the terms of this version of the License, the | ||||
| notice described in Exhibit B of this License must be attached. | ||||
| 
 | ||||
| Exhibit A - Source Code Form License Notice | ||||
| ------------------------------------------- | ||||
| 
 | ||||
|   This Source Code Form is subject to the terms of the Mozilla Public | ||||
|   License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|   file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
| 
 | ||||
| If it is not possible or desirable to put the notice in a particular | ||||
| file, then You may include the notice in a location (such as a LICENSE | ||||
| file in a relevant directory) where a recipient would be likely to look | ||||
| for such a notice. | ||||
| 
 | ||||
| You may add additional accurate notices of copyright ownership. | ||||
| 
 | ||||
| Exhibit B - "Incompatible With Secondary Licenses" Notice | ||||
| --------------------------------------------------------- | ||||
| 
 | ||||
|   This Source Code Form is "Incompatible With Secondary Licenses", as | ||||
|   defined by the Mozilla Public License, v. 2.0. | ||||
|  | @ -1,69 +0,0 @@ | |||
| # GoCertifi: SSL Certificates for Golang | ||||
| 
 | ||||
| This Go package contains a CA bundle that you can reference in your Go code. | ||||
| This is useful for systems that do not have CA bundles that Golang can find | ||||
| itself, or where a uniform set of CAs is valuable. | ||||
| 
 | ||||
| This is the same CA bundle that ships with the | ||||
| [Python Requests](https://github.com/kennethreitz/requests) library, and is a | ||||
| Golang specific port of [certifi](https://github.com/kennethreitz/certifi). The | ||||
| CA bundle is derived from Mozilla's canonical set. | ||||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
| You can use the `gocertifi` package as follows: | ||||
| 
 | ||||
| ```go | ||||
| import "github.com/certifi/gocertifi" | ||||
| 
 | ||||
| certPool, err := gocertifi.CACerts() | ||||
| ``` | ||||
| 
 | ||||
| You can use the returned `*x509.CertPool` as part of an HTTP transport, for example: | ||||
| 
 | ||||
| ```go | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"crypto/tls" | ||||
| ) | ||||
| 
 | ||||
| // Setup an HTTP client with a custom transport | ||||
| transport := &http.Transport{ | ||||
| 	Proxy: ProxyFromEnvironment, | ||||
| 	DialContext: (&net.Dialer{ | ||||
| 		Timeout:   30 * time.Second, | ||||
| 		KeepAlive: 30 * time.Second, | ||||
| 		DualStack: true, | ||||
| 	}).DialContext, | ||||
| 	ForceAttemptHTTP2:     true, | ||||
| 	MaxIdleConns:          100, | ||||
| 	IdleConnTimeout:       90 * time.Second, | ||||
| 	TLSHandshakeTimeout:   10 * time.Second, | ||||
| 	ExpectContinueTimeout: 1 * time.Second, | ||||
| } | ||||
| // or, starting with go1.13 simply use: | ||||
| // transport := http.DefaultTransport.(*http.Transport).Clone() | ||||
| 
 | ||||
| transport.TLSClientConfig = &tls.Config{RootCAs: certPool} | ||||
| client := &http.Client{Transport: transport} | ||||
| 
 | ||||
| // Make an HTTP request using our custom transport | ||||
| resp, err := client.Get("https://example.com") | ||||
| ``` | ||||
| 
 | ||||
| ## Detailed Documentation | ||||
| 
 | ||||
| Import as follows: | ||||
| 
 | ||||
| ```go | ||||
| import "github.com/certifi/gocertifi" | ||||
| ``` | ||||
| 
 | ||||
| ### Functions | ||||
| 
 | ||||
| ```go | ||||
| func CACerts() (*x509.CertPool, error) | ||||
| ``` | ||||
| CACerts builds an X.509 certificate pool containing the Mozilla CA Certificate | ||||
| bundle. This can't actually error and always returns successfully with `nil` | ||||
| as the error. This will be replaced in `v2` to only return the `CertPool`. | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -1 +0,0 @@ | |||
| .git | ||||
|  | @ -1,5 +0,0 @@ | |||
| *.test | ||||
| *.out | ||||
| example/example | ||||
| /xunit.xml | ||||
| /coverage.xml | ||||
|  | @ -1,41 +0,0 @@ | |||
| sudo: false | ||||
| language: go | ||||
| go: | ||||
|   - 1.7.x | ||||
|   - 1.8.x | ||||
|   - 1.9.x | ||||
|   - 1.10.x | ||||
|   - 1.11.x | ||||
|   - tip | ||||
| 
 | ||||
| before_install: | ||||
|   - go install -race std | ||||
|   - go get golang.org/x/tools/cmd/cover | ||||
|   - go get github.com/tebeka/go2xunit | ||||
|   - go get github.com/t-yuki/gocover-cobertura | ||||
|   - go get -v ./... | ||||
| 
 | ||||
| script: | ||||
|   - go test -v -race ./... | tee gotest.out | ||||
|   - $GOPATH/bin/go2xunit -fail -input gotest.out -output xunit.xml | ||||
|   - go test -v -coverprofile=coverage.txt -covermode count . | ||||
|   - $GOPATH/bin/gocover-cobertura < coverage.txt > coverage.xml | ||||
|      | ||||
| after_script: | ||||
|   - npm install -g @zeus-ci/cli | ||||
|   - zeus upload -t "application/x-cobertura+xml" coverage.xml | ||||
|   - zeus upload -t "application/x-xunit+xml" xunit.xml | ||||
| 
 | ||||
| matrix: | ||||
|   allow_failures: | ||||
|     - go: tip | ||||
| 
 | ||||
| notifications: | ||||
|   webhooks: | ||||
|     urls: | ||||
|       - https://zeus.ci/hooks/cd949996-d30a-11e8-ba53-0a580a28042d/public/provider/travis/webhook | ||||
|     on_success: always | ||||
|     on_failure: always | ||||
|     on_start: always | ||||
|     on_cancel: always | ||||
|     on_error: always | ||||
|  | @ -1,28 +0,0 @@ | |||
| Copyright (c) 2013 Apollic Software, LLC. All rights reserved. | ||||
| Copyright (c) 2015 Functional Software, Inc. All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
| 
 | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Apollic Software, LLC nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | @ -1,19 +0,0 @@ | |||
| # raven  | ||||
| 
 | ||||
| [](https://travis-ci.org/getsentry/raven-go) | ||||
| [](https://goreportcard.com/report/github.com/getsentry/raven-go) | ||||
| [](https://godoc.org/github.com/getsentry/raven-go) | ||||
| 
 | ||||
| raven is the official Go SDK for the [Sentry](https://github.com/getsentry/sentry) | ||||
| event/error logging system. | ||||
| 
 | ||||
| - [**API Documentation**](https://godoc.org/github.com/getsentry/raven-go) | ||||
| - [**Usage and Examples**](https://docs.sentry.io/clients/go/) | ||||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
| ```text | ||||
| go get github.com/getsentry/raven-go | ||||
| ``` | ||||
| 
 | ||||
| Note: Go 1.7 and newer are supported. | ||||
|  | @ -1,977 +0,0 @@ | |||
| // Package raven implements a client for the Sentry error logging service.
 | ||||
| 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") | ||||
| 	ErrMissingProjectID      = errors.New("raven: dsn missing project id") | ||||
| 	ErrInvalidSampleRate     = errors.New("raven: sample rate should be between 0 and 1") | ||||
| ) | ||||
| 
 | ||||
| type Severity string | ||||
| 
 | ||||
| // http://docs.python.org/2/howto/logging.html#logging-levels
 | ||||
| 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 | ||||
| } | ||||
| 
 | ||||
| func (timestamp Timestamp) Format(format string) string { | ||||
| 	t := time.Time(timestamp) | ||||
| 	return t.Format(format) | ||||
| } | ||||
| 
 | ||||
| // An Interface is a Sentry interface that will be serialized as JSON.
 | ||||
| // It must implement json.Marshaler or use json struct tags.
 | ||||
| type Interface interface { | ||||
| 	// The Sentry class name. Example: sentry.interfaces.Stacktrace
 | ||||
| 	Class() string | ||||
| } | ||||
| 
 | ||||
| type Culpriter interface { | ||||
| 	Culprit() string | ||||
| } | ||||
| 
 | ||||
| type Transport interface { | ||||
| 	Send(url, authHeader string, packet *Packet) error | ||||
| } | ||||
| 
 | ||||
| type Extra map[string]interface{} | ||||
| 
 | ||||
| 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 '[': | ||||
| 		// Unmarshal into []Tag
 | ||||
| 		if err := json.Unmarshal(data, &tags); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	case '{': | ||||
| 		// Unmarshal into map[string]string
 | ||||
| 		tagMap := make(map[string]string) | ||||
| 		if err := json.Unmarshal(data, &tagMap); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		// Convert to []Tag
 | ||||
| 		for k, v := range tagMap { | ||||
| 			tags = append(tags, Tag{k, v}) | ||||
| 		} | ||||
| 	default: | ||||
| 		return ErrUnableToUnmarshalJSON | ||||
| 	} | ||||
| 
 | ||||
| 	*t = tags | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // https://docs.getsentry.com/hosted/clientdev/#building-the-json-packet
 | ||||
| type Packet struct { | ||||
| 	// Required
 | ||||
| 	Message string `json:"message"` | ||||
| 
 | ||||
| 	// Required, set automatically by Client.Send/Report via Packet.Init if blank
 | ||||
| 	EventID   string    `json:"event_id"` | ||||
| 	Project   string    `json:"project"` | ||||
| 	Timestamp Timestamp `json:"timestamp"` | ||||
| 	Level     Severity  `json:"level"` | ||||
| 	Logger    string    `json:"logger"` | ||||
| 
 | ||||
| 	// Optional
 | ||||
| 	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       Extra             `json:"extra,omitempty"` | ||||
| 
 | ||||
| 	Interfaces []Interface `json:"-"` | ||||
| } | ||||
| 
 | ||||
| // NewPacket constructs a packet with the specified message and interfaces.
 | ||||
| func NewPacket(message string, interfaces ...Interface) *Packet { | ||||
| 	extra := Extra{} | ||||
| 	setExtraDefaults(extra) | ||||
| 	return &Packet{ | ||||
| 		Message:    message, | ||||
| 		Interfaces: interfaces, | ||||
| 		Extra:      extra, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // NewPacketWithExtra constructs a packet with the specified message, extra information, and interfaces.
 | ||||
| func NewPacketWithExtra(message string, extra Extra, interfaces ...Interface) *Packet { | ||||
| 	if extra == nil { | ||||
| 		extra = Extra{} | ||||
| 	} | ||||
| 	setExtraDefaults(extra) | ||||
| 
 | ||||
| 	return &Packet{ | ||||
| 		Message:    message, | ||||
| 		Interfaces: interfaces, | ||||
| 		Extra:      extra, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func setExtraDefaults(extra Extra) Extra { | ||||
| 	extra["runtime.Version"] = runtime.Version() | ||||
| 	extra["runtime.NumCPU"] = runtime.NumCPU() | ||||
| 	extra["runtime.GOMAXPROCS"] = runtime.GOMAXPROCS(0) // 0 just returns the current value
 | ||||
| 	extra["runtime.NumGoroutine"] = runtime.NumGoroutine() | ||||
| 	return extra | ||||
| } | ||||
| 
 | ||||
| // Init initializes required fields in a packet. It is typically called by
 | ||||
| // Client.Send/Report automatically.
 | ||||
| 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 // clear version
 | ||||
| 	id[6] |= 0x40 // set version to 4 (random uuid)
 | ||||
| 	id[8] &= 0x3F // clear variant
 | ||||
| 	id[8] |= 0x80 // set to IETF variant
 | ||||
| 	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 | ||||
| } | ||||
| 
 | ||||
| // Return a list of interfaces to be used in appending with the rest
 | ||||
| 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 | ||||
| } | ||||
| 
 | ||||
| // The maximum number of packets that will be buffered waiting to be delivered.
 | ||||
| // Packets will be dropped if the buffer is full. Used by NewClient.
 | ||||
| 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")) | ||||
| 	client.SetRelease(os.Getenv("SENTRY_RELEASE")) | ||||
| 	client.SetEnvironment(os.Getenv("SENTRY_ENVIRONMENT")) | ||||
| 	return client | ||||
| } | ||||
| 
 | ||||
| // New constructs a new Sentry client instance
 | ||||
| func New(dsn string) (*Client, error) { | ||||
| 	client := newClient(nil) | ||||
| 	return client, client.SetDSN(dsn) | ||||
| } | ||||
| 
 | ||||
| // NewWithTags constructs a new Sentry client instance with default tags.
 | ||||
| func NewWithTags(dsn string, tags map[string]string) (*Client, error) { | ||||
| 	client := newClient(tags) | ||||
| 	return client, client.SetDSN(dsn) | ||||
| } | ||||
| 
 | ||||
| // NewClient constructs a Sentry client and spawns a background goroutine to
 | ||||
| // handle packets sent by Client.Report.
 | ||||
| //
 | ||||
| // Deprecated: use New and NewWithTags instead
 | ||||
| func NewClient(dsn string, tags map[string]string) (*Client, error) { | ||||
| 	client := newClient(tags) | ||||
| 	return client, client.SetDSN(dsn) | ||||
| } | ||||
| 
 | ||||
| // Client encapsulates a connection to a Sentry server. It must be initialized
 | ||||
| // by calling NewClient. Modification of fields concurrently with Send or after
 | ||||
| // calling Report for the first time is not thread-safe.
 | ||||
| type Client struct { | ||||
| 	Tags map[string]string | ||||
| 
 | ||||
| 	Transport Transport | ||||
| 
 | ||||
| 	// DropHandler is called when a packet is dropped because the buffer is full.
 | ||||
| 	DropHandler func(*Packet) | ||||
| 
 | ||||
| 	// Context that will get appending to all packets
 | ||||
| 	context *context | ||||
| 
 | ||||
| 	mu          sync.RWMutex | ||||
| 	url         string | ||||
| 	projectID   string | ||||
| 	authHeader  string | ||||
| 	release     string | ||||
| 	environment string | ||||
| 	sampleRate  float32 | ||||
| 
 | ||||
| 	// default logger name (leave empty for 'root')
 | ||||
| 	defaultLoggerName string | ||||
| 
 | ||||
| 	includePaths       []string | ||||
| 	ignoreErrorsRegexp *regexp.Regexp | ||||
| 	queue              chan *outgoingPacket | ||||
| 
 | ||||
| 	// A WaitGroup to keep track of all currently in-progress captures
 | ||||
| 	// This is intended to be used with Client.Wait() to assure that
 | ||||
| 	// all messages have been transported before exiting the process.
 | ||||
| 	wg sync.WaitGroup | ||||
| 
 | ||||
| 	// A Once to track only starting up the background worker once
 | ||||
| 	start sync.Once | ||||
| } | ||||
| 
 | ||||
| // Initialize a default *Client instance
 | ||||
| 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) | ||||
| } | ||||
| 
 | ||||
| // SetDSN updates a client with a new DSN. It safe to call after and
 | ||||
| // concurrently with calls to Report and Send.
 | ||||
| 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, hasSecretKey := uri.User.Password() | ||||
| 	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() | ||||
| 
 | ||||
| 	if hasSecretKey { | ||||
| 		client.authHeader = fmt.Sprintf("Sentry sentry_version=4, sentry_key=%s, sentry_secret=%s", publicKey, secretKey) | ||||
| 	} else { | ||||
| 		client.authHeader = fmt.Sprintf("Sentry sentry_version=4, sentry_key=%s", publicKey) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Sets the DSN for the default *Client instance
 | ||||
| func SetDSN(dsn string) error { return DefaultClient.SetDSN(dsn) } | ||||
| 
 | ||||
| // SetRelease sets the "release" tag.
 | ||||
| func (client *Client) SetRelease(release string) { | ||||
| 	client.mu.Lock() | ||||
| 	defer client.mu.Unlock() | ||||
| 	client.release = release | ||||
| } | ||||
| 
 | ||||
| // SetEnvironment sets the "environment" tag.
 | ||||
| func (client *Client) SetEnvironment(environment string) { | ||||
| 	client.mu.Lock() | ||||
| 	defer client.mu.Unlock() | ||||
| 	client.environment = environment | ||||
| } | ||||
| 
 | ||||
| // SetDefaultLoggerName sets the default logger name.
 | ||||
| func (client *Client) SetDefaultLoggerName(name string) { | ||||
| 	client.mu.Lock() | ||||
| 	defer client.mu.Unlock() | ||||
| 	client.defaultLoggerName = name | ||||
| } | ||||
| 
 | ||||
| // SetSampleRate sets how much sampling we want on client side
 | ||||
| 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 | ||||
| } | ||||
| 
 | ||||
| // SetRelease sets the "release" tag on the default *Client
 | ||||
| func SetRelease(release string) { DefaultClient.SetRelease(release) } | ||||
| 
 | ||||
| // SetEnvironment sets the "environment" tag on the default *Client
 | ||||
| func SetEnvironment(environment string) { DefaultClient.SetEnvironment(environment) } | ||||
| 
 | ||||
| // SetDefaultLoggerName sets the "defaultLoggerName" on the default *Client
 | ||||
| func SetDefaultLoggerName(name string) { | ||||
| 	DefaultClient.SetDefaultLoggerName(name) | ||||
| } | ||||
| 
 | ||||
| // SetSampleRate sets the "sample rate" on the degault *Client
 | ||||
| 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() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Capture asynchronously delivers a packet to the Sentry server. It is a no-op
 | ||||
| // when client is nil. A channel is provided if it is important to check for a
 | ||||
| // send's success.
 | ||||
| func (client *Client) Capture(packet *Packet, captureTags map[string]string) (eventID string, ch chan error) { | ||||
| 	ch = make(chan error, 1) | ||||
| 
 | ||||
| 	if client == nil { | ||||
| 		// return a chan that always returns nil when the caller receives from it
 | ||||
| 		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 | ||||
| 	} | ||||
| 
 | ||||
| 	// Keep track of all running Captures so that we can wait for them all to finish
 | ||||
| 	// *Must* call client.wg.Done() on any path that indicates that an event was
 | ||||
| 	// finished being acted upon, whether success or failure
 | ||||
| 	client.wg.Add(1) | ||||
| 
 | ||||
| 	// Merge capture tags and client tags
 | ||||
| 	packet.AddTags(captureTags) | ||||
| 	packet.AddTags(client.Tags) | ||||
| 
 | ||||
| 	// Initialize any required packet fields
 | ||||
| 	client.mu.RLock() | ||||
| 	packet.AddTags(client.context.tags) | ||||
| 	projectID := client.projectID | ||||
| 	release := client.release | ||||
| 	environment := client.environment | ||||
| 	defaultLoggerName := client.defaultLoggerName | ||||
| 	client.mu.RUnlock() | ||||
| 
 | ||||
| 	// set the global logger name on the packet if we must
 | ||||
| 	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} | ||||
| 
 | ||||
| 	// Lazily start background worker until we
 | ||||
| 	// do our first write into the queue.
 | ||||
| 	client.start.Do(func() { | ||||
| 		go client.worker() | ||||
| 	}) | ||||
| 
 | ||||
| 	select { | ||||
| 	case client.queue <- outgoingPacket: | ||||
| 	default: | ||||
| 		// Send would block, drop the packet
 | ||||
| 		if client.DropHandler != nil { | ||||
| 			client.DropHandler(packet) | ||||
| 		} | ||||
| 		ch <- ErrPacketDropped | ||||
| 		client.wg.Done() | ||||
| 	} | ||||
| 
 | ||||
| 	return packet.EventID, ch | ||||
| } | ||||
| 
 | ||||
| // Capture asynchronously delivers a packet to the Sentry server with the default *Client.
 | ||||
| // It is a no-op when client is nil. A channel is provided if it is important to check for a
 | ||||
| // send's success.
 | ||||
| func Capture(packet *Packet, captureTags map[string]string) (eventID string, ch chan error) { | ||||
| 	return DefaultClient.Capture(packet, captureTags) | ||||
| } | ||||
| 
 | ||||
| // CaptureMessage formats and delivers a string message to the Sentry server.
 | ||||
| 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 | ||||
| } | ||||
| 
 | ||||
| // CaptureMessage formats and delivers a string message to the Sentry server with the default *Client
 | ||||
| func CaptureMessage(message string, tags map[string]string, interfaces ...Interface) string { | ||||
| 	return DefaultClient.CaptureMessage(message, tags, interfaces...) | ||||
| } | ||||
| 
 | ||||
| // CaptureMessageAndWait is identical to CaptureMessage except it blocks and waits for the message to be sent.
 | ||||
| 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 | ||||
| } | ||||
| 
 | ||||
| // CaptureMessageAndWait is identical to CaptureMessage except it blocks and waits for the message to be sent.
 | ||||
| func CaptureMessageAndWait(message string, tags map[string]string, interfaces ...Interface) string { | ||||
| 	return DefaultClient.CaptureMessageAndWait(message, tags, interfaces...) | ||||
| } | ||||
| 
 | ||||
| // CaptureErrors formats and delivers an error to the Sentry server.
 | ||||
| // Adds a stacktrace to the packet, excluding the call to this method.
 | ||||
| func (client *Client) CaptureError(err error, tags map[string]string, interfaces ...Interface) string { | ||||
| 	if client == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	if err == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	if client.shouldExcludeErr(err.Error()) { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	extra := extractExtra(err) | ||||
| 	cause := pkgErrors.Cause(err) | ||||
| 
 | ||||
| 	packet := NewPacketWithExtra(err.Error(), extra, append(append(interfaces, client.context.interfaces()...), NewException(cause, GetOrNewStacktrace(cause, 1, 3, client.includePaths)))...) | ||||
| 	eventID, _ := client.Capture(packet, tags) | ||||
| 
 | ||||
| 	return eventID | ||||
| } | ||||
| 
 | ||||
| // CaptureErrors formats and delivers an error to the Sentry server using the default *Client.
 | ||||
| // Adds a stacktrace to the packet, excluding the call to this method.
 | ||||
| func CaptureError(err error, tags map[string]string, interfaces ...Interface) string { | ||||
| 	return DefaultClient.CaptureError(err, tags, interfaces...) | ||||
| } | ||||
| 
 | ||||
| // CaptureErrorAndWait is identical to CaptureError, except it blocks and assures that the event was sent
 | ||||
| func (client *Client) CaptureErrorAndWait(err error, tags map[string]string, interfaces ...Interface) string { | ||||
| 	if client == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	if client.shouldExcludeErr(err.Error()) { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	extra := extractExtra(err) | ||||
| 	cause := pkgErrors.Cause(err) | ||||
| 
 | ||||
| 	packet := NewPacketWithExtra(err.Error(), extra, append(append(interfaces, client.context.interfaces()...), NewException(cause, GetOrNewStacktrace(cause, 1, 3, client.includePaths)))...) | ||||
| 	eventID, ch := client.Capture(packet, tags) | ||||
| 	if eventID != "" { | ||||
| 		<-ch | ||||
| 	} | ||||
| 
 | ||||
| 	return eventID | ||||
| } | ||||
| 
 | ||||
| // CaptureErrorAndWait is identical to CaptureError, except it blocks and assures that the event was sent
 | ||||
| func CaptureErrorAndWait(err error, tags map[string]string, interfaces ...Interface) string { | ||||
| 	return DefaultClient.CaptureErrorAndWait(err, tags, interfaces...) | ||||
| } | ||||
| 
 | ||||
| // CapturePanic calls f and then recovers and reports a panic to the Sentry server if it occurs.
 | ||||
| // If an error is captured, both the error and the reported Sentry error ID are returned.
 | ||||
| func (client *Client) CapturePanic(f func(), tags map[string]string, interfaces ...Interface) (err interface{}, errorID string) { | ||||
| 	// Note: This doesn't need to check for client, because we still want to go through the defer/recover path
 | ||||
| 	// Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the
 | ||||
| 	// *Packet just to be thrown away, this should not be the normal case. Could be refactored to
 | ||||
| 	// be completely noop though if we cared.
 | ||||
| 	defer func() { | ||||
| 		var packet *Packet | ||||
| 		err = recover() | ||||
| 		switch rval := err.(type) { | ||||
| 		case nil: | ||||
| 			return | ||||
| 		case error: | ||||
| 			if client.shouldExcludeErr(rval.Error()) { | ||||
| 				return | ||||
| 			} | ||||
| 			packet = NewPacket(rval.Error(), append(append(interfaces, client.context.interfaces()...), NewException(rval, NewStacktrace(2, 3, client.includePaths)))...) | ||||
| 		default: | ||||
| 			rvalStr := fmt.Sprint(rval) | ||||
| 			if client.shouldExcludeErr(rvalStr) { | ||||
| 				return | ||||
| 			} | ||||
| 			packet = NewPacket(rvalStr, append(append(interfaces, client.context.interfaces()...), NewException(errors.New(rvalStr), NewStacktrace(2, 3, client.includePaths)))...) | ||||
| 		} | ||||
| 
 | ||||
| 		errorID, _ = client.Capture(packet, tags) | ||||
| 	}() | ||||
| 
 | ||||
| 	f() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // CapturePanic calls f and then recovers and reports a panic to the Sentry server if it occurs.
 | ||||
| // If an error is captured, both the error and the reported Sentry error ID are returned.
 | ||||
| func CapturePanic(f func(), tags map[string]string, interfaces ...Interface) (interface{}, string) { | ||||
| 	return DefaultClient.CapturePanic(f, tags, interfaces...) | ||||
| } | ||||
| 
 | ||||
| // CapturePanicAndWait is identical to CaptureError, except it blocks and assures that the event was sent
 | ||||
| func (client *Client) CapturePanicAndWait(f func(), tags map[string]string, interfaces ...Interface) (err interface{}, errorID string) { | ||||
| 	// Note: This doesn't need to check for client, because we still want to go through the defer/recover path
 | ||||
| 	// Down the line, Capture will be noop'd, so while this does a _tiny_ bit of overhead constructing the
 | ||||
| 	// *Packet just to be thrown away, this should not be the normal case. Could be refactored to
 | ||||
| 	// be completely noop though if we cared.
 | ||||
| 	defer func() { | ||||
| 		var packet *Packet | ||||
| 		err = recover() | ||||
| 		switch rval := err.(type) { | ||||
| 		case nil: | ||||
| 			return | ||||
| 		case error: | ||||
| 			if client.shouldExcludeErr(rval.Error()) { | ||||
| 				return | ||||
| 			} | ||||
| 			packet = NewPacket(rval.Error(), append(append(interfaces, client.context.interfaces()...), NewException(rval, NewStacktrace(2, 3, client.includePaths)))...) | ||||
| 		default: | ||||
| 			rvalStr := fmt.Sprint(rval) | ||||
| 			if client.shouldExcludeErr(rvalStr) { | ||||
| 				return | ||||
| 			} | ||||
| 			packet = NewPacket(rvalStr, append(append(interfaces, client.context.interfaces()...), NewException(errors.New(rvalStr), NewStacktrace(2, 3, client.includePaths)))...) | ||||
| 		} | ||||
| 
 | ||||
| 		var ch chan error | ||||
| 		errorID, ch = client.Capture(packet, tags) | ||||
| 		if errorID != "" { | ||||
| 			<-ch | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	f() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // CapturePanicAndWait is identical to CaptureError, except it blocks and assures that the event was sent
 | ||||
| func CapturePanicAndWait(f func(), tags map[string]string, interfaces ...Interface) (interface{}, string) { | ||||
| 	return DefaultClient.CapturePanicAndWait(f, tags, interfaces...) | ||||
| } | ||||
| 
 | ||||
| func (client *Client) Close() { | ||||
| 	close(client.queue) | ||||
| } | ||||
| 
 | ||||
| func Close() { DefaultClient.Close() } | ||||
| 
 | ||||
| // Wait blocks and waits for all events to finish being sent to Sentry server
 | ||||
| func (client *Client) Wait() { | ||||
| 	client.wg.Wait() | ||||
| } | ||||
| 
 | ||||
| // Wait blocks and waits for all events to finish being sent to Sentry server
 | ||||
| func Wait() { DefaultClient.Wait() } | ||||
| 
 | ||||
| func (client *Client) URL() string { | ||||
| 	client.mu.RLock() | ||||
| 	defer client.mu.RUnlock() | ||||
| 
 | ||||
| 	return client.url | ||||
| } | ||||
| 
 | ||||
| func URL() string { return DefaultClient.URL() } | ||||
| 
 | ||||
| func (client *Client) ProjectID() string { | ||||
| 	client.mu.RLock() | ||||
| 	defer client.mu.RUnlock() | ||||
| 
 | ||||
| 	return client.projectID | ||||
| } | ||||
| 
 | ||||
| func ProjectID() string { return DefaultClient.ProjectID() } | ||||
| 
 | ||||
| func (client *Client) Release() string { | ||||
| 	client.mu.RLock() | ||||
| 	defer client.mu.RUnlock() | ||||
| 
 | ||||
| 	return client.release | ||||
| } | ||||
| 
 | ||||
| func Release() string { return DefaultClient.Release() } | ||||
| 
 | ||||
| func IncludePaths() []string { return DefaultClient.IncludePaths() } | ||||
| 
 | ||||
| func (client *Client) IncludePaths() []string { | ||||
| 	client.mu.RLock() | ||||
| 	defer client.mu.RUnlock() | ||||
| 
 | ||||
| 	return client.includePaths | ||||
| } | ||||
| 
 | ||||
| func SetIncludePaths(p []string) { DefaultClient.SetIncludePaths(p) } | ||||
| 
 | ||||
| func (client *Client) SetIncludePaths(p []string) { | ||||
| 	client.mu.Lock() | ||||
| 	defer client.mu.Unlock() | ||||
| 
 | ||||
| 	client.includePaths = p | ||||
| } | ||||
| 
 | ||||
| func (c *Client) SetUserContext(u *User) { | ||||
| 	c.mu.Lock() | ||||
| 	defer c.mu.Unlock() | ||||
| 	c.context.setUser(u) | ||||
| } | ||||
| 
 | ||||
| func (c *Client) SetHttpContext(h *Http) { | ||||
| 	c.mu.Lock() | ||||
| 	defer c.mu.Unlock() | ||||
| 	c.context.setHttp(h) | ||||
| } | ||||
| 
 | ||||
| func (c *Client) SetTagsContext(t map[string]string) { | ||||
| 	c.mu.Lock() | ||||
| 	defer c.mu.Unlock() | ||||
| 	c.context.setTags(t) | ||||
| } | ||||
| 
 | ||||
| func (c *Client) ClearContext() { | ||||
| 	c.mu.Lock() | ||||
| 	defer c.mu.Unlock() | ||||
| 	c.context.clear() | ||||
| } | ||||
| 
 | ||||
| func SetUserContext(u *User)             { DefaultClient.SetUserContext(u) } | ||||
| func SetHttpContext(h *Http)             { DefaultClient.SetHttpContext(h) } | ||||
| func SetTagsContext(t map[string]string) { DefaultClient.SetTagsContext(t) } | ||||
| func ClearContext()                      { DefaultClient.ClearContext() } | ||||
| 
 | ||||
| // HTTPTransport is the default transport, delivering packets to Sentry via the
 | ||||
| // HTTP API.
 | ||||
| type HTTPTransport struct { | ||||
| 	*http.Client | ||||
| } | ||||
| 
 | ||||
| func (t *HTTPTransport) Send(url, authHeader string, packet *Packet) error { | ||||
| 	if url == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	body, contentType, err := serializedPacket(packet) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error serializing packet: %v", err) | ||||
| 	} | ||||
| 	req, err := http.NewRequest("POST", url, body) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("can't create new request: %v", err) | ||||
| 	} | ||||
| 	req.Header.Set("X-Sentry-Auth", authHeader) | ||||
| 	req.Header.Set("User-Agent", userAgent) | ||||
| 	req.Header.Set("Content-Type", contentType) | ||||
| 	res, err := t.Do(req) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	io.Copy(ioutil.Discard, res.Body) | ||||
| 	res.Body.Close() | ||||
| 	if res.StatusCode != 200 { | ||||
| 		return fmt.Errorf("raven: got http status %d - x-sentry-error: %s", res.StatusCode, res.Header.Get("X-Sentry-Error")) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func serializedPacket(packet *Packet) (io.Reader, string, error) { | ||||
| 	packetJSON, err := packet.JSON() | ||||
| 	if err != nil { | ||||
| 		return nil, "", fmt.Errorf("error marshaling packet %+v to JSON: %v", packet, err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Only deflate/base64 the packet if it is bigger than 1KB, as there is
 | ||||
| 	// overhead.
 | ||||
| 	if len(packetJSON) > 1000 { | ||||
| 		buf := &bytes.Buffer{} | ||||
| 		b64 := base64.NewEncoder(base64.StdEncoding, buf) | ||||
| 		deflate, _ := zlib.NewWriterLevel(b64, zlib.BestCompression) | ||||
| 		deflate.Write(packetJSON) | ||||
| 		deflate.Close() | ||||
| 		b64.Close() | ||||
| 		return buf, "application/octet-stream", nil | ||||
| 	} | ||||
| 	return bytes.NewReader(packetJSON), "application/json", nil | ||||
| } | ||||
| 
 | ||||
| var hostname string | ||||
| 
 | ||||
| func init() { | ||||
| 	hostname, _ = os.Hostname() | ||||
| } | ||||
|  | @ -1,60 +0,0 @@ | |||
| package raven | ||||
| 
 | ||||
| type causer interface { | ||||
| 	Cause() error | ||||
| } | ||||
| 
 | ||||
| type errWrappedWithExtra struct { | ||||
| 	err       error | ||||
| 	extraInfo map[string]interface{} | ||||
| } | ||||
| 
 | ||||
| func (ewx *errWrappedWithExtra) Error() string { | ||||
| 	return ewx.err.Error() | ||||
| } | ||||
| 
 | ||||
| func (ewx *errWrappedWithExtra) Cause() error { | ||||
| 	return ewx.err | ||||
| } | ||||
| 
 | ||||
| func (ewx *errWrappedWithExtra) ExtraInfo() Extra { | ||||
| 	return ewx.extraInfo | ||||
| } | ||||
| 
 | ||||
| // Adds extra data to an error before reporting to Sentry
 | ||||
| func WrapWithExtra(err error, extraInfo map[string]interface{}) error { | ||||
| 	return &errWrappedWithExtra{ | ||||
| 		err:       err, | ||||
| 		extraInfo: extraInfo, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type ErrWithExtra interface { | ||||
| 	Error() string | ||||
| 	Cause() error | ||||
| 	ExtraInfo() Extra | ||||
| } | ||||
| 
 | ||||
| // Iteratively fetches all the Extra data added to an error,
 | ||||
| // and it's underlying errors. Extra data defined first is
 | ||||
| // respected, and is not overridden when extracting.
 | ||||
| func extractExtra(err error) Extra { | ||||
| 	extra := Extra{} | ||||
| 
 | ||||
| 	currentErr := err | ||||
| 	for currentErr != nil { | ||||
| 		if errWithExtra, ok := currentErr.(ErrWithExtra); ok { | ||||
| 			for k, v := range errWithExtra.ExtraInfo() { | ||||
| 				extra[k] = v | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if errWithCause, ok := currentErr.(causer); ok { | ||||
| 			currentErr = errWithCause.Cause() | ||||
| 		} else { | ||||
| 			currentErr = nil | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return extra | ||||
| } | ||||
|  | @ -1,50 +0,0 @@ | |||
| package raven | ||||
| 
 | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"regexp" | ||||
| ) | ||||
| 
 | ||||
| var errorMsgPattern = regexp.MustCompile(`\A(\w+): (.+)\z`) | ||||
| 
 | ||||
| func NewException(err error, stacktrace *Stacktrace) *Exception { | ||||
| 	msg := err.Error() | ||||
| 	ex := &Exception{ | ||||
| 		Stacktrace: stacktrace, | ||||
| 		Value:      msg, | ||||
| 		Type:       reflect.TypeOf(err).String(), | ||||
| 	} | ||||
| 	if m := errorMsgPattern.FindStringSubmatch(msg); m != nil { | ||||
| 		ex.Module, ex.Value = m[1], m[2] | ||||
| 	} | ||||
| 	return ex | ||||
| } | ||||
| 
 | ||||
| // https://docs.getsentry.com/hosted/clientdev/interfaces/#failure-interfaces
 | ||||
| type Exception struct { | ||||
| 	// Required
 | ||||
| 	Value string `json:"value"` | ||||
| 
 | ||||
| 	// Optional
 | ||||
| 	Type       string      `json:"type,omitempty"` | ||||
| 	Module     string      `json:"module,omitempty"` | ||||
| 	Stacktrace *Stacktrace `json:"stacktrace,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (e *Exception) Class() string { return "exception" } | ||||
| 
 | ||||
| func (e *Exception) Culprit() string { | ||||
| 	if e.Stacktrace == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return e.Stacktrace.Culprit() | ||||
| } | ||||
| 
 | ||||
| // Exceptions allows for chained errors
 | ||||
| // https://docs.sentry.io/clientdev/interfaces/exception/
 | ||||
| type Exceptions struct { | ||||
| 	// Required
 | ||||
| 	Values []*Exception `json:"values"` | ||||
| } | ||||
| 
 | ||||
| func (es Exceptions) Class() string { return "exception" } | ||||
|  | @ -1,99 +0,0 @@ | |||
| package raven | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"runtime/debug" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| func NewHttp(req *http.Request) *Http { | ||||
| 	proto := "http" | ||||
| 	if req.TLS != nil || req.Header.Get("X-Forwarded-Proto") == "https" { | ||||
| 		proto = "https" | ||||
| 	} | ||||
| 	h := &Http{ | ||||
| 		Method:  req.Method, | ||||
| 		Cookies: req.Header.Get("Cookie"), | ||||
| 		Query:   sanitizeQuery(req.URL.Query()).Encode(), | ||||
| 		URL:     proto + "://" + req.Host + req.URL.Path, | ||||
| 		Headers: make(map[string]string, len(req.Header)), | ||||
| 	} | ||||
| 	if addr, port, err := net.SplitHostPort(req.RemoteAddr); err == nil { | ||||
| 		h.Env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port} | ||||
| 	} | ||||
| 	for k, v := range req.Header { | ||||
| 		h.Headers[k] = strings.Join(v, ",") | ||||
| 	} | ||||
| 	h.Headers["Host"] = req.Host | ||||
| 	return h | ||||
| } | ||||
| 
 | ||||
| var querySecretFields = []string{"password", "passphrase", "passwd", "secret"} | ||||
| 
 | ||||
| func sanitizeQuery(query url.Values) url.Values { | ||||
| 	for _, keyword := range querySecretFields { | ||||
| 		for field := range query { | ||||
| 			if strings.Contains(field, keyword) { | ||||
| 				query[field] = []string{"********"} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return query | ||||
| } | ||||
| 
 | ||||
| // https://docs.getsentry.com/hosted/clientdev/interfaces/#context-interfaces
 | ||||
| type Http struct { | ||||
| 	// Required
 | ||||
| 	URL    string `json:"url"` | ||||
| 	Method string `json:"method"` | ||||
| 	Query  string `json:"query_string,omitempty"` | ||||
| 
 | ||||
| 	// Optional
 | ||||
| 	Cookies string            `json:"cookies,omitempty"` | ||||
| 	Headers map[string]string `json:"headers,omitempty"` | ||||
| 	Env     map[string]string `json:"env,omitempty"` | ||||
| 
 | ||||
| 	// Must be either a string or map[string]string
 | ||||
| 	Data interface{} `json:"data,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (h *Http) Class() string { return "request" } | ||||
| 
 | ||||
| // Recovery handler to wrap the stdlib net/http Mux.
 | ||||
| // Example:
 | ||||
| //	http.HandleFunc("/", raven.RecoveryHandler(func(w http.ResponseWriter, r *http.Request) {
 | ||||
| //		...
 | ||||
| //	}))
 | ||||
| func RecoveryHandler(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) { | ||||
| 	return Recoverer(http.HandlerFunc(handler)).ServeHTTP | ||||
| } | ||||
| 
 | ||||
| // Recovery handler to wrap the stdlib net/http Mux.
 | ||||
| // Example:
 | ||||
| //  mux := http.NewServeMux
 | ||||
| //  ...
 | ||||
| //	http.Handle("/", raven.Recoverer(mux))
 | ||||
| func Recoverer(handler http.Handler) http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		defer func() { | ||||
| 			if rval := recover(); rval != nil { | ||||
| 				debug.PrintStack() | ||||
| 				rvalStr := fmt.Sprint(rval) | ||||
| 				var packet *Packet | ||||
| 				if err, ok := rval.(error); ok { | ||||
| 					packet = NewPacket(rvalStr, NewException(errors.New(rvalStr), GetOrNewStacktrace(err, 2, 3, nil)), NewHttp(r)) | ||||
| 				} else { | ||||
| 					packet = NewPacket(rvalStr, NewException(errors.New(rvalStr), NewStacktrace(2, 3, nil)), NewHttp(r)) | ||||
| 				} | ||||
| 				Capture(packet, nil) | ||||
| 				w.WriteHeader(http.StatusInternalServerError) | ||||
| 			} | ||||
| 		}() | ||||
| 
 | ||||
| 		handler.ServeHTTP(w, r) | ||||
| 	}) | ||||
| } | ||||
|  | @ -1,49 +0,0 @@ | |||
| package raven | ||||
| 
 | ||||
| // https://docs.getsentry.com/hosted/clientdev/interfaces/#message-interface
 | ||||
| type Message struct { | ||||
| 	// Required
 | ||||
| 	Message string `json:"message"` | ||||
| 
 | ||||
| 	// Optional
 | ||||
| 	Params []interface{} `json:"params,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (m *Message) Class() string { return "logentry" } | ||||
| 
 | ||||
| // https://docs.getsentry.com/hosted/clientdev/interfaces/#template-interface
 | ||||
| type Template struct { | ||||
| 	// Required
 | ||||
| 	Filename    string `json:"filename"` | ||||
| 	Lineno      int    `json:"lineno"` | ||||
| 	ContextLine string `json:"context_line"` | ||||
| 
 | ||||
| 	// Optional
 | ||||
| 	PreContext   []string `json:"pre_context,omitempty"` | ||||
| 	PostContext  []string `json:"post_context,omitempty"` | ||||
| 	AbsolutePath string   `json:"abs_path,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (t *Template) Class() string { return "template" } | ||||
| 
 | ||||
| // https://docs.getsentry.com/hosted/clientdev/interfaces/#context-interfaces
 | ||||
| type User struct { | ||||
| 	// All fields are optional
 | ||||
| 	ID       string `json:"id,omitempty"` | ||||
| 	Username string `json:"username,omitempty"` | ||||
| 	Email    string `json:"email,omitempty"` | ||||
| 	IP       string `json:"ip_address,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (h *User) Class() string { return "user" } | ||||
| 
 | ||||
| // https://docs.getsentry.com/hosted/clientdev/interfaces/#context-interfaces
 | ||||
| type Query struct { | ||||
| 	// Required
 | ||||
| 	Query string `json:"query"` | ||||
| 
 | ||||
| 	// Optional
 | ||||
| 	Engine string `json:"engine,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (q *Query) Class() string { return "query" } | ||||
|  | @ -1,4 +0,0 @@ | |||
| #!/bin/bash | ||||
| go test -race ./... | ||||
| go test -cover ./... | ||||
| go test -v ./... | ||||
|  | @ -1,277 +0,0 @@ | |||
| // Copyright 2011 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| // Some code from the runtime/debug package of the Go standard library.
 | ||||
| 
 | ||||
| package raven | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"go/build" | ||||
| 	"io/ioutil" | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| // https://docs.getsentry.com/hosted/clientdev/interfaces/#failure-interfaces
 | ||||
| type Stacktrace struct { | ||||
| 	// Required
 | ||||
| 	Frames []*StacktraceFrame `json:"frames"` | ||||
| } | ||||
| 
 | ||||
| func (s *Stacktrace) Class() string { return "stacktrace" } | ||||
| 
 | ||||
| func (s *Stacktrace) Culprit() string { | ||||
| 	for i := len(s.Frames) - 1; i >= 0; i-- { | ||||
| 		frame := s.Frames[i] | ||||
| 		if frame.InApp == true && frame.Module != "" && frame.Function != "" { | ||||
| 			return frame.Module + "." + frame.Function | ||||
| 		} | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| type StacktraceFrame struct { | ||||
| 	// At least one required
 | ||||
| 	Filename string `json:"filename,omitempty"` | ||||
| 	Function string `json:"function,omitempty"` | ||||
| 	Module   string `json:"module,omitempty"` | ||||
| 
 | ||||
| 	// Optional
 | ||||
| 	Lineno       int      `json:"lineno,omitempty"` | ||||
| 	Colno        int      `json:"colno,omitempty"` | ||||
| 	AbsolutePath string   `json:"abs_path,omitempty"` | ||||
| 	ContextLine  string   `json:"context_line,omitempty"` | ||||
| 	PreContext   []string `json:"pre_context,omitempty"` | ||||
| 	PostContext  []string `json:"post_context,omitempty"` | ||||
| 	InApp        bool     `json:"in_app"` | ||||
| } | ||||
| 
 | ||||
| // Try to get stacktrace from err as an interface of github.com/pkg/errors, or else NewStacktrace()
 | ||||
| func GetOrNewStacktrace(err error, skip int, context int, appPackagePrefixes []string) *Stacktrace { | ||||
| 	stacktracer, errHasStacktrace := err.(interface { | ||||
| 		StackTrace() errors.StackTrace | ||||
| 	}) | ||||
| 	if errHasStacktrace { | ||||
| 		var frames []*StacktraceFrame | ||||
| 		for _, f := range stacktracer.StackTrace() { | ||||
| 			pc := uintptr(f) - 1 | ||||
| 			fn := runtime.FuncForPC(pc) | ||||
| 			var fName string | ||||
| 			var file string | ||||
| 			var line int | ||||
| 			if fn != nil { | ||||
| 				file, line = fn.FileLine(pc) | ||||
| 				fName = fn.Name() | ||||
| 			} else { | ||||
| 				file = "unknown" | ||||
| 				fName = "unknown" | ||||
| 			} | ||||
| 			frame := NewStacktraceFrame(pc, fName, file, line, context, appPackagePrefixes) | ||||
| 			if frame != nil { | ||||
| 				frames = append([]*StacktraceFrame{frame}, frames...) | ||||
| 			} | ||||
| 		} | ||||
| 		return &Stacktrace{Frames: frames} | ||||
| 	} else { | ||||
| 		return NewStacktrace(skip+1, context, appPackagePrefixes) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Intialize and populate a new stacktrace, skipping skip frames.
 | ||||
| //
 | ||||
| // context is the number of surrounding lines that should be included for context.
 | ||||
| // Setting context to 3 would try to get seven lines. Setting context to -1 returns
 | ||||
| // one line with no surrounding context, and 0 returns no context.
 | ||||
| //
 | ||||
| // appPackagePrefixes is a list of prefixes used to check whether a package should
 | ||||
| // be considered "in app".
 | ||||
| func NewStacktrace(skip int, context int, appPackagePrefixes []string) *Stacktrace { | ||||
| 	var frames []*StacktraceFrame | ||||
| 
 | ||||
| 	callerPcs := make([]uintptr, 100) | ||||
| 	numCallers := runtime.Callers(skip+2, callerPcs) | ||||
| 
 | ||||
| 	// If there are no callers, the entire stacktrace is nil
 | ||||
| 	if numCallers == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	callersFrames := runtime.CallersFrames(callerPcs) | ||||
| 
 | ||||
| 	for { | ||||
| 		fr, more := callersFrames.Next() | ||||
| 		if fr.Func != nil { | ||||
| 			frame := NewStacktraceFrame(fr.PC, fr.Function, fr.File, fr.Line, context, appPackagePrefixes) | ||||
| 			if frame != nil { | ||||
| 				frames = append(frames, frame) | ||||
| 			} | ||||
| 		} | ||||
| 		if !more { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	// If there are no frames, the entire stacktrace is nil
 | ||||
| 	if len(frames) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	// Optimize the path where there's only 1 frame
 | ||||
| 	if len(frames) == 1 { | ||||
| 		return &Stacktrace{frames} | ||||
| 	} | ||||
| 	// Sentry wants the frames with the oldest first, so reverse them
 | ||||
| 	for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 { | ||||
| 		frames[i], frames[j] = frames[j], frames[i] | ||||
| 	} | ||||
| 	return &Stacktrace{frames} | ||||
| } | ||||
| 
 | ||||
| // Build a single frame using data returned from runtime.Caller.
 | ||||
| //
 | ||||
| // context is the number of surrounding lines that should be included for context.
 | ||||
| // Setting context to 3 would try to get seven lines. Setting context to -1 returns
 | ||||
| // one line with no surrounding context, and 0 returns no context.
 | ||||
| //
 | ||||
| // appPackagePrefixes is a list of prefixes used to check whether a package should
 | ||||
| // be considered "in app".
 | ||||
| func NewStacktraceFrame(pc uintptr, fName, file string, line, context int, appPackagePrefixes []string) *StacktraceFrame { | ||||
| 	frame := &StacktraceFrame{AbsolutePath: file, Filename: trimPath(file), Lineno: line, InApp: false} | ||||
| 	frame.Module, frame.Function = functionName(fName) | ||||
| 
 | ||||
| 	// `runtime.goexit` is effectively a placeholder that comes from
 | ||||
| 	// runtime/asm_amd64.s and is meaningless.
 | ||||
| 	if frame.Module == "runtime" && frame.Function == "goexit" { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	if frame.Module == "main" { | ||||
| 		frame.InApp = true | ||||
| 	} else { | ||||
| 		for _, prefix := range appPackagePrefixes { | ||||
| 			if strings.HasPrefix(frame.Module, prefix) && !strings.Contains(frame.Module, "vendor") && !strings.Contains(frame.Module, "third_party") { | ||||
| 				frame.InApp = true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if context > 0 { | ||||
| 		contextLines, lineIdx := sourceCodeLoader.Load(file, line, context) | ||||
| 		if len(contextLines) > 0 { | ||||
| 			for i, line := range contextLines { | ||||
| 				switch { | ||||
| 				case i < lineIdx: | ||||
| 					frame.PreContext = append(frame.PreContext, string(line)) | ||||
| 				case i == lineIdx: | ||||
| 					frame.ContextLine = string(line) | ||||
| 				default: | ||||
| 					frame.PostContext = append(frame.PostContext, string(line)) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} else if context == -1 { | ||||
| 		contextLine, _ := sourceCodeLoader.Load(file, line, 0) | ||||
| 		if len(contextLine) > 0 { | ||||
| 			frame.ContextLine = string(contextLine[0]) | ||||
| 		} | ||||
| 	} | ||||
| 	return frame | ||||
| } | ||||
| 
 | ||||
| // Retrieve the name of the package and function containing the PC.
 | ||||
| func functionName(fName string) (pack string, name string) { | ||||
| 	name = fName | ||||
| 	// We get this:
 | ||||
| 	//	runtime/debug.*T·ptrmethod
 | ||||
| 	// and want this:
 | ||||
| 	//  pack = runtime/debug
 | ||||
| 	//	name = *T.ptrmethod
 | ||||
| 	if idx := strings.LastIndex(name, "."); idx != -1 { | ||||
| 		pack = name[:idx] | ||||
| 		name = name[idx+1:] | ||||
| 	} | ||||
| 	name = strings.Replace(name, "·", ".", -1) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| type SourceCodeLoader interface { | ||||
| 	Load(filename string, line, context int) ([][]byte, int) | ||||
| } | ||||
| 
 | ||||
| var sourceCodeLoader SourceCodeLoader = &fsLoader{cache: make(map[string][][]byte)} | ||||
| 
 | ||||
| func SetSourceCodeLoader(loader SourceCodeLoader) { | ||||
| 	sourceCodeLoader = loader | ||||
| } | ||||
| 
 | ||||
| type fsLoader struct { | ||||
| 	mu    sync.Mutex | ||||
| 	cache map[string][][]byte | ||||
| } | ||||
| 
 | ||||
| func (fs *fsLoader) Load(filename string, line, context int) ([][]byte, int) { | ||||
| 	fs.mu.Lock() | ||||
| 	defer fs.mu.Unlock() | ||||
| 	lines, ok := fs.cache[filename] | ||||
| 	if !ok { | ||||
| 		data, err := ioutil.ReadFile(filename) | ||||
| 		if err != nil { | ||||
| 			// cache errors as nil slice: code below handles it correctly
 | ||||
| 			// otherwise when missing the source or running as a different user, we try
 | ||||
| 			// reading the file on each error which is unnecessary
 | ||||
| 			fs.cache[filename] = nil | ||||
| 			return nil, 0 | ||||
| 		} | ||||
| 		lines = bytes.Split(data, []byte{'\n'}) | ||||
| 		fs.cache[filename] = lines | ||||
| 	} | ||||
| 
 | ||||
| 	if lines == nil { | ||||
| 		// cached error from ReadFile: return no lines
 | ||||
| 		return nil, 0 | ||||
| 	} | ||||
| 
 | ||||
| 	line-- // stack trace lines are 1-indexed
 | ||||
| 	start := line - context | ||||
| 	var idx int | ||||
| 	if start < 0 { | ||||
| 		start = 0 | ||||
| 		idx = line | ||||
| 	} else { | ||||
| 		idx = context | ||||
| 	} | ||||
| 	end := line + context + 1 | ||||
| 	if line >= len(lines) { | ||||
| 		return nil, 0 | ||||
| 	} | ||||
| 	if end > len(lines) { | ||||
| 		end = len(lines) | ||||
| 	} | ||||
| 	return lines[start:end], idx | ||||
| } | ||||
| 
 | ||||
| var trimPaths []string | ||||
| 
 | ||||
| // Try to trim the GOROOT or GOPATH prefix off of a filename
 | ||||
| func trimPath(filename string) string { | ||||
| 	for _, prefix := range trimPaths { | ||||
| 		if trimmed := strings.TrimPrefix(filename, prefix); len(trimmed) < len(filename) { | ||||
| 			return trimmed | ||||
| 		} | ||||
| 	} | ||||
| 	return filename | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	// Collect all source directories, and make sure they
 | ||||
| 	// end in a trailing "separator"
 | ||||
| 	for _, prefix := range build.Default.SrcDirs() { | ||||
| 		if prefix[len(prefix)-1] != filepath.Separator { | ||||
| 			prefix += string(filepath.Separator) | ||||
| 		} | ||||
| 		trimPaths = append(trimPaths, prefix) | ||||
| 	} | ||||
| } | ||||
|  | @ -1,20 +0,0 @@ | |||
| package raven | ||||
| 
 | ||||
| type Writer struct { | ||||
| 	Client *Client | ||||
| 	Level  Severity | ||||
| 	Logger string // Logger name reported to Sentry
 | ||||
| } | ||||
| 
 | ||||
| // Write formats the byte slice p into a string, and sends a message to
 | ||||
| // Sentry at the severity level indicated by the Writer w.
 | ||||
| func (w *Writer) Write(p []byte) (int, error) { | ||||
| 	message := string(p) | ||||
| 
 | ||||
| 	packet := NewPacket(message, &Message{message, nil}) | ||||
| 	packet.Level = w.Level | ||||
| 	packet.Logger = w.Logger | ||||
| 	w.Client.Capture(packet, nil) | ||||
| 
 | ||||
| 	return len(p), nil | ||||
| } | ||||
|  | @ -8,9 +8,6 @@ github.com/apparentlymart/go-cidr/cidr | |||
| # github.com/beorn7/perks v1.0.1 | ||||
| ## explicit; go 1.11 | ||||
| github.com/beorn7/perks/quantile | ||||
| # github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d | ||||
| ## explicit; go 1.12 | ||||
| github.com/certifi/gocertifi | ||||
| # github.com/cespare/xxhash/v2 v2.1.2 | ||||
| ## explicit; go 1.11 | ||||
| github.com/cespare/xxhash/v2 | ||||
|  | @ -109,9 +106,6 @@ github.com/flynn/go-shlex | |||
| # github.com/fsnotify/fsnotify v1.4.9 | ||||
| ## explicit; go 1.13 | ||||
| github.com/fsnotify/fsnotify | ||||
| # github.com/getsentry/raven-go v0.2.0 | ||||
| ## explicit | ||||
| github.com/getsentry/raven-go | ||||
| # github.com/getsentry/sentry-go v0.16.0 | ||||
| ## explicit; go 1.19 | ||||
| github.com/getsentry/sentry-go | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue