├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── LICENSE ├── README.md ├── clevels ├── austerity.go ├── austerity_test.go ├── austeritylevel_string.go └── doc.go ├── filters ├── austerity.go ├── austerity_test.go ├── doc.go ├── timeprefix.go └── timeprefix_test.go ├── generate_input.sh ├── go.mod ├── go.sum ├── json ├── json.go └── json_test.go ├── logger ├── doc.go ├── unilog.go └── unilog_test.go ├── main.go ├── reader ├── doc.go ├── reader.go └── reader_test.go └── vendor ├── github.com ├── DataDog │ └── datadog-go │ │ ├── LICENSE.txt │ │ └── statsd │ │ ├── README.md │ │ └── statsd.go ├── davecgh │ └── go-spew │ │ ├── LICENSE │ │ └── spew │ │ ├── bypass.go │ │ ├── bypasssafe.go │ │ ├── common.go │ │ ├── config.go │ │ ├── doc.go │ │ ├── dump.go │ │ ├── format.go │ │ └── spew.go ├── getsentry │ └── sentry-go │ │ ├── .craft.yml │ │ ├── .gitignore │ │ ├── .golangci.yml │ │ ├── .travis.yml │ │ ├── CHANGELOG.md │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── MIGRATION.md │ │ ├── README.md │ │ ├── client.go │ │ ├── dsn.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── hub.go │ │ ├── integrations.go │ │ ├── interfaces.go │ │ ├── scope.go │ │ ├── sentry.go │ │ ├── sourcereader.go │ │ ├── stacktrace.go │ │ ├── transport.go │ │ └── util.go ├── pmezard │ └── go-difflib │ │ ├── LICENSE │ │ └── difflib │ │ └── difflib.go └── stretchr │ └── testify │ ├── LICENSE │ ├── assert │ ├── assertion_format.go │ ├── assertion_format.go.tmpl │ ├── assertion_forward.go │ ├── assertion_forward.go.tmpl │ ├── assertion_order.go │ ├── assertions.go │ ├── doc.go │ ├── errors.go │ ├── forward_assertions.go │ └── http_assertions.go │ └── require │ ├── doc.go │ ├── forward_requirements.go │ ├── require.go │ ├── require.go.tmpl │ ├── require_forward.go │ ├── require_forward.go.tmpl │ └── requirements.go ├── gopkg.in └── yaml.v2 │ ├── .travis.yml │ ├── LICENSE │ ├── LICENSE.libyaml │ ├── NOTICE │ ├── README.md │ ├── apic.go │ ├── decode.go │ ├── emitterc.go │ ├── encode.go │ ├── go.mod │ ├── parserc.go │ ├── readerc.go │ ├── resolve.go │ ├── scannerc.go │ ├── sorter.go │ ├── writerc.go │ ├── yaml.go │ ├── yamlh.go │ └── yamlprivateh.go ├── launchpad.net └── gnuflag │ ├── LICENSE │ └── flag.go └── modules.txt /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### Summary 2 | 3 | 4 | 5 | #### Motivation 6 | 7 | 8 | 9 | #### Test plan 10 | 11 | 12 | 13 | #### Rollout/monitoring/revert plan 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.test 2 | /unilog/unilog 3 | .vscode 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2011- Stripe, Inc. (https://stripe.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## unilog -- a logger process for daemontools [![Build Status](https://travis-ci.org/stripe/unilog.svg?branch=master)](https://travis-ci.org/stripe/unilog) 2 | 3 | unilog is a tool for use with djb's [daemontools][daemontools] 4 | process-monitor. 5 | 6 | The way logging works with daemontools is that all output from a 7 | daemontools-controlled process runs into a pipe, which is passed to a 8 | second program, which can read from the pipe and format log output, 9 | write it to disk or whatever. Daemontools ships with such a tool, 10 | called ["multilog"][multilog], but it is arcane and weird, so we use 11 | this one. 12 | 13 | The job of unilog is to read lines of log output from stdin (which 14 | will normally be connected to a running daemon by way of a pipe), 15 | format them (which normally consists just of adding a timestamp), and 16 | write them to a log file provided on the command-line. 17 | 18 | If unilog receives a `SIGHUP` or `SIGALRM`, it responds by closing and 19 | reopening the output file. This can be used to perform graceful log 20 | rotation without requiring any special support from the running 21 | daemon. 22 | 23 | If unilog is unable to open or write to the output file, it will email 24 | about this error, once per hour, until it succeeds in a write, 25 | discarding output in the process. 26 | 27 | In addition to the pipe buffer, unilog maintains an in-process buffer 28 | of log lines that it will fill up if writes to the disk are slow or 29 | blocking. This is intended to prevent the kernel pipe buffers from 30 | filling up (and thus blocking the daemon from writing) during brief 31 | periods of disk overload or hangs (sadly common in virtualized 32 | environments). 33 | 34 | ### Filters 35 | 36 | Unilog can be configured to apply filters to each line and perform arbitrary transformations. (For example, you may want to strip out sensitive information, or strip high-volume logs). 37 | 38 | ### Criticality and Austerity 39 | 40 | Unilog contains an optional system for managing log volume, using criticality and austerity levels. If this systems is enabled, every log line has a **criticality level** associated with it. There are four levels of log criticality. In ascending order of importance, they are: `sheddable`, `sheddableplus` (default), `critical`, and `criticalplus`. (These names are taken from [Site Reliability Engineering, How Google Runs Production Systems](https://landing.google.com/sre/book.html).) 41 | 42 | During times of high log volume, log lines may be sampled at exponential rates. The criticality level (clevel) of a log line determines its relative priority when sampling. By default, the system **austerity level** is set to `sheddable`, which means that all lines are preserved. If the austerity level is raised to `sheddableplus`, then only 10% of lines logged at `sheddable` are preserved, and the rest are filtered. If the austerity level is raised to `critical`, then 10% of lines logged at `clevel=sheddableplus` are preserved, and 1% of lines logged at `clevel=sheddable` are preserved, and so forth. 43 | 44 | Criticality levels operate using filters, so this system is not just limited to sampling logs to reduce volume - it can be used to apply arbitrary transformations to a random subset of log lines. 45 | 46 | [daemontools]: http://cr.yp.to/daemontools.html 47 | [multilog]: http://cr.yp.to/daemontools/multilog.html 48 | [googlsre]: https://landing.google.com/sre/book.html 49 | -------------------------------------------------------------------------------- /clevels/austerity.go: -------------------------------------------------------------------------------- 1 | package clevels 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "regexp" 9 | "strings" 10 | "time" 11 | 12 | "github.com/DataDog/datadog-go/statsd" 13 | "github.com/stripe/unilog/json" 14 | ) 15 | 16 | var Stats *statsd.Client 17 | 18 | //go:generate stringer -type=AusterityLevel 19 | type AusterityLevel int 20 | 21 | // There are four levels of logs. The names and definitions are adapted 22 | // from Google's usage. 23 | // 24 | // For more information, see Chapter 21 of Site Reliability Engineering 25 | // by Beyer, Jones, Petoff, and Murphy 26 | // 27 | // Sheddable is the least-important, and we can expect frequent partial unavailability 28 | // of Sheddable logs as well as occassional full unavailability without 29 | // disruption. 30 | // 31 | // SheddablePlus is the default level, and we can expect occasional partial availability 32 | // and rare full unavailability. 33 | // 34 | // Critical is used for lines for which partial unavailability is rare. 35 | // 36 | // CriticalPlus is used for lines which should only be unavailable 37 | // in the most extreme circumstances. 38 | const ( 39 | Sheddable AusterityLevel = iota 40 | SheddablePlus 41 | Critical 42 | CriticalPlus 43 | ) 44 | 45 | // Define two helper constants here. Since our default policies may change 46 | // later, this means we only have to change the definitions at one point 47 | 48 | // The default criticality line for any log line 49 | // that doesn't have an explicit clevel tag 50 | const DefaultCriticality AusterityLevel = SheddablePlus 51 | 52 | // The default system austerity level, in case 53 | // we are unable to determine it from the state file 54 | const DefaultAusterity AusterityLevel = Sheddable 55 | 56 | func (a AusterityLevel) AusterityLevel() string { 57 | return "" 58 | } 59 | 60 | // AusterityBuffer ensures that high-volume services will never block 61 | // when trying to read the system austerity level, even if the process 62 | // that updates the cache is slow. The tradeoff is that, when the system 63 | // austerity level is changed, it will read an extra $AusterityBuffer log 64 | // lines before the change takes effect. 65 | const AusterityBuffer = 100 66 | 67 | var CacheInterval = 30 * time.Second 68 | 69 | // To determine the current austerity level, read from SystemAusterityLevel. 70 | // Austerity is updated according to CacheInterval, 71 | // and this channel is buffered (size AusterityBuffer) to avoid 72 | // blocking a critical code path. 73 | // As a result, services which have a low log volume will take longer 74 | // to have their austerity level changes come into effect. 75 | var SystemAusterityLevel = make(chan AusterityLevel, AusterityBuffer) 76 | 77 | // AusterityFile is the full path to a file that contains the current 78 | // system austerity level. 79 | var AusterityFile string 80 | 81 | var InvalidAusterityLevel = errors.New("Invalid austerity level") 82 | 83 | // LoadLevel loads the AusterityFile and parses it to determine 84 | // the system austerity level. If it encounters an error, it will 85 | // return the DefaultAusterity. 86 | func LoadLevel() (AusterityLevel, error) { 87 | f, err := os.Open(AusterityFile) 88 | if err != nil { 89 | return DefaultAusterity, err 90 | } 91 | defer f.Close() 92 | level, err := ParseLevel(f) 93 | 94 | return level, err 95 | } 96 | 97 | var canonicalRegex = regexp.MustCompile(`(?i)CANONICAL-[-\w]+?-LINE`) 98 | var cLevelRegex = regexp.MustCompile(`\[clevel: (\w*?)\]`) 99 | var cLevelChalkRegex = regexp.MustCompile(`\sclevel=(\w+?)\b`) 100 | 101 | var clevelRegexes = []*regexp.Regexp{cLevelRegex, cLevelChalkRegex} 102 | 103 | // criticality parses the criticality level 104 | // of a log line. Defaults to the value of DefaultCriticality. 105 | func Criticality(line string) AusterityLevel { 106 | // Things like CANONICAL-API-LINE should never be dropped 107 | if canonicalRegex.MatchString(line) { 108 | return CriticalPlus 109 | } 110 | 111 | for _, r := range clevelRegexes { 112 | matches := r.FindStringSubmatch(line) 113 | if len(matches) < 2 { 114 | continue 115 | } 116 | 117 | level, err := ParseLevel(strings.NewReader(matches[1])) 118 | // we don't really care about any error here 119 | // and want to default to not dropping anything 120 | if err != nil { 121 | continue 122 | } 123 | // if we've made it this far, we've succesfully parsed 124 | // the clevel 125 | return level 126 | } 127 | 128 | return DefaultCriticality 129 | } 130 | 131 | func JSONCriticality(line json.LogLine) AusterityLevel { 132 | // Never drop JSON log lines declaring themselves canonical: 133 | if canonicalI, ok := line["canonical"]; ok { 134 | if canonical, ok := canonicalI.(bool); ok && canonical { 135 | return CriticalPlus 136 | } 137 | } 138 | 139 | if clevelI, ok := line["clevel"]; ok { 140 | if clevel, ok := clevelI.(string); ok { 141 | level, err := ParseLevel(strings.NewReader(clevel)) 142 | if err == nil { 143 | return DefaultCriticality 144 | } 145 | return level 146 | } 147 | } 148 | return DefaultCriticality 149 | } 150 | 151 | func ParseLevel(r io.Reader) (AusterityLevel, error) { 152 | bts, err := ioutil.ReadAll(r) 153 | if err != nil { 154 | return DefaultAusterity, err 155 | } 156 | 157 | level := strings.ToLower(strings.TrimSpace(string(bts))) 158 | switch level { 159 | case strings.ToLower(Sheddable.String()): 160 | return Sheddable, nil 161 | case strings.ToLower(SheddablePlus.String()): 162 | return SheddablePlus, nil 163 | case strings.ToLower(Critical.String()): 164 | return Critical, nil 165 | case strings.ToLower(CriticalPlus.String()): 166 | return CriticalPlus, nil 167 | } 168 | return DefaultAusterity, InvalidAusterityLevel 169 | } 170 | 171 | func reportLoadStatus(err error) { 172 | if Stats != nil { 173 | metric := 0 174 | if err != nil { 175 | metric = 1 176 | } 177 | Stats.Count("unilog.errors.load_level", int64(metric), nil, 1) 178 | } 179 | } 180 | 181 | func SendSystemAusterityLevel() { 182 | // This is the cached austerity level 183 | // that will be sent anytime Unilog requests the austerity level, 184 | // so that there is never any delay. 185 | // By default, there is no austerity. 186 | var currentLevel = Sheddable 187 | 188 | // shadow this variable so we can override it in tests, if needed. 189 | // multiple goroutines write to SystemAusterityLevel in tests, so this shadowing 190 | // gives us the ability to separate the channels in tests. 191 | var _SystemAusterityLevel = SystemAusterityLevel 192 | 193 | newLevelCh := make(chan AusterityLevel) 194 | go func(updatedLevel chan<- AusterityLevel) { 195 | c := time.Tick(CacheInterval) 196 | for _ = range c { 197 | l, err := LoadLevel() 198 | reportLoadStatus(err) 199 | if err != nil { 200 | continue 201 | } 202 | updatedLevel <- l 203 | } 204 | }(newLevelCh) 205 | 206 | // Loop forever on this 207 | for { 208 | // This select statement will be on a hotpath 209 | // so it should never block for long 210 | select { 211 | // Continuously send the current austerity level 212 | // to whoever asks for it 213 | case _SystemAusterityLevel <- currentLevel: 214 | 215 | case newLevel := <-newLevelCh: 216 | go ReportAusterity(newLevel) 217 | currentLevel = newLevel 218 | } 219 | } 220 | } 221 | 222 | func ReportAusterity(l AusterityLevel) { 223 | if Stats != nil { 224 | Stats.Gauge("unilog.austerity.box", float64(l), nil, 1) 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /clevels/austerity_test.go: -------------------------------------------------------------------------------- 1 | package clevels 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "strings" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestParseAusterityLevel(t *testing.T) { 14 | type ParseAusterityTestCase struct { 15 | Contents string 16 | Expected AusterityLevel 17 | ExpectedError error 18 | } 19 | 20 | cases := []ParseAusterityTestCase{ 21 | { 22 | Contents: "Sheddable", 23 | Expected: Sheddable, 24 | }, 25 | { 26 | Contents: "SheddablePlus", 27 | Expected: SheddablePlus, 28 | }, 29 | { 30 | Contents: "Critical", 31 | Expected: Critical, 32 | }, 33 | { 34 | Contents: "CriticalPlus", 35 | Expected: CriticalPlus, 36 | }, 37 | { 38 | Contents: "InvalidAusterity", 39 | Expected: Sheddable, 40 | ExpectedError: InvalidAusterityLevel, 41 | }, 42 | { 43 | // test case-insensitivity 44 | Contents: "sHedDaBlePlUs", 45 | Expected: SheddablePlus, 46 | }, 47 | } 48 | 49 | for _, tc := range cases { 50 | t.Run(tc.Contents, func(t *testing.T) { 51 | l, err := ParseLevel(strings.NewReader(tc.Contents)) 52 | if tc.ExpectedError == nil { 53 | assert.NoError(t, err) 54 | } else { 55 | assert.Equal(t, err, InvalidAusterityLevel) 56 | } 57 | assert.Equal(t, tc.Expected, l) 58 | }) 59 | } 60 | } 61 | 62 | // Test that SendAusterityLevel still works properly 63 | // if loading the file returns an error 64 | func TestLoadLevelError(t *testing.T) { 65 | // For tests, we don't actually want to implement a delay 66 | // Since both channels will always be ready to send, 67 | // the scheduler will choose pseudorandomly between the two 68 | CacheInterval = 0 * time.Millisecond 69 | 70 | // Assert that the file doesn't actually exist, so it should return an error 71 | // of type *os.PathError 72 | l, err := LoadLevel() 73 | 74 | // Check the default austerity level 75 | // For now, if we encountered an error, we should default 76 | // to the lowest possible level. 77 | // This may change at some point later. 78 | assert.Equal(t, Sheddable, l) 79 | assert.Error(t, err) 80 | assert.IsType(t, &os.PathError{}, err) 81 | 82 | go SendSystemAusterityLevel() 83 | 84 | for i := 0; i < 10; i++ { 85 | lvl := <-SystemAusterityLevel 86 | 87 | assert.Equal(t, Sheddable, lvl) 88 | } 89 | } 90 | 91 | func TestCriticality(t *testing.T) { 92 | type CriticalityTestCase struct { 93 | name string 94 | line string 95 | level AusterityLevel 96 | } 97 | cases := []CriticalityTestCase{ 98 | { 99 | name: "CANONICAL-API-LINE", 100 | // actual CANONICAL-API-LINE, with explicit merchant-identifying tokens removed 101 | line: `[2016-11-10 19:18:05.844100] [98381|f1.northwest-1.apiori.com/EzBDuA4iNq-2631925524 85137cc252d87354>e9b8c49860f01f15] CANONICAL-API-LINE: api_method=AccountRetrieveMethod content_type="application/x-www-form-urlencoded" created=1478805073.5253563 http_method=GET ip="54.xxx.xxx.xxx" path="/v1/accounts/acct_xxxxxxxxxxxxxxxx" user_agent="Stripe/v1 RubyBindings/1.31.0" request_id=req_xxxxxxxxxxxxxx response_stripe_version="2016-03-07" status=200 merchant=acct_xxxxxxxxxxxxx`, 102 | level: CriticalPlus, 103 | }, 104 | { 105 | name: "CANONICAL-OTHER-CRITICAL-LINE", 106 | // actual CANONICAL-API-LINE, with explicit merchant-identifying tokens removed 107 | line: `[2016-11-10 19:18:05.844100] [98381|f1.northwest-1.apiori.com/EzBDuA4iNq-2631925524 85137cc252d87354>e9b8c49860f01f15] CANONICAL-OTHER-CRITICAL-LINE: api_method=AccountRetrieveMethod content_type="application/x-www-form-urlencoded" created=1478805073.5253563 http_method=GET ip="54.xxx.xxx.xxx" path="/v1/accounts/acct_xxxxxxxxxxxxxxxx" user_agent="Stripe/v1 RubyBindings/1.31.0" request_id=req_xxxxxxxxxxxxxx response_stripe_version="2016-03-07" status=200 merchant=acct_xxxxxxxxxxxxx`, 108 | level: CriticalPlus, 109 | }, 110 | { 111 | name: "CANONICAL-API-LINE", 112 | // actual CANONICAL-API-LINE, with explicit merchant-identifying tokens removed 113 | // this should be criticalplus, despite clevel=sheddable being set 114 | line: `[2016-11-10 19:18:05.844100] [98381|f1.northwest-1.apiori.com/EzBDuA4iNq-2631925524 85137cc252d87354>e9b8c49860f01f15] CANONICAL-API-LINE: api_method=AccountRetrieveMethod content_type="application/x-www-form-urlencoded" created=1478805073.5253563 http_method=GET ip="54.xxx.xxx.xxx" path="/v1/accounts/acct_xxxxxxxxxxxxxxxx" user_agent="Stripe/v1 RubyBindings/1.31.0" request_id=req_xxxxxxxxxxxxxx response_stripe_version="2016-03-07" status=200 merchant=acct_xxxxxxxxxxxxx clevel=sheddable`, 115 | level: CriticalPlus, 116 | }, 117 | { 118 | name: "CANONICAL-ADMIN-LINE", 119 | // actual CANONICAL-ADMIN-LINE, with user-identifying tokens removed 120 | line: `[2016-11-10 19:10:49.230930] [22560|adminbox--04ec81f3361370d7f.northwest.stripe.io/kUku-rmgfZ-349 0000000000000000>a020f53ed1dd83ef] CANONICAL-ADMIN-LINE: path="/fonts/glyphicons-halflings-regular.woff" http_method=GET referer="/css/bootstrap3.min.css" response_content_type="application/octet-stream" status=200`, 121 | level: CriticalPlus, 122 | }, 123 | { 124 | name: "canonical-monster-line", 125 | // test for case-insensitivity 126 | line: `[2016-11-10 19:10:49.230930] [22560|adminbox--04ec81f3361370d7f.northwest.stripe.io/kUku-rmgfZ-349 0000000000000000>a020f53ed1dd83ef] canonical-monster-line: path="/fonts/glyphicons-halflings-regular.woff" http_method=GET referer="/css/bootstrap3.min.css" response_content_type="application/octet-stream" status=200`, 127 | level: CriticalPlus, 128 | }, 129 | { 130 | name: "PlainOldLogLine", 131 | line: `[2016-11-10 19:01:02.461489] [21515|adminbox--04ec81f3361370d7f.northwest.stripe.io/kUku-wvrZK-28 0000000000000000>93e612b5bd9b69eb] HTTP response headers: Content-Type="text/html;charset=utf-8" Content-Length="10879" Set`, 132 | level: SheddablePlus, 133 | }, 134 | { 135 | name: "PlainOldLogLineWithClevelUppercase", 136 | line: `[2016-11-10 19:01:02.461489] [21515|adminbox--04ec81f3361370d7f.northwest.stripe.io/kUku-wvrZK-28 0000000000000000>93e612b5bd9b69eb] HTTP response headers: Content-Type="text/html;charset=utf-8" Content-Length="10879" Set [clevel: Critical]`, 137 | level: Critical, 138 | }, 139 | { 140 | name: "PlainOldLogLineWithClevelLowercase", 141 | line: `[2016-11-10 19:01:02.461489] [21515|adminbox--04ec81f3361370d7f.northwest.stripe.io/kUku-wvrZK-28 0000000000000000>93e612b5bd9b69eb] HTTP response headers: Content-Type="text/html;charset=utf-8" Content-Length="10879" Set [clevel: critical]`, 142 | level: Critical, 143 | }, 144 | { 145 | name: "LogLineWithClevelChalk", 146 | line: `[2016-11-10 20:02:01.932272] [24607|adminbox--04ec81f3361370d7f.northwest.stripe.io/kUku-WdiJA-3204 0000000000000000>831e61790017a475] Showed info for merchant: merchant=acct_xxxxxxxxxxxxxxxxx tier=tier0 clevel=criticalplus`, 147 | level: CriticalPlus, 148 | }, 149 | } 150 | 151 | for i, tc := range cases { 152 | name := strconv.Itoa(i) 153 | if tc.name != "" { 154 | name = tc.name 155 | } 156 | t.Run(name, func(t *testing.T) { 157 | l := Criticality(tc.line) 158 | assert.Equal(t, tc.level.String(), l.String()) 159 | }) 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /clevels/austeritylevel_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=AusterityLevel"; DO NOT EDIT 2 | 3 | package clevels 4 | 5 | import "fmt" 6 | 7 | const _AusterityLevel_name = "SheddableSheddablePlusCriticalCriticalPlus" 8 | 9 | var _AusterityLevel_index = [...]uint8{0, 9, 22, 30, 42} 10 | 11 | func (i AusterityLevel) String() string { 12 | if i < 0 || i >= AusterityLevel(len(_AusterityLevel_index)-1) { 13 | return fmt.Sprintf("AusterityLevel(%d)", i) 14 | } 15 | return _AusterityLevel_name[_AusterityLevel_index[i]:_AusterityLevel_index[i+1]] 16 | } 17 | -------------------------------------------------------------------------------- /clevels/doc.go: -------------------------------------------------------------------------------- 1 | // Package clevels includes utilities for identifying log criticality and system austerity, which 2 | // can be used to sample logs. 3 | package clevels 4 | -------------------------------------------------------------------------------- /filters/austerity.go: -------------------------------------------------------------------------------- 1 | package filters 2 | 3 | import ( 4 | "math" 5 | "math/rand" 6 | "sync" 7 | 8 | "github.com/stripe/unilog/clevels" 9 | "github.com/stripe/unilog/json" 10 | ) 11 | 12 | var startSystemAusterityLevel sync.Once 13 | 14 | // AusterityFilter applies system-wide austerity levels to a log 15 | // line. If austerity levels indicate a line should be shedded, the 16 | // like's contents will replaced with "(shedded)" in text mode and the 17 | // "shedded"=true attribude in JSON mode. 18 | // 19 | // Shedding log lines retains their time stamps. 20 | type AusterityFilter struct{} 21 | 22 | // AusteritySetup starts the parser for the system austerity level. It is 23 | // exported so that tests can call it with testing=true in test setup, 24 | // which will disable sending the austerity level (and also appease 25 | // the race detector). 26 | func AusteritySetup(testing bool) { 27 | // Start austerity level loop sender in goroutine just once 28 | startSystemAusterityLevel.Do(func() { 29 | if !testing { 30 | go clevels.SendSystemAusterityLevel() 31 | } 32 | }) 33 | } 34 | 35 | // FilterLine applies shedding to a text event 36 | func (a *AusterityFilter) FilterLine(line string) string { 37 | AusteritySetup(false) 38 | if ShouldShed(clevels.Criticality(line)) { 39 | return "(shedded)" 40 | } 41 | return line 42 | } 43 | 44 | // FilterJSON applies shedding to a JSON event 45 | func (a *AusterityFilter) FilterJSON(line *json.LogLine) { 46 | AusteritySetup(false) 47 | if ShouldShed(clevels.JSONCriticality(*line)) { 48 | // clear the line: 49 | newLine := map[string]interface{}{} 50 | if ts, ok := (*line)["ts"]; ok { 51 | newLine["ts"] = ts 52 | } 53 | newLine["shedded"] = true 54 | *line = newLine 55 | } 56 | } 57 | 58 | // ShouldShed returns true if the given criticalityLevel indicates a log 59 | // should be shed, according to the system austerity level 60 | func ShouldShed(criticalityLevel clevels.AusterityLevel) bool { 61 | austerityLevel := <-clevels.SystemAusterityLevel 62 | if criticalityLevel >= austerityLevel { 63 | return false 64 | } 65 | 66 | return rand.Float64() > samplingRate(austerityLevel, criticalityLevel) 67 | } 68 | 69 | // samplingRate calculates the rate at which loglines will be sampled for the 70 | // given criticality level and austerity level. For example, if the austerity level 71 | // is Critical (3), then lines that are Sheddable (0) will be sampled at .001. 72 | func samplingRate(austerityLevel, criticalityLevel clevels.AusterityLevel) float64 { 73 | if criticalityLevel > austerityLevel { 74 | return 1 75 | } 76 | 77 | levelDiff := austerityLevel - criticalityLevel 78 | samplingRate := math.Pow(10, float64(-levelDiff)) 79 | 80 | return samplingRate 81 | } 82 | -------------------------------------------------------------------------------- /filters/austerity_test.go: -------------------------------------------------------------------------------- 1 | package filters 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stripe/unilog/clevels" 11 | "github.com/stripe/unilog/json" 12 | ) 13 | 14 | func TestCalculateSamplingRate(t *testing.T) { 15 | type CalculateSamplingLevel struct { 16 | Name string 17 | Austerity clevels.AusterityLevel 18 | Criticality clevels.AusterityLevel 19 | Expected float64 20 | } 21 | 22 | cases := []CalculateSamplingLevel{ 23 | { 24 | Name: "log level higher than austerity", 25 | Austerity: clevels.Sheddable, 26 | Criticality: clevels.SheddablePlus, 27 | Expected: 1.0, 28 | }, 29 | { 30 | Name: "log level one lower than austerity", 31 | Austerity: clevels.Critical, 32 | Criticality: clevels.SheddablePlus, 33 | Expected: 0.1, 34 | }, 35 | { 36 | Name: "log level two lower than austerity", 37 | Austerity: clevels.Critical, 38 | Criticality: clevels.Sheddable, 39 | Expected: 0.01, 40 | }, 41 | { 42 | Name: "log level three lower than austerity", 43 | Austerity: clevels.CriticalPlus, 44 | Criticality: clevels.Sheddable, 45 | Expected: 0.001, 46 | }, 47 | } 48 | 49 | for _, tc := range cases { 50 | t.Run(tc.Name, func(t *testing.T) { 51 | samplingRate := samplingRate(tc.Austerity, tc.Criticality) 52 | assert.Equal(t, tc.Expected, samplingRate) 53 | }) 54 | } 55 | } 56 | 57 | func TestAusterityFilter(t *testing.T) { 58 | // Make sure SendSystemAusterityLevel is called before we override 59 | // the underlying channel below 60 | a := AusterityFilter{} 61 | AusteritySetup(true) 62 | 63 | clevels.SystemAusterityLevel = make(chan clevels.AusterityLevel) 64 | 65 | line := fmt.Sprintf("some random log line! clevel=%s", clevels.SheddablePlus) 66 | 67 | kill := make(chan struct{}) 68 | 69 | go func() { 70 | for { 71 | select { 72 | case clevels.SystemAusterityLevel <- clevels.Critical: 73 | case <-kill: 74 | return 75 | } 76 | } 77 | }() 78 | 79 | // seed rand deterministically 80 | rand.Seed(17) 81 | 82 | // count number of lines dropped 83 | dropped := 0 84 | var outputtedLine string 85 | 86 | // now sample out the line a bunch! 87 | for i := 0; i < 10000; i++ { 88 | outputtedLine = a.FilterLine(line) 89 | if strings.Contains(outputtedLine, "(shedded)") { 90 | dropped++ 91 | } 92 | } 93 | 94 | // this number is deterministic because rand is seeded & deterministic 95 | // TODO (kiran, 2016-12-06): maybe add an epsilon 96 | assert.Equal(t, 8983, dropped) 97 | kill <- struct{}{} 98 | } 99 | 100 | func TestAusterityJSON(t *testing.T) { 101 | // Make sure SendSystemAusterityLevel is called before we override 102 | // the underlying channel below 103 | a := AusterityFilter{} 104 | AusteritySetup(true) 105 | clevels.SystemAusterityLevel = make(chan clevels.AusterityLevel) 106 | kill := make(chan struct{}) 107 | 108 | go func() { 109 | for { 110 | select { 111 | case clevels.SystemAusterityLevel <- clevels.Critical: 112 | case <-kill: 113 | return 114 | } 115 | } 116 | }() 117 | 118 | // seed rand deterministically 119 | rand.Seed(17) 120 | 121 | // count number of lines dropped 122 | dropped := 0 123 | 124 | // now sample out the line a bunch! 125 | for i := 0; i < 10000; i++ { 126 | line := json.LogLine{"message": "some random log line!", "clevel": "sheddableplus"} 127 | a.FilterJSON(&line) 128 | if _, ok := line["message"]; !ok { 129 | dropped++ 130 | } 131 | } 132 | 133 | // this number is deterministic because rand is seeded & deterministic 134 | assert.Equal(t, 8983, dropped) 135 | kill <- struct{}{} 136 | } 137 | -------------------------------------------------------------------------------- /filters/doc.go: -------------------------------------------------------------------------------- 1 | // Package filters contains implementations of unilog filters. 2 | package filters 3 | -------------------------------------------------------------------------------- /filters/timeprefix.go: -------------------------------------------------------------------------------- 1 | package filters 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/stripe/unilog/json" 8 | flag "launchpad.net/gnuflag" 9 | ) 10 | 11 | const defaultFormat = "2006-01-02 15:04:05.000000" 12 | 13 | // TimePrefixFilter prepends a timestamp onto each event line using the specified 14 | // format string, plus an optional newline. 15 | type TimePrefixFilter struct { 16 | Omit bool 17 | Format string 18 | } 19 | 20 | // FilterLine prepends the current time, in square brackets with a separating 21 | // space, to the provided log line. 22 | func (f *TimePrefixFilter) FilterLine(line string) string { 23 | if f.Omit { 24 | return line 25 | } 26 | return fmt.Sprintf("[%s] %s", time.Now().Format(f.getTimeFormat()), line) 27 | } 28 | 29 | // FilterJSON is a no-op - TimePrefixFilter does nothing on JSON logs (for now!). 30 | func (f *TimePrefixFilter) FilterJSON(line *json.LogLine) {} 31 | 32 | func (f *TimePrefixFilter) getTimeFormat() string { 33 | if f.Format != "" { 34 | return f.Format 35 | } 36 | 37 | return defaultFormat 38 | } 39 | 40 | // AddFlags adds time-prefix related flags to the CLI options 41 | func (f *TimePrefixFilter) AddFlags() { 42 | flag.BoolVar(&f.Omit, "omit-timestamps", false, "Do not prepend timestamps to each line before flushing.") 43 | } 44 | -------------------------------------------------------------------------------- /filters/timeprefix_test.go: -------------------------------------------------------------------------------- 1 | package filters 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stripe/unilog/json" 10 | ) 11 | 12 | var low time.Time = time.Now() 13 | 14 | func TestTimePrefixLine(t *testing.T) { 15 | f := TimePrefixFilter{} 16 | str := f.FilterLine("") 17 | f.Format = time.UnixDate 18 | str2 := f.FilterLine("") 19 | 20 | between(t, str[1:27], defaultFormat, low, time.Now()) 21 | between(t, str2[1:29], time.UnixDate, low, time.Now()) 22 | 23 | f.Omit = true 24 | assert.Equal(t, "", f.FilterLine(""), "Empty input should have empty output when f.Omit == true, got %q", f.FilterLine("")) 25 | } 26 | 27 | func TestTimePrefixJSON(t *testing.T) { 28 | f := TimePrefixFilter{} 29 | m := json.LogLine(map[string]interface{}{}) 30 | f.FilterJSON(&m) 31 | 32 | assert.Equal(t, len(m), 0, "JSON filter should make no map modifications") 33 | } 34 | 35 | func between(t *testing.T, check string, format string, bottom, high time.Time) { 36 | if h, ok := interface{}(t).(interface { 37 | Helper() 38 | }); ok { 39 | h.Helper() 40 | } 41 | 42 | c, err := time.Parse(format, check) 43 | 44 | if err != nil { 45 | t.Fatal(fmt.Sprintf("Input string was not a parseable timestamp: %s", check)) 46 | } 47 | 48 | // Converting the timestamps to strings and back - as inherently happens 49 | // when the filter prints them in the log - seems to introduce a stable 50 | // inaccuracy specific to the format. So, convert our low and high bounds to 51 | // and back from strings, then check bounds based on that. 52 | lstr, hstr := bottom.Format(format), high.Format(format) 53 | plow, _ := time.Parse(format, lstr) 54 | phigh, _ := time.Parse(format, hstr) 55 | 56 | assert.True(t, (c.After(plow) || c.Equal(plow)), "Input (%q) was below lower bound (%q) by %dns", check, plow.Format(format), plow.Sub(c)) 57 | assert.True(t, (phigh.After(c) || c.Equal(phigh)), "Input (%q) was above upper bound (%q) by %dns", check, phigh.Format(format), c.Sub(phigh)) 58 | } 59 | -------------------------------------------------------------------------------- /generate_input.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # This script will generate input at different clevels 3 | # to help test behavior locally. 4 | 5 | set -e 6 | 7 | 8 | function dumpText(){ 9 | echo "this is a sheddable clevel=sheddable" 10 | echo "this is a sheddableplus clevel=sheddableplus" 11 | echo "this is a critical clevel=critical" 12 | echo "this is a criticalplus clevel=criticalplus" 13 | echo "this is an ERROR clevel=sleddable" 14 | echo "this is a default (sheddableplus)" 15 | } 16 | 17 | 18 | 19 | while true; do 20 | dumpText 21 | sleep 2 22 | done 23 | 24 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/stripe/unilog 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/DataDog/datadog-go v0.0.0-20160822161430-909c02b65dd8 7 | github.com/getsentry/sentry-go v0.6.1 8 | github.com/stretchr/testify v1.4.0 9 | launchpad.net/gnuflag v0.0.0-20150127164241-000000000014 10 | ) 11 | -------------------------------------------------------------------------------- /json/json.go: -------------------------------------------------------------------------------- 1 | // Package json provides a type and helpers for representing JSON log 2 | // lines. 3 | // 4 | // Format 5 | // 6 | // The JSON log line format consists of JSON objects, one per 7 | // \n-terminated line. 8 | // 9 | // Unilog recognizes three fields in that object that are considered 10 | // special (all are optional): 11 | // 12 | // - timestamp: The time stamp of an event. Unilog understands both 13 | // epoch timestamps as float, or RFC3339Nano-formatted 14 | // timestamps. The timestamp will be normalized to 15 | // nanosecond-resolution float "timestamp" fields. 16 | // - canonical: Identifies the log event as "canonical", i.e. the 17 | // most important line a service can log. It is considered to have 18 | // the highest criticality level. 19 | // - clevel: The criticality level of the event. 20 | // 21 | // Example 22 | // 23 | // {"timestamp":"2006-01-02T15:04:05.999Z07:00","message":"hi there"} 24 | // {"timestamp":"2006-01-02T15:04:05.999Z07:00","message":"hi there"} 25 | package json 26 | 27 | import ( 28 | "bytes" 29 | "encoding/json" 30 | "fmt" 31 | "time" 32 | ) 33 | 34 | // JSONLogLine is a representation of a generic log line that unilog 35 | // can destructure. 36 | type LogLine map[string]interface{} 37 | 38 | const timestampField = "timestamp" 39 | 40 | var tsFields = []string{ 41 | timestampField, 42 | "ts", 43 | } 44 | 45 | // Timestamp returns the timestamp of a log line; if a timestamp is 46 | // set on the line, Timestamp will attempt to interpret it 47 | // (integers/floats as UNIX epochs with fractional sub-second 48 | // components, and strings first according to time.RFC3339Nano and 49 | // then time.RFC1123Z). If no timestamp is present, or the present 50 | // time stamp can not be parsed, Timestamp returns the current time. 51 | func (j *LogLine) Timestamp() time.Time { 52 | for _, tsField := range tsFields { 53 | if tsS, ok := (*j)[tsField]; ok { 54 | // We support two different kinds of 55 | // timestamps here: UNIX epoch timestamps as 56 | // floats, and RFC3339Nano for strings: 57 | switch tsV := tsS.(type) { 58 | case string: 59 | ts, err := time.Parse(time.RFC3339Nano, tsV) 60 | if err == nil { 61 | return ts 62 | } 63 | ts, err = time.Parse(time.RFC1123Z, tsV) 64 | if err == nil { 65 | return ts 66 | } 67 | case float64: 68 | epochInt := int64(tsV) 69 | nsec := int64((tsV - float64(epochInt)) * 1000000000) 70 | return time.Unix(epochInt, nsec) 71 | default: 72 | return time.Now() 73 | } 74 | } 75 | } 76 | return time.Now() 77 | } 78 | 79 | // Holds the starting `{`, timestamp field name and field separator 80 | // prefix for the timestamp value. 81 | var encodePrefix []byte 82 | 83 | func init() { 84 | encodePrefix = []byte(fmt.Sprintf(`{"%s":`, timestampField)) 85 | } 86 | 87 | // MarshalJSON writes the log line in a specific format that's 88 | // optimized for splunk ingestion: First, it writes the timestamp as a 89 | // float UNIX epoch, followed by all the other fields. 90 | func (j LogLine) MarshalJSON() ([]byte, error) { 91 | b := bytes.NewBuffer(encodePrefix) 92 | b.Grow(len(j) * 15) // very naive assumption: average key/value pair is 15 bytes long. 93 | 94 | nsepoch := j.Timestamp().UnixNano() 95 | sec := time.Duration(nsepoch) / time.Second 96 | usec := (time.Duration(nsepoch) - (sec * time.Second)) / time.Nanosecond 97 | fmt.Fprintf(b, "%d.%09d", sec, usec) 98 | 99 | for k, v := range j { 100 | if k == timestampField { 101 | continue 102 | } 103 | b.WriteString(",") 104 | kJSON, _ := json.Marshal(k) 105 | vJSON, err := json.Marshal(v) 106 | if err != nil { 107 | vJSON, _ = json.Marshal(fmt.Sprintf(`[unilog json marshal error: %v]`, err)) 108 | } 109 | b.Write(kJSON) 110 | b.WriteString(":") 111 | b.Write(vJSON) 112 | } 113 | b.WriteString("}") 114 | return b.Bytes(), nil 115 | } 116 | -------------------------------------------------------------------------------- /json/json_test.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strings" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestTS(t *testing.T) { 15 | tests := []struct { 16 | inputTS string 17 | now bool 18 | epochS int64 19 | epochNS int64 20 | }{ 21 | {`"2006-01-02T15:04:05.999999999Z"`, false, 1136214245, 999999999}, 22 | {`"2006-01-02T15:04:05Z"`, false, 1136214245, 0}, 23 | {`"Mon, 02 Jan 2006 15:04:05 -0700"`, false, 1136239445, 0}, 24 | {`"gibberish"`, true, 0, 0}, 25 | {`1550493962.283873`, false, 1550493962, 283873010}, 26 | {`1550493962`, false, 1550493962, 0}, 27 | } 28 | nowish := time.Now() 29 | for _, elt := range tests { 30 | test := elt 31 | name := fmt.Sprintf("%v", elt.inputTS) 32 | t.Run(name, func(t *testing.T) { 33 | t.Parallel() 34 | jsonText := fmt.Sprintf(`{"timestamp":%s,"foo":"bar"}`, test.inputTS) 35 | var line LogLine 36 | err := json.Unmarshal([]byte(jsonText), &line) 37 | require.NoError(t, err) 38 | 39 | ts := line.Timestamp() 40 | if !test.now { 41 | assert.False(t, nowish.Before(ts), 42 | "timestamp %v should be an actual timestamp, not time.Now()", 43 | nowish, 44 | ) 45 | epoch := time.Unix(test.epochS, test.epochNS) 46 | assert.WithinDuration(t, epoch, ts, time.Microsecond) 47 | 48 | // Try round-tripping the TS through Marshal: 49 | b, err := json.Marshal(&line) 50 | require.NoError(t, err) 51 | 52 | var roundtrip LogLine 53 | err = json.Unmarshal(b, &roundtrip) 54 | require.NoError(t, err) 55 | 56 | t.Logf("log line: %s", string(b)) 57 | assert.WithinDuration(t, epoch, roundtrip.Timestamp(), time.Microsecond) 58 | } else { 59 | assert.True(t, nowish.Before(ts), 60 | "timestamp %v should be sometime after the start of the test %v", 61 | nowish, ts) 62 | } 63 | }) 64 | } 65 | } 66 | 67 | func TestMarshal(t *testing.T) { 68 | tests := []struct { 69 | in string 70 | }{ 71 | {`{"msg":"hi", "timestamp":"2006-01-02T15:04:05.999999999Z"}`}, 72 | {`{"msg":"hi"}`}, 73 | {`{"what":"no", "teletubbies":["boo", "lala"]}`}, 74 | } 75 | for _, elt := range tests { 76 | test := elt 77 | t.Run(test.in, func(t *testing.T) { 78 | t.Parallel() 79 | var line LogLine 80 | err := json.Unmarshal(([]byte)(test.in), &line) 81 | require.NoError(t, err) 82 | 83 | out, err := json.Marshal(line) 84 | outstr := (string)(out) 85 | require.NoError(t, err) 86 | assert.True(t, strings.HasPrefix(outstr, `{"timestamp":`), outstr) 87 | 88 | var roundtrip LogLine 89 | err = json.Unmarshal(out, &roundtrip) 90 | assert.NoError(t, err) 91 | }) 92 | } 93 | } 94 | 95 | type unwritable struct{} 96 | 97 | func (j unwritable) MarshalJSON() ([]byte, error) { 98 | return nil, fmt.Errorf("I'm a bobby tables \" error") 99 | } 100 | 101 | func TestMarshalBrokenFields(t *testing.T) { 102 | line := LogLine{ 103 | "this_is_broken": unwritable{}, 104 | "this_works": "hi there", 105 | } 106 | 107 | out, err := json.Marshal(line) 108 | outstr := (string)(out) 109 | require.NoError(t, err) 110 | assert.True(t, strings.HasPrefix(outstr, `{"timestamp":`), outstr) 111 | 112 | t.Log(outstr) 113 | 114 | var roundtrip LogLine 115 | err = json.Unmarshal(out, &roundtrip) 116 | assert.NoError(t, err) 117 | assert.Contains(t, roundtrip["this_is_broken"], "[unilog json marshal error:") 118 | assert.Equal(t, "hi there", roundtrip["this_works"]) 119 | } 120 | 121 | const lineWithoutTS = `{"trace_id":"6527664022527835840","id":"6045760440938957111","parent_id":"8576604763011053087","start_timestamp":1554825181.8588214,"end_timestamp":1554825181.8588665,"duration_ns":44890,"error":false,"service":"veneur","tags":{"alerting_host_group":"localdnscritical","availability-zone":"us-west-2c","host_cluster":"northwest","host_contact":"developer-platform","host_domain":"stripe.io","host_env":"prod","host_lsbdistcodename":"xenial","host_set":"full-b","host_type":"apibox","instance-type":"m5d.12xlarge"},"indicator":false,"name":"http.gotFirstByte"}` 122 | const lineWithTimestamp = `{"trace_id":"6527664022527835840","id":"6045760440938957111","parent_id":"8576604763011053087","start_timestamp":1554825181.8588214,"end_timestamp":1554825181.8588665,"duration_ns":44890,"error":false,"service":"veneur","tags":{"alerting_host_group":"localdnscritical","availability-zone":"us-west-2c","host_cluster":"northwest","host_contact":"developer-platform","host_domain":"stripe.io","host_env":"prod","host_lsbdistcodename":"xenial","host_set":"full-b","host_type":"apibox","instance-type":"m5d.12xlarge"},"indicator":false,"name":"http.gotFirstByte","timestamp":"2006-01-02T15:04:05.999999999Z"}` 123 | const lineWithTS = `{"trace_id":"6527664022527835840","id":"6045760440938957111","parent_id":"8576604763011053087","start_timestamp":1554825181.8588214,"end_timestamp":1554825181.8588665,"duration_ns":44890,"error":false,"service":"veneur","tags":{"alerting_host_group":"localdnscritical","availability-zone":"us-west-2c","host_cluster":"northwest","host_contact":"developer-platform","host_domain":"stripe.io","host_env":"prod","host_lsbdistcodename":"xenial","host_set":"full-b","host_type":"apibox","instance-type":"m5d.12xlarge"},"indicator":false,"name":"http.gotFirstByte","ts":"2006-01-02T15:04:05.999999999Z"}` 124 | 125 | // BenchmarkLineRoundtrip roundtrips each log event through the main 126 | // json decoding & re-encoding machinery: parsing the JSON line, 127 | // extracting or generating a timestamp, serializing it to bytes 128 | // again. 129 | func BenchmarkLineRoundtrip(b *testing.B) { 130 | tests := []struct{ name, line string }{ 131 | {"without_ts", lineWithoutTS}, 132 | {"with_timestamp", lineWithTimestamp}, 133 | {"with_ts", lineWithTS}, 134 | } 135 | for _, elt := range tests { 136 | test := elt 137 | b.Run(test.name, func(b *testing.B) { 138 | lineBytes := []byte(test.line) 139 | b.ResetTimer() 140 | for i := 0; i < b.N; i++ { 141 | var ll LogLine 142 | err := json.Unmarshal(lineBytes, &ll) 143 | require.NoError(b, err) 144 | 145 | _, err = json.Marshal(ll) 146 | require.NoError(b, err) 147 | } 148 | 149 | }) 150 | } 151 | } 152 | 153 | // BenchmarkLineRoundtripPlain roundtrips each log event through 154 | // json.Unmarshal/Marshal for a plain map[string]interface{}, 155 | // simulating a unilog that doesn't parse/extract timestamps and 156 | // doesn't re-arrange the fields to be in a particular order via a 157 | // custom marshaller. It is meant to provide a reference point for 158 | // gauging how much processing power the real json-processing 159 | // machinery (and timestamp synthesis) take. 160 | func BenchmarkLineRoundtripPlain(b *testing.B) { 161 | tests := []struct{ name, line string }{ 162 | {"without_ts", lineWithoutTS}, 163 | {"with_timestamp", lineWithTimestamp}, 164 | {"with_ts", lineWithTS}, 165 | } 166 | for _, elt := range tests { 167 | test := elt 168 | b.Run(test.name, func(b *testing.B) { 169 | lineBytes := []byte(test.line) 170 | b.ResetTimer() 171 | for i := 0; i < b.N; i++ { 172 | var ll map[string]interface{} 173 | err := json.Unmarshal(lineBytes, &ll) 174 | require.NoError(b, err) 175 | 176 | _, err = json.Marshal(ll) 177 | require.NoError(b, err) 178 | } 179 | 180 | }) 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /logger/doc.go: -------------------------------------------------------------------------------- 1 | // Package logger includes the core implemenation of unilog's logging process. 2 | package logger 3 | -------------------------------------------------------------------------------- /logger/unilog_test.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "bytes" 5 | encjson "encoding/json" 6 | "fmt" 7 | "os" 8 | "strings" 9 | "syscall" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stripe/unilog/filters" 14 | "github.com/stripe/unilog/json" 15 | ) 16 | 17 | var shakespeare = []string{ 18 | "To be, or not to be, that is the question-", 19 | "Whether 'tis Nobler in the mind to suffer", 20 | "The Slings and Arrows of outrageous Fortune,", 21 | "Or to take Arms against a Sea of troubles,", 22 | "And by opposing end them?", 23 | } 24 | 25 | func TestReadlines(t *testing.T) { 26 | r := strings.NewReader(strings.Join(shakespeare, "\n")) 27 | ch := make(chan struct{}) 28 | defer close(ch) 29 | lc, _ := readlines(r, 1, ch) 30 | var i int 31 | for line := range lc { 32 | if line != shakespeare[i] { 33 | t.Errorf("Line %d should have been %q, but got %q", i, 34 | shakespeare[i], line) 35 | } 36 | i++ 37 | } 38 | if i != len(shakespeare) { 39 | t.Errorf("Did not get enough lines of shakespeare") 40 | } 41 | } 42 | 43 | var big = strings.Repeat("Unique New York", 9000) 44 | 45 | func TestReadlinesWithLongLines(t *testing.T) { 46 | r := strings.NewReader(big) 47 | ch := make(chan struct{}) 48 | defer close(ch) 49 | 50 | lc, _ := readlines(r, 1, ch) 51 | line := <-lc 52 | if line != big { 53 | t.Errorf("Lines do not match! Got %d bytes; expected %d", 54 | len(line), len(big)) 55 | } 56 | if <-lc != "" { 57 | t.Error("Did not reach EOF") 58 | } 59 | } 60 | 61 | // double the first two instances of the character "e" 62 | type doubleEFilter struct{} 63 | 64 | func (ee *doubleEFilter) FilterLine(line string) string { 65 | return strings.Replace(line, "e", "ee", 2) 66 | } 67 | 68 | func (ee *doubleEFilter) FilterJSON(line *json.LogLine) { 69 | msg := (*line)["message"].(string) 70 | (*line)["message"] = strings.Replace(msg, "e", "ee", 2) 71 | } 72 | 73 | type isToAintFilter struct{} 74 | 75 | func (ee *isToAintFilter) FilterLine(line string) string { 76 | return strings.Replace(line, "is", "ain't", -1) 77 | } 78 | 79 | func (ee *isToAintFilter) FilterJSON(line *json.LogLine) { 80 | msg := (*line)["message"].(string) 81 | (*line)["message"] = strings.Replace(msg, "is", "ain't", -1) 82 | } 83 | 84 | func TestFilterFunction(t *testing.T) { 85 | var input = shakespeare[0] 86 | const expected = "To bee, or not to bee, that ain't the question-\n" 87 | 88 | u := &Unilog{} 89 | 90 | u.Filters = []Filter{ 91 | Filter(&doubleEFilter{}), 92 | Filter(&isToAintFilter{}), 93 | } 94 | 95 | result := u.format(input) 96 | if result != expected { 97 | t.Errorf("expected %q, found %q", expected, result) 98 | } 99 | } 100 | 101 | func TestTwoStateExit(t *testing.T) { 102 | u := &Unilog{} 103 | exitCode := -1 104 | 105 | term := make(chan os.Signal, 2) 106 | quit := make(chan os.Signal, 2) 107 | exit := make(chan int, 2) 108 | 109 | u.sigTerm = term 110 | u.sigQuit = quit 111 | u.exit = func(code int) { 112 | exitCode = code 113 | exit <- 1 114 | } 115 | u.shutdown = make(chan struct{}) 116 | 117 | go u.run() 118 | 119 | term <- syscall.SIGTERM 120 | <-u.shutdown 121 | quit <- syscall.SIGQUIT 122 | 123 | <-exit 124 | if exitCode != 1 { 125 | t.Error("Did not call exit.") 126 | } 127 | } 128 | 129 | func TestSigTermNoExit(t *testing.T) { 130 | u := &Unilog{} 131 | 132 | term := make(chan os.Signal, 1) 133 | quit := make(chan os.Signal, 1) 134 | 135 | u.sigTerm = term 136 | u.sigQuit = quit 137 | u.exit = func(code int) { 138 | t.Error("Called exit.") 139 | } 140 | u.shutdown = make(chan struct{}) 141 | 142 | term <- syscall.SIGTERM 143 | if !u.tick() { 144 | t.Error("Tick returned false.") 145 | } 146 | } 147 | 148 | func TestSigQuitNoOp(t *testing.T) { 149 | u := &Unilog{} 150 | 151 | term := make(chan os.Signal, 1) 152 | quit := make(chan os.Signal, 1) 153 | 154 | u.sigTerm = term 155 | u.sigQuit = quit 156 | u.exit = func(code int) { 157 | t.Error("Called exit.") 158 | } 159 | u.shutdown = make(chan struct{}) 160 | 161 | quit <- syscall.SIGQUIT 162 | if !u.tick() { 163 | t.Error("Tick returned false.") 164 | } 165 | } 166 | 167 | type MockClient struct { 168 | Counts map[string]int64 169 | } 170 | 171 | func (mc *MockClient) Count(name string, value int64, tags []string, rate float64) error { 172 | var buffer bytes.Buffer 173 | for _, tag := range tags { 174 | buffer.WriteString("[") 175 | buffer.WriteString(tag) 176 | buffer.WriteString("]") 177 | } 178 | buffer.WriteString(name) 179 | mc.Counts[buffer.String()] += value 180 | return nil 181 | } 182 | 183 | func TestNoTags(t *testing.T) { 184 | client := &MockClient{Counts: make(map[string]int64)} 185 | for i := 0; i < 100; i++ { 186 | IndependentCount(client, "metric", 10, nil, 1) 187 | } 188 | if client.Counts["metric"] != 1000 { 189 | t.Errorf("Count was %d, not %d", client.Counts["metric"], 1000) 190 | } 191 | } 192 | 193 | func TestWithGlobalTags(t *testing.T) { 194 | client := &MockClient{Counts: make(map[string]int64)} 195 | for i := 0; i < 100; i++ { 196 | IndependentCount(client, "metric", 10, []string{"foo:bar"}, 1) 197 | IndependentCount(client, "metric", 5, []string{"baz:qaz", "veneurglobalonly:true"}, 1) 198 | } 199 | var tests = map[string]int64{ 200 | "[foo:bar]metric": 1000, 201 | "[baz:qaz][veneurglobalonly:true]metric": 500, 202 | } 203 | for key, value := range tests { 204 | if client.Counts[key] != value { 205 | t.Errorf("Count for %s was %d, not %d", key, client.Counts[key], value) 206 | } 207 | } 208 | } 209 | 210 | func TestWithIndependentTags(t *testing.T) { 211 | // Save and set tagState 212 | tmp := tagState 213 | tagState = newIndependentTags([]string{"veneurglobalonly:true", "owner:observability"}) 214 | 215 | client := &MockClient{Counts: make(map[string]int64)} 216 | for i := 0; i < 100; i++ { 217 | IndependentCount(client, "metric", 10, nil, 1) 218 | IndependentCount(client, "metric", 5, []string{"baz:qaz"}, 1) 219 | } 220 | var tests = map[string]int64{ 221 | "metric": 1000, 222 | "[veneurglobalonly:true]metric.veneurglobalonly": 1000, 223 | "[owner:observability]metric.owner": 1000, 224 | "[baz:qaz]metric": 500, 225 | "[baz:qaz][veneurglobalonly:true]metric.veneurglobalonly": 500, 226 | "[baz:qaz][owner:observability]metric.owner": 500, 227 | } 228 | for key, value := range tests { 229 | if client.Counts[key] != value { 230 | t.Errorf("Count for %s was %d, not %d", key, client.Counts[key], value) 231 | } 232 | } 233 | // Restore tagState 234 | tagState = tmp 235 | } 236 | 237 | func TestIndependentTagRace(t *testing.T) { 238 | tagState = newIndependentTags([]string{"foo:bar"}) 239 | for i := 0; i < 100; i++ { 240 | go func(num int) { 241 | tagState.GetTags(fmt.Sprintf("%d", num)) 242 | }(i) 243 | } 244 | } 245 | 246 | func TestSentryPanic(t *testing.T) { 247 | defer func() { 248 | r := recover() 249 | if r == nil { 250 | t.Errorf("Expected panic, but got none") 251 | } else if !strings.HasPrefix(fmt.Sprintf("%v", r), "Invalid DSN:") { 252 | t.Errorf("Expected `Invalid DSN: ...`, but got: %s", r) 253 | } 254 | }() 255 | 256 | u := &Unilog{SentryDSN: "foo"} 257 | u.setupSentry() 258 | } 259 | 260 | func TestSentrySuccess(t *testing.T) { 261 | defer func() { 262 | r := recover() 263 | if r != nil { 264 | t.Errorf("Expected no panic, but got: %s", r) 265 | } 266 | }() 267 | 268 | u := &Unilog{SentryDSN: "https://123:456@foo/789"} 269 | u.setupSentry() 270 | } 271 | 272 | type mockFile struct { 273 | buf *bytes.Buffer 274 | } 275 | 276 | func (m mockFile) Write(p []byte) (int, error) { 277 | return m.buf.Write(p) 278 | } 279 | func (mockFile) Close() error { 280 | return nil 281 | } 282 | 283 | func getLogLine(u *Unilog, line string) string { 284 | var buf bytes.Buffer 285 | u.file = mockFile{buf: &buf} 286 | 287 | u.logLine(line) 288 | return buf.String() 289 | } 290 | 291 | func getLogJSON(u *Unilog, line string) string { 292 | var buf bytes.Buffer 293 | u.file = mockFile{buf: &buf} 294 | u.jsonEncoder = encjson.NewEncoder(u.file) 295 | 296 | u.logJSON(line) 297 | return buf.String() 298 | } 299 | 300 | func TestLogLine(t *testing.T) { 301 | out := getLogLine(&Unilog{}, "hi") 302 | assert.Equal(t, "hi\n", out) 303 | 304 | out = getLogLine(&Unilog{ 305 | Filters: []Filter{ 306 | &filters.TimePrefixFilter{Format: "foo"}, 307 | }, 308 | }, "hi") 309 | assert.Equal(t, "[foo] hi\n", out) 310 | } 311 | 312 | func TestLogJSON(t *testing.T) { 313 | out := getLogJSON(&Unilog{}, "hi") 314 | assert.Equal(t, "hi\n", out) 315 | 316 | out = getLogJSON(&Unilog{}, `{"message":"hi"}`) 317 | assert.Regexp(t, `\{"timestamp":[\d\.]+,"message":"hi"}\n`, out) 318 | } 319 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/stripe/unilog/filters" 5 | "github.com/stripe/unilog/logger" 6 | ) 7 | 8 | func main() { 9 | tf := &filters.TimePrefixFilter{} 10 | // Register flags so they're picked up when u.Main() calls flag.Parse() (ugh) 11 | tf.AddFlags() 12 | 13 | u := &logger.Unilog{ 14 | Filters: []logger.Filter{ 15 | logger.Filter(&filters.AusterityFilter{}), 16 | logger.Filter(tf), 17 | }, 18 | } 19 | u.Main() 20 | } 21 | -------------------------------------------------------------------------------- /reader/doc.go: -------------------------------------------------------------------------------- 1 | // Package reader defines a wrapper around io.Reader that supports graceful shutdowns. 2 | package reader 3 | -------------------------------------------------------------------------------- /reader/reader.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import ( 4 | "io" 5 | "sync" 6 | ) 7 | 8 | // Reader wraps an existing io.Reader and adds the ability to 9 | // perform a graceful shutdown by reading from the underlying Reader 10 | // up through the next newline character, and then start returning 11 | // EOF. 12 | type Reader struct { 13 | inner io.Reader 14 | // Was the last character read a newline? 15 | nl bool 16 | shuttingDown bool 17 | mtx sync.Mutex 18 | } 19 | 20 | // NewReader creates a new Reader for use in Unilog. Once shutdown becomes readable, the 21 | // returned Reader will start reading from the underlying Reader one 22 | // byte at a time until newline, and then start returning EOF. 23 | func NewReader(in io.Reader, shutdown <-chan struct{}) io.Reader { 24 | r := &Reader{inner: in} 25 | go func() { 26 | <-shutdown 27 | r.mtx.Lock() 28 | r.shuttingDown = true 29 | r.mtx.Unlock() 30 | }() 31 | return r 32 | } 33 | 34 | func (r *Reader) isShuttingDown() bool { 35 | r.mtx.Lock() 36 | defer r.mtx.Unlock() 37 | return r.shuttingDown 38 | } 39 | 40 | func (r *Reader) Read(buf []byte) (int, error) { 41 | if r.nl && r.isShuttingDown() { 42 | return 0, io.EOF 43 | } 44 | 45 | n, e := r.inner.Read(buf) 46 | if n > 0 { 47 | r.nl = buf[n-1] == '\n' 48 | } 49 | return n, e 50 | } 51 | -------------------------------------------------------------------------------- /reader/reader_test.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "strings" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | type op struct { 12 | read int 13 | expect string 14 | } 15 | 16 | func testUnilogReader(t *testing.T) { 17 | var twoLines = "hello world\nsecond line\n" 18 | tests := []struct { 19 | in string 20 | ops []op 21 | }{ 22 | {twoLines, []op{ 23 | {5, "hello"}, 24 | {-1, ""}, 25 | {10, " "}, 26 | {10, "w"}, 27 | {10, "o"}, 28 | {10, "r"}, 29 | {1, "l"}, 30 | {1, "d"}, 31 | {5, "\n"}, 32 | {5, ""}, 33 | {1, ""}, 34 | }}, 35 | {twoLines, []op{ 36 | {12, "hello world\n"}, 37 | {-1, ""}, 38 | {10, ""}, 39 | {1, ""}, 40 | }}, 41 | } 42 | for i, tc := range tests { 43 | shutdown := make(chan struct{}) 44 | rd := NewReader(strings.NewReader(tc.in), shutdown) 45 | buf := make([]byte, 128) 46 | for _, o := range tc.ops { 47 | if o.read < 0 { 48 | close(shutdown) 49 | // HACK: Wait for the shutdown to propagate. 50 | for !rd.(*Reader).shuttingDown { 51 | time.Sleep(1 * time.Millisecond) 52 | } 53 | continue 54 | } 55 | if len(buf) < o.read { 56 | buf = make([]byte, o.read) 57 | } 58 | n, e := rd.Read(buf[:o.read]) 59 | if n != len(o.expect) { 60 | t.Errorf("test %d, read(%d) expected %v, but got %d bytes", 61 | i, o.read, o.expect, n) 62 | break 63 | } 64 | got := string(buf[:n]) 65 | if got != o.expect { 66 | t.Errorf("test %d, read(%d) expected %v, but got %v", 67 | i, o.read, o.expect, got) 68 | break 69 | } 70 | if e != nil && len(o.expect) > 0 { 71 | t.Errorf("test %d, read(%d) expected %v, but got err: %s", 72 | i, o.read, o.expect, e.Error()) 73 | break 74 | } 75 | if len(o.expect) == 0 && e != io.EOF { 76 | t.Errorf("expected EOF, got %v", e) 77 | break 78 | } 79 | } 80 | } 81 | } 82 | 83 | type OurReader struct { 84 | input chan []byte 85 | } 86 | 87 | func (or *OurReader) Read(p []byte) (n int, err error) { 88 | bs, ok := <-or.input 89 | if !ok { 90 | return 0, io.EOF 91 | } 92 | return copy(p, bs), nil 93 | } 94 | 95 | func TestReaderBasic(t *testing.T) { 96 | input := make(chan []byte, 1) 97 | inRdr := OurReader{input} 98 | shutdown := make(chan struct{}, 1) 99 | close(shutdown) 100 | rdr := NewReader(&inRdr, shutdown) 101 | bufRdr := bufio.NewReader(rdr) 102 | input <- []byte("hello I am a line\n") 103 | line, err := bufRdr.ReadString('\n') 104 | if err != nil { 105 | t.Error(err) 106 | } 107 | if line != "hello I am a line\n" { 108 | t.Error(line) 109 | } 110 | } 111 | 112 | func TestReaderComplex(t *testing.T) { 113 | input := make(chan []byte) 114 | inRdr := OurReader{input} 115 | shutdown := make(chan struct{}) 116 | close(shutdown) 117 | rdr := NewReader(&inRdr, shutdown) 118 | bufRdr := bufio.NewReader(rdr) 119 | 120 | go func() { 121 | input <- []byte("unterminated") 122 | input <- []byte("done\n") 123 | }() 124 | line, err := bufRdr.ReadString('\n') 125 | if err != nil { 126 | t.Error(err) 127 | } 128 | if line != "unterminateddone\n" { 129 | t.Error(line) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /vendor/github.com/DataDog/datadog-go/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Datadog, Inc 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/DataDog/datadog-go/statsd/README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | Package `statsd` provides a Go [dogstatsd](http://docs.datadoghq.com/guides/dogstatsd/) client. Dogstatsd extends Statsd, adding tags 4 | and histograms. 5 | 6 | ## Get the code 7 | 8 | $ go get github.com/DataDog/datadog-go/statsd 9 | 10 | ## Usage 11 | 12 | ```go 13 | // Create the client 14 | c, err := statsd.New("127.0.0.1:8125") 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | // Prefix every metric with the app name 19 | c.Namespace = "flubber." 20 | // Send the EC2 availability zone as a tag with every metric 21 | c.Tags = append(c.Tags, "us-east-1a") 22 | 23 | // Do some metrics! 24 | err = c.Gauge("request.queue_depth", 12, nil, 1) 25 | err = c.Timing("request.duration", duration, nil, 1) // Uses a time.Duration! 26 | err = c.TimeInMilliseconds("request", 12, nil, 1) 27 | err = c.Incr("request.count_total", nil, 1) 28 | err = c.Decr("request.count_total", nil, 1) 29 | err = c.Count("request.count_total", 2, nil, 1) 30 | ``` 31 | 32 | ## Buffering Client 33 | 34 | DogStatsD accepts packets with multiple statsd payloads in them. Using the BufferingClient via `NewBufferingClient` will buffer up commands and send them when the buffer is reached or after 100msec. 35 | 36 | ## Development 37 | 38 | Run the tests with: 39 | 40 | $ go test 41 | 42 | ## Documentation 43 | 44 | Please see: http://godoc.org/github.com/DataDog/datadog-go/statsd 45 | 46 | ## License 47 | 48 | go-dogstatsd is released under the [MIT license](http://www.opensource.org/licenses/mit-license.php). 49 | 50 | ## Credits 51 | 52 | Original code by [ooyala](https://github.com/ooyala/go-dogstatsd). 53 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2012-2016 Dave Collins 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypass.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is not running on Google App Engine, compiled by GopherJS, and 17 | // "-tags safe" is not added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // Go versions prior to 1.4 are disabled because they use a different layout 20 | // for interfaces which make the implementation of unsafeReflectValue more complex. 21 | // +build !js,!appengine,!safe,!disableunsafe,go1.4 22 | 23 | package spew 24 | 25 | import ( 26 | "reflect" 27 | "unsafe" 28 | ) 29 | 30 | const ( 31 | // UnsafeDisabled is a build-time constant which specifies whether or 32 | // not access to the unsafe package is available. 33 | UnsafeDisabled = false 34 | 35 | // ptrSize is the size of a pointer on the current arch. 36 | ptrSize = unsafe.Sizeof((*byte)(nil)) 37 | ) 38 | 39 | type flag uintptr 40 | 41 | var ( 42 | // flagRO indicates whether the value field of a reflect.Value 43 | // is read-only. 44 | flagRO flag 45 | 46 | // flagAddr indicates whether the address of the reflect.Value's 47 | // value may be taken. 48 | flagAddr flag 49 | ) 50 | 51 | // flagKindMask holds the bits that make up the kind 52 | // part of the flags field. In all the supported versions, 53 | // it is in the lower 5 bits. 54 | const flagKindMask = flag(0x1f) 55 | 56 | // Different versions of Go have used different 57 | // bit layouts for the flags type. This table 58 | // records the known combinations. 59 | var okFlags = []struct { 60 | ro, addr flag 61 | }{{ 62 | // From Go 1.4 to 1.5 63 | ro: 1 << 5, 64 | addr: 1 << 7, 65 | }, { 66 | // Up to Go tip. 67 | ro: 1<<5 | 1<<6, 68 | addr: 1 << 8, 69 | }} 70 | 71 | var flagValOffset = func() uintptr { 72 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 73 | if !ok { 74 | panic("reflect.Value has no flag field") 75 | } 76 | return field.Offset 77 | }() 78 | 79 | // flagField returns a pointer to the flag field of a reflect.Value. 80 | func flagField(v *reflect.Value) *flag { 81 | return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) 82 | } 83 | 84 | // unsafeReflectValue converts the passed reflect.Value into a one that bypasses 85 | // the typical safety restrictions preventing access to unaddressable and 86 | // unexported data. It works by digging the raw pointer to the underlying 87 | // value out of the protected value and generating a new unprotected (unsafe) 88 | // reflect.Value to it. 89 | // 90 | // This allows us to check for implementations of the Stringer and error 91 | // interfaces to be used for pretty printing ordinarily unaddressable and 92 | // inaccessible values such as unexported struct fields. 93 | func unsafeReflectValue(v reflect.Value) reflect.Value { 94 | if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { 95 | return v 96 | } 97 | flagFieldPtr := flagField(&v) 98 | *flagFieldPtr &^= flagRO 99 | *flagFieldPtr |= flagAddr 100 | return v 101 | } 102 | 103 | // Sanity checks against future reflect package changes 104 | // to the type or semantics of the Value.flag field. 105 | func init() { 106 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 107 | if !ok { 108 | panic("reflect.Value has no flag field") 109 | } 110 | if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { 111 | panic("reflect.Value flag field has changed kind") 112 | } 113 | type t0 int 114 | var t struct { 115 | A t0 116 | // t0 will have flagEmbedRO set. 117 | t0 118 | // a will have flagStickyRO set 119 | a t0 120 | } 121 | vA := reflect.ValueOf(t).FieldByName("A") 122 | va := reflect.ValueOf(t).FieldByName("a") 123 | vt0 := reflect.ValueOf(t).FieldByName("t0") 124 | 125 | // Infer flagRO from the difference between the flags 126 | // for the (otherwise identical) fields in t. 127 | flagPublic := *flagField(&vA) 128 | flagWithRO := *flagField(&va) | *flagField(&vt0) 129 | flagRO = flagPublic ^ flagWithRO 130 | 131 | // Infer flagAddr from the difference between a value 132 | // taken from a pointer and not. 133 | vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") 134 | flagNoPtr := *flagField(&vA) 135 | flagPtr := *flagField(&vPtrA) 136 | flagAddr = flagNoPtr ^ flagPtr 137 | 138 | // Check that the inferred flags tally with one of the known versions. 139 | for _, f := range okFlags { 140 | if flagRO == f.ro && flagAddr == f.addr { 141 | return 142 | } 143 | } 144 | panic("reflect.Value read-only flag has changed semantics") 145 | } 146 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypasssafe.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is running on Google App Engine, compiled by GopherJS, or 17 | // "-tags safe" is added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build js appengine safe disableunsafe !go1.4 20 | 21 | package spew 22 | 23 | import "reflect" 24 | 25 | const ( 26 | // UnsafeDisabled is a build-time constant which specifies whether or 27 | // not access to the unsafe package is available. 28 | UnsafeDisabled = true 29 | ) 30 | 31 | // unsafeReflectValue typically converts the passed reflect.Value into a one 32 | // that bypasses the typical safety restrictions preventing access to 33 | // unaddressable and unexported data. However, doing this relies on access to 34 | // the unsafe package. This is a stub version which simply returns the passed 35 | // reflect.Value when the unsafe package is not available. 36 | func unsafeReflectValue(v reflect.Value) reflect.Value { 37 | return v 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* 18 | Package spew implements a deep pretty printer for Go data structures to aid in 19 | debugging. 20 | 21 | A quick overview of the additional features spew provides over the built-in 22 | printing facilities for Go data types are as follows: 23 | 24 | * Pointers are dereferenced and followed 25 | * Circular data structures are detected and handled properly 26 | * Custom Stringer/error interfaces are optionally invoked, including 27 | on unexported types 28 | * Custom types which only implement the Stringer/error interfaces via 29 | a pointer receiver are optionally invoked when passing non-pointer 30 | variables 31 | * Byte arrays and slices are dumped like the hexdump -C command which 32 | includes offsets, byte values in hex, and ASCII output (only when using 33 | Dump style) 34 | 35 | There are two different approaches spew allows for dumping Go data structures: 36 | 37 | * Dump style which prints with newlines, customizable indentation, 38 | and additional debug information such as types and all pointer addresses 39 | used to indirect to the final value 40 | * A custom Formatter interface that integrates cleanly with the standard fmt 41 | package and replaces %v, %+v, %#v, and %#+v to provide inline printing 42 | similar to the default %v while providing the additional functionality 43 | outlined above and passing unsupported format verbs such as %x and %q 44 | along to fmt 45 | 46 | Quick Start 47 | 48 | This section demonstrates how to quickly get started with spew. See the 49 | sections below for further details on formatting and configuration options. 50 | 51 | To dump a variable with full newlines, indentation, type, and pointer 52 | information use Dump, Fdump, or Sdump: 53 | spew.Dump(myVar1, myVar2, ...) 54 | spew.Fdump(someWriter, myVar1, myVar2, ...) 55 | str := spew.Sdump(myVar1, myVar2, ...) 56 | 57 | Alternatively, if you would prefer to use format strings with a compacted inline 58 | printing style, use the convenience wrappers Printf, Fprintf, etc with 59 | %v (most compact), %+v (adds pointer addresses), %#v (adds types), or 60 | %#+v (adds types and pointer addresses): 61 | spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) 62 | spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 63 | spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) 64 | spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 65 | 66 | Configuration Options 67 | 68 | Configuration of spew is handled by fields in the ConfigState type. For 69 | convenience, all of the top-level functions use a global state available 70 | via the spew.Config global. 71 | 72 | It is also possible to create a ConfigState instance that provides methods 73 | equivalent to the top-level functions. This allows concurrent configuration 74 | options. See the ConfigState documentation for more details. 75 | 76 | The following configuration options are available: 77 | * Indent 78 | String to use for each indentation level for Dump functions. 79 | It is a single space by default. A popular alternative is "\t". 80 | 81 | * MaxDepth 82 | Maximum number of levels to descend into nested data structures. 83 | There is no limit by default. 84 | 85 | * DisableMethods 86 | Disables invocation of error and Stringer interface methods. 87 | Method invocation is enabled by default. 88 | 89 | * DisablePointerMethods 90 | Disables invocation of error and Stringer interface methods on types 91 | which only accept pointer receivers from non-pointer variables. 92 | Pointer method invocation is enabled by default. 93 | 94 | * DisablePointerAddresses 95 | DisablePointerAddresses specifies whether to disable the printing of 96 | pointer addresses. This is useful when diffing data structures in tests. 97 | 98 | * DisableCapacities 99 | DisableCapacities specifies whether to disable the printing of 100 | capacities for arrays, slices, maps and channels. This is useful when 101 | diffing data structures in tests. 102 | 103 | * ContinueOnMethod 104 | Enables recursion into types after invoking error and Stringer interface 105 | methods. Recursion after method invocation is disabled by default. 106 | 107 | * SortKeys 108 | Specifies map keys should be sorted before being printed. Use 109 | this to have a more deterministic, diffable output. Note that 110 | only native types (bool, int, uint, floats, uintptr and string) 111 | and types which implement error or Stringer interfaces are 112 | supported with other types sorted according to the 113 | reflect.Value.String() output which guarantees display 114 | stability. Natural map order is used by default. 115 | 116 | * SpewKeys 117 | Specifies that, as a last resort attempt, map keys should be 118 | spewed to strings and sorted by those strings. This is only 119 | considered if SortKeys is true. 120 | 121 | Dump Usage 122 | 123 | Simply call spew.Dump with a list of variables you want to dump: 124 | 125 | spew.Dump(myVar1, myVar2, ...) 126 | 127 | You may also call spew.Fdump if you would prefer to output to an arbitrary 128 | io.Writer. For example, to dump to standard error: 129 | 130 | spew.Fdump(os.Stderr, myVar1, myVar2, ...) 131 | 132 | A third option is to call spew.Sdump to get the formatted output as a string: 133 | 134 | str := spew.Sdump(myVar1, myVar2, ...) 135 | 136 | Sample Dump Output 137 | 138 | See the Dump example for details on the setup of the types and variables being 139 | shown here. 140 | 141 | (main.Foo) { 142 | unexportedField: (*main.Bar)(0xf84002e210)({ 143 | flag: (main.Flag) flagTwo, 144 | data: (uintptr) 145 | }), 146 | ExportedField: (map[interface {}]interface {}) (len=1) { 147 | (string) (len=3) "one": (bool) true 148 | } 149 | } 150 | 151 | Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C 152 | command as shown. 153 | ([]uint8) (len=32 cap=32) { 154 | 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | 155 | 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| 156 | 00000020 31 32 |12| 157 | } 158 | 159 | Custom Formatter 160 | 161 | Spew provides a custom formatter that implements the fmt.Formatter interface 162 | so that it integrates cleanly with standard fmt package printing functions. The 163 | formatter is useful for inline printing of smaller data types similar to the 164 | standard %v format specifier. 165 | 166 | The custom formatter only responds to the %v (most compact), %+v (adds pointer 167 | addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb 168 | combinations. Any other verbs such as %x and %q will be sent to the the 169 | standard fmt package for formatting. In addition, the custom formatter ignores 170 | the width and precision arguments (however they will still work on the format 171 | specifiers not handled by the custom formatter). 172 | 173 | Custom Formatter Usage 174 | 175 | The simplest way to make use of the spew custom formatter is to call one of the 176 | convenience functions such as spew.Printf, spew.Println, or spew.Printf. The 177 | functions have syntax you are most likely already familiar with: 178 | 179 | spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) 180 | spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 181 | spew.Println(myVar, myVar2) 182 | spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) 183 | spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 184 | 185 | See the Index for the full list convenience functions. 186 | 187 | Sample Formatter Output 188 | 189 | Double pointer to a uint8: 190 | %v: <**>5 191 | %+v: <**>(0xf8400420d0->0xf8400420c8)5 192 | %#v: (**uint8)5 193 | %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 194 | 195 | Pointer to circular struct with a uint8 field and a pointer to itself: 196 | %v: <*>{1 <*>} 197 | %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} 198 | %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} 199 | %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} 200 | 201 | See the Printf example for details on the setup of variables being shown 202 | here. 203 | 204 | Errors 205 | 206 | Since it is possible for custom Stringer/error interfaces to panic, spew 207 | detects them and handles them internally by printing the panic information 208 | inline with the output. Since spew is intended to provide deep pretty printing 209 | capabilities on structures, it intentionally does not return any errors. 210 | */ 211 | package spew 212 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/spew.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | ) 23 | 24 | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were 25 | // passed with a default Formatter interface returned by NewFormatter. It 26 | // returns the formatted string as a value that satisfies error. See 27 | // NewFormatter for formatting details. 28 | // 29 | // This function is shorthand for the following syntax: 30 | // 31 | // fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 32 | func Errorf(format string, a ...interface{}) (err error) { 33 | return fmt.Errorf(format, convertArgs(a)...) 34 | } 35 | 36 | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were 37 | // passed with a default Formatter interface returned by NewFormatter. It 38 | // returns the number of bytes written and any write error encountered. See 39 | // NewFormatter for formatting details. 40 | // 41 | // This function is shorthand for the following syntax: 42 | // 43 | // fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) 44 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) { 45 | return fmt.Fprint(w, convertArgs(a)...) 46 | } 47 | 48 | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were 49 | // passed with a default Formatter interface returned by NewFormatter. It 50 | // returns the number of bytes written and any write error encountered. See 51 | // NewFormatter for formatting details. 52 | // 53 | // This function is shorthand for the following syntax: 54 | // 55 | // fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) 56 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { 57 | return fmt.Fprintf(w, format, convertArgs(a)...) 58 | } 59 | 60 | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it 61 | // passed with a default Formatter interface returned by NewFormatter. See 62 | // NewFormatter for formatting details. 63 | // 64 | // This function is shorthand for the following syntax: 65 | // 66 | // fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) 67 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { 68 | return fmt.Fprintln(w, convertArgs(a)...) 69 | } 70 | 71 | // Print is a wrapper for fmt.Print that treats each argument as if it were 72 | // passed with a default Formatter interface returned by NewFormatter. It 73 | // returns the number of bytes written and any write error encountered. See 74 | // NewFormatter for formatting details. 75 | // 76 | // This function is shorthand for the following syntax: 77 | // 78 | // fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) 79 | func Print(a ...interface{}) (n int, err error) { 80 | return fmt.Print(convertArgs(a)...) 81 | } 82 | 83 | // Printf is a wrapper for fmt.Printf that treats each argument as if it were 84 | // passed with a default Formatter interface returned by NewFormatter. It 85 | // returns the number of bytes written and any write error encountered. See 86 | // NewFormatter for formatting details. 87 | // 88 | // This function is shorthand for the following syntax: 89 | // 90 | // fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 91 | func Printf(format string, a ...interface{}) (n int, err error) { 92 | return fmt.Printf(format, convertArgs(a)...) 93 | } 94 | 95 | // Println is a wrapper for fmt.Println that treats each argument as if it were 96 | // passed with a default Formatter interface returned by NewFormatter. It 97 | // returns the number of bytes written and any write error encountered. See 98 | // NewFormatter for formatting details. 99 | // 100 | // This function is shorthand for the following syntax: 101 | // 102 | // fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) 103 | func Println(a ...interface{}) (n int, err error) { 104 | return fmt.Println(convertArgs(a)...) 105 | } 106 | 107 | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were 108 | // passed with a default Formatter interface returned by NewFormatter. It 109 | // returns the resulting string. See NewFormatter for formatting details. 110 | // 111 | // This function is shorthand for the following syntax: 112 | // 113 | // fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) 114 | func Sprint(a ...interface{}) string { 115 | return fmt.Sprint(convertArgs(a)...) 116 | } 117 | 118 | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were 119 | // passed with a default Formatter interface returned by NewFormatter. It 120 | // returns the resulting string. See NewFormatter for formatting details. 121 | // 122 | // This function is shorthand for the following syntax: 123 | // 124 | // fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 125 | func Sprintf(format string, a ...interface{}) string { 126 | return fmt.Sprintf(format, convertArgs(a)...) 127 | } 128 | 129 | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it 130 | // were passed with a default Formatter interface returned by NewFormatter. It 131 | // returns the resulting string. See NewFormatter for formatting details. 132 | // 133 | // This function is shorthand for the following syntax: 134 | // 135 | // fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) 136 | func Sprintln(a ...interface{}) string { 137 | return fmt.Sprintln(convertArgs(a)...) 138 | } 139 | 140 | // convertArgs accepts a slice of arguments and returns a slice of the same 141 | // length with each argument converted to a default spew Formatter interface. 142 | func convertArgs(args []interface{}) (formatters []interface{}) { 143 | formatters = make([]interface{}, len(args)) 144 | for index, arg := range args { 145 | formatters[index] = NewFormatter(arg) 146 | } 147 | return formatters 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/.craft.yml: -------------------------------------------------------------------------------- 1 | minVersion: '0.9.2' 2 | github: 3 | owner: getsentry 4 | repo: sentry-go 5 | preReleaseCommand: bash scripts/craft-pre-release.sh 6 | changelogPolicy: simple 7 | statusProvider: 8 | name: github 9 | config: 10 | contexts: 11 | - Travis CI - Branch 12 | targets: 13 | - name: github 14 | includeNames: /none/ 15 | tagPrefix: v 16 | - name: registry 17 | type: sdk 18 | config: 19 | canonical: "github:getsentry/sentry-go" 20 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/.gitignore: -------------------------------------------------------------------------------- 1 | coverage.txt 2 | 3 | # Just my personal way of tracking stuff — Kamil 4 | FIXME.md 5 | TODO.md 6 | !NOTES.md -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | disable-all: true 3 | enable: 4 | - bodyclose 5 | - deadcode 6 | - depguard 7 | - dogsled 8 | - dupl 9 | - errcheck 10 | - gochecknoglobals 11 | - gochecknoinits 12 | - goconst 13 | - gocritic 14 | - gocyclo 15 | - gofmt 16 | - goimports 17 | - golint 18 | - gosec 19 | - gosimple 20 | - govet 21 | - ineffassign 22 | - interfacer 23 | - lll 24 | - maligned 25 | - misspell 26 | - nakedret 27 | - prealloc 28 | - scopelint 29 | - staticcheck 30 | - structcheck 31 | - typecheck 32 | - unconvert 33 | - unparam 34 | - unused 35 | - varcheck 36 | run: 37 | skip-dirs: 38 | - echo 39 | - example/echo 40 | issues: 41 | exclude: 42 | - "not declared by package utf8" 43 | - "unicode/utf8/utf8.go" 44 | exclude-rules: 45 | - path: _test\.go 46 | linters: 47 | - prealloc 48 | - path: errors_test\.go 49 | linters: 50 | - unused 51 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.12.x 5 | - 1.13.x 6 | - 1.14.x 7 | - master 8 | 9 | env: 10 | - GO111MODULE=on GOFLAGS=-mod=readonly 11 | - GO111MODULE=off 12 | 13 | jobs: 14 | include: 15 | - name: "Module support outside of GOPATH" 16 | go: stable 17 | before_script: >- 18 | mv $GOPATH/src/github.com/getsentry/sentry-go ~/sentry-go && 19 | cd ~/sentry-go && 20 | export GOPATH= && 21 | go env GOPATH 22 | script: >- 23 | go test ./... && 24 | go test ./... -race 25 | allow_failures: 26 | - go: master 27 | fast_finish: true 28 | 29 | before_install: 30 | - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/v1.19.1/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.19.1 31 | # Fetch origin/master. This is required for `git merge-base` when testing a 32 | # branch, since Travis clones only the target branch. 33 | - git fetch origin master:remotes/origin/master 34 | 35 | script: 36 | - golangci-lint run --new-from-rev=$(git merge-base origin/master HEAD) 37 | - go build ./... 38 | - go test ./... 39 | - go test ./... -race 40 | 41 | notifications: 42 | webhooks: 43 | urls: 44 | - https://zeus.ci/hooks/befe9810-9285-11e9-b01a-0a580a281808/public/provider/travis/webhook 45 | on_success: always 46 | on_failure: always 47 | on_start: always 48 | on_cancel: always 49 | on_error: always 50 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | - "I am running away from my responsibilities. And it feels good." – Michael Scott, Season 4, "Money" 6 | 7 | ## v0.6.1 8 | 9 | - fix: Use NewEvent to init Event struct (#220) 10 | 11 | _NOTE:_ 12 | A change introduced in v0.6.0 with the intent of avoiding allocations made a 13 | pattern used in official examples break in certain circumstances (attempting 14 | to write to a nil map). 15 | This release reverts the change such that maps in the Event struct are always 16 | allocated. 17 | 18 | ## v0.6.0 19 | 20 | - feat: Read module dependencies from runtime/debug (#199) 21 | - feat: Support chained errors using Unwrap (#206) 22 | - feat: Report chain of errors when available (#185) 23 | - **[breaking]** fix: Accept http.RoundTripper to customize transport (#205) 24 | Before the SDK accepted a concrete value of type `*http.Transport` in 25 | `ClientOptions`, now it accepts any value implementing the `http.RoundTripper` 26 | interface. Note that `*http.Transport` implements `http.RoundTripper`, so most 27 | code bases will continue to work unchanged. 28 | Users of custom transport gain the ability to pass in other implementations of 29 | `http.RoundTripper` and may be able to simplify their code bases. 30 | - fix: Do not panic when scope event processor drops event (#192) 31 | - **[breaking]** fix: Use time.Time for timestamps (#191) 32 | Users of sentry-go typically do not need to manipulate timestamps manually. 33 | For those who do, the field type changed from `int64` to `time.Time`, which 34 | should be more convenient to use. The recommended way to get the current time 35 | is `time.Now().UTC()`. 36 | - fix: Report usage error including stack trace (#189) 37 | - feat: Add Exception.ThreadID field (#183) 38 | - ci: Test against Go 1.14, drop 1.11 (#170) 39 | - feat: Limit reading bytes from request bodies (#168) 40 | - **[breaking]** fix: Rename fasthttp integration package sentryhttp => sentryfasthttp 41 | The current recommendation is to use a named import, in which case existing 42 | code should not require any change: 43 | ```go 44 | package main 45 | 46 | import ( 47 | "fmt" 48 | 49 | "github.com/getsentry/sentry-go" 50 | sentryfasthttp "github.com/getsentry/sentry-go/fasthttp" 51 | "github.com/valyala/fasthttp" 52 | ) 53 | ``` 54 | 55 | _NOTE:_ 56 | This version includes some new features and a few breaking changes, none of 57 | which should pose troubles with upgrading. Most code bases should be able to 58 | upgrade without any changes. 59 | 60 | ## v0.5.1 61 | 62 | - fix: Ignore err.Cause() when it is nil (#160) 63 | 64 | ## v0.5.0 65 | 66 | - fix: Synchronize access to HTTPTransport.disabledUntil (#158) 67 | - docs: Update Flush documentation (#153) 68 | - fix: HTTPTransport.Flush panic and data race (#140) 69 | 70 | _NOTE:_ 71 | This version changes the implementation of the default transport, modifying the 72 | behavior of `sentry.Flush`. The previous behavior was to wait until there were 73 | no buffered events; new concurrent events kept `Flush` from returning. The new 74 | behavior is to wait until the last event prior to the call to `Flush` has been 75 | sent or the timeout; new concurrent events have no effect. The new behavior is 76 | inline with the [Unified API 77 | Guidelines](https://docs.sentry.io/development/sdk-dev/unified-api/). 78 | 79 | We have updated the documentation and examples to clarify that `Flush` is meant 80 | to be called typically only once before program termination, to wait for 81 | in-flight events to be sent to Sentry. Calling `Flush` after every event is not 82 | recommended, as it introduces unnecessary latency to the surrounding function. 83 | Please verify the usage of `sentry.Flush` in your code base. 84 | 85 | ## v0.4.0 86 | 87 | - fix(stacktrace): Correctly report package names (#127) 88 | - fix(stacktrace): Do not rely on AbsPath of files (#123) 89 | - build: Require github.com/ugorji/go@v1.1.7 (#110) 90 | - fix: Correctly store last event id (#99) 91 | - fix: Include request body in event payload (#94) 92 | - build: Reset go.mod version to 1.11 (#109) 93 | - fix: Eliminate data race in modules integration (#105) 94 | - feat: Add support for path prefixes in the DSN (#102) 95 | - feat: Add HTTPClient option (#86) 96 | - feat: Extract correct type and value from top-most error (#85) 97 | - feat: Check for broken pipe errors in Gin integration (#82) 98 | - fix: Client.CaptureMessage accept nil EventModifier (#72) 99 | 100 | ## v0.3.1 101 | 102 | - feat: Send extra information exposed by the Go runtime (#76) 103 | - fix: Handle new lines in module integration (#65) 104 | - fix: Make sure that cache is locked when updating for contextifyFramesIntegration 105 | - ref: Update Iris integration and example to version 12 106 | - misc: Remove indirect dependencies in order to move them to separate go.mod files 107 | 108 | ## v0.3.0 109 | 110 | - feat: Retry event marshalling without contextual data if the first pass fails 111 | - fix: Include `url.Parse` error in `DsnParseError` 112 | - fix: Make more `Scope` methods safe for concurrency 113 | - fix: Synchronize concurrent access to `Hub.client` 114 | - ref: Remove mutex from `Scope` exported API 115 | - ref: Remove mutex from `Hub` exported API 116 | - ref: Compile regexps for `filterFrames` only once 117 | - ref: Change `SampleRate` type to `float64` 118 | - doc: `Scope.Clear` not safe for concurrent use 119 | - ci: Test sentry-go with `go1.13`, drop `go1.10` 120 | 121 | _NOTE:_ 122 | This version removes some of the internal APIs that landed publicly (namely `Hub/Scope` mutex structs) and may require (but shouldn't) some changes to your code. 123 | It's not done through major version update, as we are still in `0.x` stage. 124 | 125 | ## v0.2.1 126 | 127 | - fix: Run `Contextify` integration on `Threads` as well 128 | 129 | ## v0.2.0 130 | 131 | - feat: Add `SetTransaction()` method on the `Scope` 132 | - feat: `fasthttp` framework support with `sentryfasthttp` package 133 | - fix: Add `RWMutex` locks to internal `Hub` and `Scope` changes 134 | 135 | ## v0.1.3 136 | 137 | - feat: Move frames context reading into `contextifyFramesIntegration` (#28) 138 | 139 | _NOTE:_ 140 | In case of any performance issues due to source contexts IO, you can let us know and turn off the integration in the meantime with: 141 | 142 | ```go 143 | sentry.Init(sentry.ClientOptions{ 144 | Integrations: func(integrations []sentry.Integration) []sentry.Integration { 145 | var filteredIntegrations []sentry.Integration 146 | for _, integration := range integrations { 147 | if integration.Name() == "ContextifyFrames" { 148 | continue 149 | } 150 | filteredIntegrations = append(filteredIntegrations, integration) 151 | } 152 | return filteredIntegrations 153 | }, 154 | }) 155 | ``` 156 | 157 | ## v0.1.2 158 | 159 | - feat: Better source code location resolution and more useful inapp frames (#26) 160 | - feat: Use `noopTransport` when no `Dsn` provided (#27) 161 | - ref: Allow empty `Dsn` instead of returning an error (#22) 162 | - fix: Use `NewScope` instead of literal struct inside a `scope.Clear` call (#24) 163 | - fix: Add to `WaitGroup` before the request is put inside a buffer (#25) 164 | 165 | ## v0.1.1 166 | 167 | - fix: Check for initialized `Client` in `AddBreadcrumbs` (#20) 168 | - build: Bump version when releasing with Craft (#19) 169 | 170 | ## v0.1.0 171 | 172 | - First stable release! \o/ 173 | 174 | ## v0.0.1-beta.5 175 | 176 | - feat: **[breaking]** Add `NewHTTPTransport` and `NewHTTPSyncTransport` which accepts all transport options 177 | - feat: New `HTTPSyncTransport` that blocks after each call 178 | - feat: New `Echo` integration 179 | - ref: **[breaking]** Remove `BufferSize` option from `ClientOptions` and move it to `HTTPTransport` instead 180 | - ref: Export default `HTTPTransport` 181 | - ref: Export `net/http` integration handler 182 | - ref: Set `Request` instantly in the package handlers, not in `recoverWithSentry` so it can be accessed later on 183 | - ci: Add craft config 184 | 185 | ## v0.0.1-beta.4 186 | 187 | - feat: `IgnoreErrors` client option and corresponding integration 188 | - ref: Reworked `net/http` integration, wrote better example and complete readme 189 | - ref: Reworked `Gin` integration, wrote better example and complete readme 190 | - ref: Reworked `Iris` integration, wrote better example and complete readme 191 | - ref: Reworked `Negroni` integration, wrote better example and complete readme 192 | - ref: Reworked `Martini` integration, wrote better example and complete readme 193 | - ref: Remove `Handle()` from frameworks handlers and return it directly from New 194 | 195 | ## v0.0.1-beta.3 196 | 197 | - feat: `Iris` framework support with `sentryiris` package 198 | - feat: `Gin` framework support with `sentrygin` package 199 | - feat: `Martini` framework support with `sentrymartini` package 200 | - feat: `Negroni` framework support with `sentrynegroni` package 201 | - feat: Add `Hub.Clone()` for easier frameworks integration 202 | - feat: Return `EventID` from `Recovery` methods 203 | - feat: Add `NewScope` and `NewEvent` functions and use them in the whole codebase 204 | - feat: Add `AddEventProcessor` to the `Client` 205 | - fix: Operate on requests body copy instead of the original 206 | - ref: Try to read source files from the root directory, based on the filename as well, to make it work on AWS Lambda 207 | - ref: Remove `gocertifi` dependence and document how to provide your own certificates 208 | - ref: **[breaking]** Remove `Decorate` and `DecorateFunc` methods in favor of `sentryhttp` package 209 | - ref: **[breaking]** Allow for integrations to live on the client, by passing client instance in `SetupOnce` method 210 | - ref: **[breaking]** Remove `GetIntegration` from the `Hub` 211 | - ref: **[breaking]** Remove `GlobalEventProcessors` getter from the public API 212 | 213 | ## v0.0.1-beta.2 214 | 215 | - feat: Add `AttachStacktrace` client option to include stacktrace for messages 216 | - feat: Add `BufferSize` client option to configure transport buffer size 217 | - feat: Add `SetRequest` method on a `Scope` to control `Request` context data 218 | - feat: Add `FromHTTPRequest` for `Request` type for easier extraction 219 | - ref: Extract `Request` information more accurately 220 | - fix: Attach `ServerName`, `Release`, `Dist`, `Environment` options to the event 221 | - fix: Don't log events dropped due to full transport buffer as sent 222 | - fix: Don't panic and create an appropriate event when called `CaptureException` or `Recover` with `nil` value 223 | 224 | ## v0.0.1-beta 225 | 226 | - Initial release 227 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Community 2 | 3 | The public-facing channels for support and development of Sentry SDKs can be found on [Discord](https://discord.gg/Ww9hbqr). 4 | 5 | ## Testing 6 | 7 | ```console 8 | $ go test 9 | ``` 10 | 11 | ### Watch mode 12 | 13 | Use: https://github.com/cespare/reflex 14 | 15 | ```console 16 | $ reflex -g '*.go' -d "none" -- sh -c 'printf "\n"; go test' 17 | ``` 18 | 19 | ### With data race detection 20 | 21 | ```console 22 | $ go test -race 23 | ``` 24 | 25 | ### Coverage 26 | ```console 27 | $ go test -race -coverprofile=coverage.txt -covermode=atomic && go tool cover -html coverage.txt 28 | ``` 29 | 30 | ## Linting 31 | 32 | ```console 33 | $ golangci-lint run 34 | ``` 35 | 36 | ## Release 37 | 38 | 1. Update `CHANGELOG.md` with new version in `vX.X.X` format title and list of changes. 39 | 40 | The command below can be used to get a list of changes since the last tag, with the format used in `CHANGELOG.md`: 41 | 42 | ```console 43 | $ git log --no-merges --format=%s $(git describe --abbrev=0).. | sed 's/^/- /' 44 | ``` 45 | 46 | 2. Commit with `misc: vX.X.X changelog` commit message and push to `master`. 47 | 48 | 3. Let [`craft`](https://github.com/getsentry/craft) do the rest: 49 | 50 | ```console 51 | $ craft prepare X.X.X 52 | $ craft publish X.X.X 53 | ``` 54 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Sentry (https://sentry.io) and individual contributors. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | * 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. 8 | 9 | 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 HOLDER 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. 10 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/MIGRATION.md: -------------------------------------------------------------------------------- 1 | # `raven-go` to `sentry-go` Migration Guide 2 | 3 | ## Installation 4 | 5 | raven-go 6 | 7 | ```go 8 | go get github.com/getsentry/raven-go 9 | ``` 10 | 11 | sentry-go 12 | 13 | ```go 14 | go get github.com/getsentry/sentry-go@v0.0.1 15 | ``` 16 | 17 | ## Configuration 18 | 19 | raven-go 20 | 21 | ```go 22 | import "github.com/getsentry/raven-go" 23 | 24 | func main() { 25 | raven.SetDSN("___PUBLIC_DSN___") 26 | } 27 | ``` 28 | 29 | sentry-go 30 | 31 | ```go 32 | import ( 33 | "fmt" 34 | "github.com/getsentry/sentry-go" 35 | ) 36 | 37 | func main() { 38 | err := sentry.Init(sentry.ClientOptions{ 39 | Dsn: "___PUBLIC_DSN___", 40 | }) 41 | 42 | if err != nil { 43 | fmt.Printf("Sentry initialization failed: %v\n", err) 44 | } 45 | } 46 | ``` 47 | 48 | raven-go 49 | 50 | ```go 51 | SetDSN() 52 | SetDefaultLoggerName() 53 | SetDebug() 54 | SetEnvironment() 55 | SetRelease() 56 | SetSampleRate() 57 | SetIgnoreErrors() 58 | SetIncludePaths() 59 | ``` 60 | 61 | sentry-go 62 | 63 | ```go 64 | sentry.Init(sentry.ClientOptions{ 65 | Dsn: "___PUBLIC_DSN___", 66 | DebugWriter: os.Stderr, 67 | Debug: true, 68 | Environment: "environment", 69 | Release: "release", 70 | SampleRate: 0.5, 71 | // IgnoreErrors: TBD, 72 | // IncludePaths: TBD 73 | }) 74 | ``` 75 | 76 | Available options: see [Configuration](https://docs.sentry.io/platforms/go/config/) section. 77 | 78 | ### Providing SSL Certificates 79 | 80 | By default, TLS uses the host's root CA set. If you don't have `ca-certificates` (which should be your go-to way of fixing the issue of missing certificates) and want to use `gocertifi` instead, you can provide pre-loaded cert files as one of the options to the `sentry.Init` call: 81 | 82 | ```go 83 | package main 84 | 85 | import ( 86 | "log" 87 | 88 | "github.com/certifi/gocertifi" 89 | "github.com/getsentry/sentry-go" 90 | ) 91 | 92 | sentryClientOptions := sentry.ClientOptions{ 93 | Dsn: "___PUBLIC_DSN___", 94 | } 95 | 96 | rootCAs, err := gocertifi.CACerts() 97 | if err != nil { 98 | log.Println("Couldn't load CA Certificates: %v\n", err) 99 | } else { 100 | sentryClientOptions.CaCerts = rootCAs 101 | } 102 | 103 | sentry.Init(sentryClientOptions) 104 | ``` 105 | 106 | ## Usage 107 | 108 | ### Capturing Errors 109 | 110 | raven-go 111 | 112 | ```go 113 | f, err := os.Open("filename.ext") 114 | if err != nil { 115 | raven.CaptureError(err, nil) 116 | } 117 | ``` 118 | 119 | sentry-go 120 | 121 | ```go 122 | f, err := os.Open("filename.ext") 123 | if err != nil { 124 | sentry.CaptureException(err) 125 | } 126 | ``` 127 | 128 | ### Capturing Panics 129 | 130 | raven-go 131 | 132 | ```go 133 | raven.CapturePanic(func() { 134 | // do all of the scary things here 135 | }, nil) 136 | ``` 137 | 138 | sentry-go 139 | 140 | ```go 141 | func() { 142 | defer sentry.Recover() 143 | // do all of the scary things here 144 | }() 145 | ``` 146 | 147 | ### Capturing Messages 148 | 149 | raven-go 150 | 151 | ```go 152 | raven.CaptureMessage("Something bad happened and I would like to know about that") 153 | ``` 154 | 155 | sentry-go 156 | 157 | ```go 158 | sentry.CaptureMessage("Something bad happened and I would like to know about that") 159 | ``` 160 | 161 | ### Capturing Events 162 | 163 | raven-go 164 | 165 | ```go 166 | packet := &raven.Packet{ 167 | Message: "Hand-crafted event", 168 | Extra: &raven.Extra{ 169 | "runtime.Version": runtime.Version(), 170 | "runtime.NumCPU": runtime.NumCPU(), 171 | }, 172 | } 173 | raven.Capture(packet) 174 | ``` 175 | 176 | sentry-go 177 | 178 | ```go 179 | event := &sentry.NewEvent() 180 | event.Message = "Hand-crafted event" 181 | event.Extra["runtime.Version"] = runtime.Version() 182 | event.Extra["runtime.NumCPU"] = runtime.NumCPU() 183 | 184 | sentry.CaptureEvent(event) 185 | ``` 186 | 187 | ### Additional Data 188 | 189 | See Context section. 190 | 191 | ### Event Sampling 192 | 193 | raven-go 194 | 195 | ```go 196 | raven.SetSampleRate(0.25) 197 | ``` 198 | 199 | sentry-go 200 | 201 | ```go 202 | sentry.Init(sentry.ClientOptions{ 203 | SampleRate: 0.25, 204 | }) 205 | ``` 206 | 207 | ### Awaiting the response (not recommended) 208 | 209 | ```go 210 | raven.CaptureMessageAndWait("Something bad happened and I would like to know about that") 211 | ``` 212 | 213 | sentry-go 214 | 215 | ```go 216 | sentry.CaptureMessage("Something bad happened and I would like to know about that") 217 | 218 | if sentry.Flush(time.Second * 2) { 219 | // event delivered 220 | } else { 221 | // timeout reached 222 | } 223 | ``` 224 | 225 | ## Context 226 | 227 | ### Per-event 228 | 229 | raven-go 230 | 231 | ```go 232 | raven.CaptureError(err, map[string]string{"browser": "Firefox"}, &raven.Http{ 233 | Method: "GET", 234 | URL: "https://example.com/raven-go" 235 | }) 236 | ``` 237 | 238 | sentry-go 239 | 240 | ```go 241 | sentry.WithScope(func(scope *sentry.Scope) { 242 | scope.SetTag("browser", "Firefox") 243 | scope.SetContext("Request", map[string]string{ 244 | "Method": "GET", 245 | "URL": "https://example.com/raven-go", 246 | }) 247 | sentry.CaptureException(err) 248 | }) 249 | ``` 250 | 251 | ### Globally 252 | 253 | #### SetHttpContext 254 | 255 | raven-go 256 | 257 | ```go 258 | raven.SetHttpContext(&raven.Http{ 259 | Method: "GET", 260 | URL: "https://example.com/raven-go", 261 | }) 262 | ``` 263 | 264 | sentry-go 265 | 266 | ```go 267 | sentry.ConfigureScope(func(scope *sentry.Scope) { 268 | scope.SetContext("Request", map[string]string{ 269 | "Method": "GET", 270 | "URL": "https://example.com/raven-go", 271 | }) 272 | }) 273 | ``` 274 | 275 | #### SetTagsContext 276 | 277 | raven-go 278 | 279 | ```go 280 | t := map[string]string{"day": "Friday", "sport": "Weightlifting"} 281 | raven.SetTagsContext(map[string]string{"day": "Friday", "sport": "Weightlifting"}) 282 | ``` 283 | 284 | sentry-go 285 | 286 | ```go 287 | sentry.ConfigureScope(func(scope *sentry.Scope) { 288 | scope.SetTags(map[string]string{"day": "Friday", "sport": "Weightlifting"}) 289 | }) 290 | ``` 291 | 292 | #### SetUserContext 293 | 294 | raven-go 295 | 296 | ```go 297 | raven.SetUserContext(&raven.User{ 298 | ID: "1337", 299 | Username: "kamilogorek", 300 | Email: "kamil@sentry.io", 301 | IP: "127.0.0.1", 302 | }) 303 | ``` 304 | 305 | sentry-go 306 | 307 | ```go 308 | sentry.ConfigureScope(func(scope *sentry.Scope) { 309 | scope.SetUser(sentry.User{ 310 | ID: "1337", 311 | Username: "kamilogorek", 312 | Email: "kamil@sentry.io", 313 | IPAddress: "127.0.0.1", 314 | }) 315 | }) 316 | ``` 317 | 318 | #### ClearContext 319 | 320 | raven-go 321 | 322 | ```go 323 | raven.ClearContext() 324 | ``` 325 | 326 | sentry-go 327 | 328 | ```go 329 | sentry.ConfigureScope(func(scope *sentry.Scope) { 330 | scope.Clear() 331 | }) 332 | ``` 333 | 334 | #### WrapWithExtra 335 | 336 | raven-go 337 | 338 | ```go 339 | path := "filename.ext" 340 | f, err := os.Open(path) 341 | if err != nil { 342 | err = raven.WrapWithExtra(err, map[string]string{"path": path, "cwd": os.Getwd()} 343 | raven.CaptureError(err, nil) 344 | } 345 | ``` 346 | 347 | sentry-go 348 | 349 | ```go 350 | // use `sentry.WithScope`, see "Context / Per-event Section" 351 | path := "filename.ext" 352 | f, err := os.Open(path) 353 | if err != nil { 354 | sentry.WithScope(func(scope *sentry.Scope) { 355 | sentry.SetExtras(map[string]interface{}{"path": path, "cwd": os.Getwd()) 356 | sentry.CaptureException(err) 357 | }) 358 | } 359 | ``` 360 | 361 | ## Integrations 362 | 363 | ### net/http 364 | 365 | raven-go 366 | 367 | ```go 368 | mux := http.NewServeMux 369 | http.Handle("/", raven.Recoverer(mux)) 370 | 371 | // or 372 | 373 | func root(w http.ResponseWriter, r *http.Request) {} 374 | http.HandleFunc("/", raven.RecoveryHandler(root)) 375 | ``` 376 | 377 | sentry-go 378 | 379 | ```go 380 | sentryHandler := sentryhttp.New(sentryhttp.Options{ 381 | Repanic: false, 382 | WaitForDelivery: true, 383 | }) 384 | 385 | mux := http.NewServeMux 386 | http.Handle("/", sentryHandler.Handle(mux)) 387 | 388 | // or 389 | 390 | func root(w http.ResponseWriter, r *http.Request) {} 391 | http.HandleFunc("/", sentryHandler.HandleFunc(root)) 392 | ``` 393 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |
6 |

7 | 8 | # Official Sentry SDK for Go 9 | 10 | [![Build Status](https://travis-ci.com/getsentry/sentry-go.svg?branch=master)](https://travis-ci.com/getsentry/sentry-go) 11 | [![Go Report Card](https://goreportcard.com/badge/github.com/getsentry/sentry-go)](https://goreportcard.com/report/github.com/getsentry/sentry-go) 12 | [![Discord](https://img.shields.io/discord/621778831602221064)](https://discord.gg/Ww9hbqr) 13 | [![GoDoc](https://godoc.org/github.com/getsentry/sentry-go?status.svg)](https://godoc.org/github.com/getsentry/sentry-go) 14 | [![go.dev](https://img.shields.io/badge/go.dev-pkg-007d9c.svg?style=flat)](https://pkg.go.dev/github.com/getsentry/sentry-go) 15 | 16 | `sentry-go` provides a Sentry client implementation for the Go programming 17 | language. This is the next line of the Go SDK for [Sentry](https://sentry.io/), 18 | intended to replace the `raven-go` package. 19 | 20 | > Looking for the old `raven-go` SDK documentation? See the Legacy client section [here](https://docs.sentry.io/clients/go/). 21 | > If you want to start using sentry-go instead, check out the [migration guide](https://docs.sentry.io/platforms/go/migration/). 22 | 23 | ## Requirements 24 | 25 | The only requirement is a Go compiler. 26 | 27 | We verify this package against the 3 most recent releases of Go. Those are the 28 | supported versions. The exact versions are defined in 29 | [`.travis.yml`](.travis.yml). 30 | 31 | In addition, we run tests against the current master branch of the Go toolchain, 32 | though support for this configuration is best-effort. 33 | 34 | ## Installation 35 | 36 | `sentry-go` can be installed like any other Go library through `go get`: 37 | 38 | ```console 39 | $ go get github.com/getsentry/sentry-go 40 | ``` 41 | 42 | Or, if you are already using 43 | [Go Modules](https://github.com/golang/go/wiki/Modules), you may specify a 44 | version number as well: 45 | 46 | ```console 47 | $ go get github.com/getsentry/sentry-go@latest 48 | ``` 49 | 50 | Check out the [list of released versions](https://pkg.go.dev/github.com/getsentry/sentry-go?tab=versions). 51 | 52 | ## Configuration 53 | 54 | To use `sentry-go`, you’ll need to import the `sentry-go` package and initialize 55 | it with your DSN and other [options](https://godoc.org/github.com/getsentry/sentry-go#ClientOptions). 56 | 57 | If not specified in the SDK initialization, the 58 | [DSN](https://docs.sentry.io/error-reporting/configuration/?platform=go#dsn), 59 | [Release](https://docs.sentry.io/workflow/releases/?platform=go) and 60 | [Environment](https://docs.sentry.io/enriching-error-data/environments/?platform=go) 61 | are read from the environment variables `SENTRY_DSN`, `SENTRY_RELEASE` and 62 | `SENTRY_ENVIRONMENT`, respectively. 63 | 64 | More on this in the [Configuration](https://docs.sentry.io/platforms/go/config/) 65 | section of the official Sentry documentation. 66 | 67 | ## Usage 68 | 69 | The SDK must be initialized with a call to `sentry.Init`. The default transport 70 | is asynchronous and thus most programs should call `sentry.Flush` to wait until 71 | buffered events are sent to Sentry right before the program terminates. 72 | 73 | Typically, `sentry.Init` is called in the beginning of `func main` and 74 | `sentry.Flush` is [deferred](https://golang.org/ref/spec#Defer_statements) right 75 | after. 76 | 77 | > Note that if the program terminates with a call to 78 | > [`os.Exit`](https://golang.org/pkg/os/#Exit), either directly or indirectly 79 | > via another function like `log.Fatal`, deferred functions are not run. 80 | > 81 | > In that case, and if it is important for you to report outstanding events 82 | > before terminating the program, arrange for `sentry.Flush` to be called before 83 | > the program terminates. 84 | 85 | Example: 86 | 87 | ```go 88 | // This is an example program that makes an HTTP request and prints response 89 | // headers. Whenever a request fails, the error is reported to Sentry. 90 | // 91 | // Try it by running: 92 | // 93 | // go run main.go 94 | // go run main.go https://sentry.io 95 | // go run main.go bad-url 96 | // 97 | // To actually report events to Sentry, set the DSN either by editing the 98 | // appropriate line below or setting the environment variable SENTRY_DSN to 99 | // match the DSN of your Sentry project. 100 | package main 101 | 102 | import ( 103 | "fmt" 104 | "log" 105 | "net/http" 106 | "os" 107 | "time" 108 | 109 | "github.com/getsentry/sentry-go" 110 | ) 111 | 112 | func main() { 113 | if len(os.Args) < 2 { 114 | log.Fatalf("usage: %s URL", os.Args[0]) 115 | } 116 | 117 | err := sentry.Init(sentry.ClientOptions{ 118 | // Either set your DSN here or set the SENTRY_DSN environment variable. 119 | Dsn: "", 120 | // Enable printing of SDK debug messages. 121 | // Useful when getting started or trying to figure something out. 122 | Debug: true, 123 | }) 124 | if err != nil { 125 | log.Fatalf("sentry.Init: %s", err) 126 | } 127 | // Flush buffered events before the program terminates. 128 | // Set the timeout to the maximum duration the program can afford to wait. 129 | defer sentry.Flush(2 * time.Second) 130 | 131 | resp, err := http.Get(os.Args[1]) 132 | if err != nil { 133 | sentry.CaptureException(err) 134 | log.Printf("reported to Sentry: %s", err) 135 | return 136 | } 137 | defer resp.Body.Close() 138 | 139 | for header, values := range resp.Header { 140 | for _, value := range values { 141 | fmt.Printf("%s=%s\n", header, value) 142 | } 143 | } 144 | } 145 | ``` 146 | 147 | For your convenience, this example is available at 148 | [`example/basic/main.go`](example/basic/main.go). 149 | There are also more examples in the 150 | [example](example) directory. 151 | 152 | For more detailed information about how to get the most out of `sentry-go`, 153 | checkout the official documentation: 154 | 155 | - [Configuration](https://docs.sentry.io/platforms/go/config) 156 | - [Error Reporting](https://docs.sentry.io/error-reporting/quickstart?platform=go) 157 | - [Enriching Error Data](https://docs.sentry.io/enriching-error-data/context?platform=go) 158 | - [Transports](https://docs.sentry.io/platforms/go/transports) 159 | - [Integrations](https://docs.sentry.io/platforms/go/integrations) 160 | - [net/http](https://docs.sentry.io/platforms/go/http) 161 | - [echo](https://docs.sentry.io/platforms/go/echo) 162 | - [fasthttp](https://docs.sentry.io/platforms/go/fasthttp) 163 | - [gin](https://docs.sentry.io/platforms/go/gin) 164 | - [iris](https://docs.sentry.io/platforms/go/iris) 165 | - [martini](https://docs.sentry.io/platforms/go/martini) 166 | - [negroni](https://docs.sentry.io/platforms/go/negroni) 167 | 168 | ## Resources 169 | 170 | - [Bug Tracker](https://github.com/getsentry/sentry-go/issues) 171 | - [GitHub Project](https://github.com/getsentry/sentry-go) 172 | - [![GoDoc](https://godoc.org/github.com/getsentry/sentry-go?status.svg)](https://godoc.org/github.com/getsentry/sentry-go) 173 | - [![go.dev](https://img.shields.io/badge/go.dev-pkg-007d9c.svg?style=flat)](https://pkg.go.dev/github.com/getsentry/sentry-go) 174 | - [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/go/) 175 | - [![Forum](https://img.shields.io/badge/forum-sentry-green.svg)](https://forum.sentry.io/c/sdks) 176 | - [![Discord](https://img.shields.io/discord/621778831602221064)](https://discord.gg/Ww9hbqr) 177 | - [![Stack Overflow](https://img.shields.io/badge/stack%20overflow-sentry-green.svg)](http://stackoverflow.com/questions/tagged/sentry) 178 | - [![Twitter Follow](https://img.shields.io/twitter/follow/getsentry?label=getsentry&style=social)](https://twitter.com/intent/follow?screen_name=getsentry) 179 | 180 | 181 | ## License 182 | 183 | Licensed under 184 | [The 2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause), see 185 | [`LICENSE`](LICENSE). 186 | 187 | ## Community 188 | 189 | Join Sentry's [`#go` channel on Discord](https://discord.gg/Ww9hbqr) to get 190 | involved and help us improve the SDK! 191 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/dsn.go: -------------------------------------------------------------------------------- 1 | package sentry 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | type scheme string 13 | 14 | const ( 15 | schemeHTTP scheme = "http" 16 | schemeHTTPS scheme = "https" 17 | ) 18 | 19 | func (scheme scheme) defaultPort() int { 20 | switch scheme { 21 | case schemeHTTPS: 22 | return 443 23 | case schemeHTTP: 24 | return 80 25 | default: 26 | return 80 27 | } 28 | } 29 | 30 | type DsnParseError struct { 31 | Message string 32 | } 33 | 34 | func (e DsnParseError) Error() string { 35 | return "[Sentry] DsnParseError: " + e.Message 36 | } 37 | 38 | // Dsn is used as the remote address source to client transport. 39 | type Dsn struct { 40 | scheme scheme 41 | publicKey string 42 | secretKey string 43 | host string 44 | port int 45 | path string 46 | projectID int 47 | } 48 | 49 | // NewDsn creates an instance of `Dsn` by parsing provided url in a `string` format. 50 | // If Dsn is not set the client is effectively disabled. 51 | func NewDsn(rawURL string) (*Dsn, error) { 52 | // Parse 53 | parsedURL, err := url.Parse(rawURL) 54 | if err != nil { 55 | return nil, &DsnParseError{fmt.Sprintf("invalid url: %v", err)} 56 | } 57 | 58 | // Scheme 59 | var scheme scheme 60 | switch parsedURL.Scheme { 61 | case "http": 62 | scheme = schemeHTTP 63 | case "https": 64 | scheme = schemeHTTPS 65 | default: 66 | return nil, &DsnParseError{"invalid scheme"} 67 | } 68 | 69 | // PublicKey 70 | publicKey := parsedURL.User.Username() 71 | if publicKey == "" { 72 | return nil, &DsnParseError{"empty username"} 73 | } 74 | 75 | // SecretKey 76 | var secretKey string 77 | if parsedSecretKey, ok := parsedURL.User.Password(); ok { 78 | secretKey = parsedSecretKey 79 | } 80 | 81 | // Host 82 | host := parsedURL.Hostname() 83 | if host == "" { 84 | return nil, &DsnParseError{"empty host"} 85 | } 86 | 87 | // Port 88 | var port int 89 | if parsedURL.Port() != "" { 90 | parsedPort, err := strconv.Atoi(parsedURL.Port()) 91 | if err != nil { 92 | return nil, &DsnParseError{"invalid port"} 93 | } 94 | port = parsedPort 95 | } else { 96 | port = scheme.defaultPort() 97 | } 98 | 99 | // ProjectID 100 | if len(parsedURL.Path) == 0 || parsedURL.Path == "/" { 101 | return nil, &DsnParseError{"empty project id"} 102 | } 103 | pathSegments := strings.Split(parsedURL.Path[1:], "/") 104 | projectID, err := strconv.Atoi(pathSegments[len(pathSegments)-1]) 105 | if err != nil { 106 | return nil, &DsnParseError{"invalid project id"} 107 | } 108 | 109 | // Path 110 | var path string 111 | if len(pathSegments) > 1 { 112 | path = "/" + strings.Join(pathSegments[0:len(pathSegments)-1], "/") 113 | } 114 | 115 | return &Dsn{ 116 | scheme: scheme, 117 | publicKey: publicKey, 118 | secretKey: secretKey, 119 | host: host, 120 | port: port, 121 | path: path, 122 | projectID: projectID, 123 | }, nil 124 | } 125 | 126 | // String formats Dsn struct into a valid string url 127 | func (dsn Dsn) String() string { 128 | var url string 129 | url += fmt.Sprintf("%s://%s", dsn.scheme, dsn.publicKey) 130 | if dsn.secretKey != "" { 131 | url += fmt.Sprintf(":%s", dsn.secretKey) 132 | } 133 | url += fmt.Sprintf("@%s", dsn.host) 134 | if dsn.port != dsn.scheme.defaultPort() { 135 | url += fmt.Sprintf(":%d", dsn.port) 136 | } 137 | if dsn.path != "" { 138 | url += dsn.path 139 | } 140 | url += fmt.Sprintf("/%d", dsn.projectID) 141 | return url 142 | } 143 | 144 | // StoreAPIURL returns assembled url to be used in the transport. 145 | // It points to configures Sentry instance. 146 | func (dsn Dsn) StoreAPIURL() *url.URL { 147 | var rawURL string 148 | rawURL += fmt.Sprintf("%s://%s", dsn.scheme, dsn.host) 149 | if dsn.port != dsn.scheme.defaultPort() { 150 | rawURL += fmt.Sprintf(":%d", dsn.port) 151 | } 152 | if dsn.path != "" { 153 | rawURL += dsn.path 154 | } 155 | rawURL += fmt.Sprintf("/api/%d/store/", dsn.projectID) 156 | parsedURL, _ := url.Parse(rawURL) 157 | return parsedURL 158 | } 159 | 160 | // RequestHeaders returns all the necessary headers that have to be used in the transport. 161 | func (dsn Dsn) RequestHeaders() map[string]string { 162 | auth := fmt.Sprintf("Sentry sentry_version=%s, sentry_timestamp=%d, "+ 163 | "sentry_client=sentry.go/%s, sentry_key=%s", apiVersion, time.Now().Unix(), Version, dsn.publicKey) 164 | 165 | if dsn.secretKey != "" { 166 | auth = fmt.Sprintf("%s, sentry_secret=%s", auth, dsn.secretKey) 167 | } 168 | 169 | return map[string]string{ 170 | "Content-Type": "application/json", 171 | "X-Sentry-Auth": auth, 172 | } 173 | } 174 | 175 | func (dsn Dsn) MarshalJSON() ([]byte, error) { 176 | return json.Marshal(dsn.String()) 177 | } 178 | 179 | func (dsn *Dsn) UnmarshalJSON(data []byte) error { 180 | var str string 181 | _ = json.Unmarshal(data, &str) 182 | newDsn, err := NewDsn(str) 183 | if err != nil { 184 | return err 185 | } 186 | *dsn = *newDsn 187 | return nil 188 | } 189 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/getsentry/sentry-go 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/ajg/form v1.5.1 // indirect 7 | github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect 8 | github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect 9 | github.com/gin-gonic/gin v1.4.0 10 | github.com/go-errors/errors v1.0.1 11 | github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab 12 | github.com/google/go-cmp v0.4.0 13 | github.com/google/go-querystring v1.0.0 // indirect 14 | github.com/imkira/go-interpol v1.1.0 // indirect 15 | github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect 16 | github.com/kataras/iris/v12 v12.0.1 17 | github.com/labstack/echo/v4 v4.1.11 18 | github.com/moul/http2curl v1.0.0 // indirect 19 | github.com/onsi/ginkgo v1.10.3 // indirect 20 | github.com/onsi/gomega v1.7.1 // indirect 21 | github.com/pingcap/errors v0.11.4 22 | github.com/pkg/errors v0.8.1 23 | github.com/sergi/go-diff v1.0.0 // indirect 24 | github.com/smartystreets/goconvey v1.6.4 // indirect 25 | github.com/ugorji/go v1.1.7 // indirect 26 | github.com/urfave/negroni v1.0.0 27 | github.com/valyala/fasthttp v1.6.0 28 | github.com/xeipuuv/gojsonschema v1.2.0 // indirect 29 | github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect 30 | github.com/yudai/gojsondiff v1.0.0 // indirect 31 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect 32 | github.com/yudai/pp v2.0.1+incompatible // indirect 33 | ) 34 | 35 | replace github.com/ugorji/go v1.1.4 => github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 36 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/hub.go: -------------------------------------------------------------------------------- 1 | package sentry 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type contextKey int 10 | 11 | // HubContextKey is a context key used to store Hub on any context.Context type 12 | const HubContextKey = contextKey(1) 13 | 14 | // RequestContextKey is a context key used to store http.Request on the context passed to RecoverWithContext 15 | const RequestContextKey = contextKey(2) 16 | 17 | // Default maximum number of breadcrumbs added to an event. Can be overwritten `maxBreadcrumbs` option. 18 | const defaultMaxBreadcrumbs = 30 19 | 20 | // Absolute maximum number of breadcrumbs added to an event. 21 | // The `maxBreadcrumbs` option cannot be higher than this value. 22 | const maxBreadcrumbs = 100 23 | 24 | // Initial instance of the Hub that has no `Client` bound and an empty `Scope` 25 | var currentHub = NewHub(nil, NewScope()) //nolint: gochecknoglobals 26 | 27 | // Hub is the central object that manages scopes and clients. 28 | // 29 | // This can be used to capture events and manage the scope. 30 | // The default hub that is available automatically. 31 | // 32 | // In most situations developers do not need to interface the hub. Instead 33 | // toplevel convenience functions are exposed that will automatically dispatch 34 | // to global (`CurrentHub`) hub. In some situations this might not be 35 | // possible in which case it might become necessary to manually work with the 36 | // hub. This is for instance the case when working with async code. 37 | type Hub struct { 38 | mu sync.RWMutex 39 | stack *stack 40 | lastEventID EventID 41 | } 42 | 43 | type layer struct { 44 | // mu protects concurrent reads and writes to client. 45 | mu sync.RWMutex 46 | client *Client 47 | // scope is read-only, not protected by mu. 48 | scope *Scope 49 | } 50 | 51 | // Client returns the layer's client. Safe for concurrent use. 52 | func (l *layer) Client() *Client { 53 | l.mu.RLock() 54 | defer l.mu.RUnlock() 55 | return l.client 56 | } 57 | 58 | // SetClient sets the layer's client. Safe for concurrent use. 59 | func (l *layer) SetClient(c *Client) { 60 | l.mu.Lock() 61 | defer l.mu.Unlock() 62 | l.client = c 63 | } 64 | 65 | type stack []*layer 66 | 67 | // NewHub returns an instance of a `Hub` with provided `Client` and `Scope` bound. 68 | func NewHub(client *Client, scope *Scope) *Hub { 69 | hub := Hub{ 70 | stack: &stack{{ 71 | client: client, 72 | scope: scope, 73 | }}, 74 | } 75 | return &hub 76 | } 77 | 78 | // CurrentHub returns an instance of previously initialized `Hub` stored in the global namespace. 79 | func CurrentHub() *Hub { 80 | return currentHub 81 | } 82 | 83 | // LastEventID returns an ID of last captured event for the current `Hub`. 84 | func (hub *Hub) LastEventID() EventID { 85 | return hub.lastEventID 86 | } 87 | 88 | func (hub *Hub) stackTop() *layer { 89 | hub.mu.RLock() 90 | defer hub.mu.RUnlock() 91 | 92 | stack := hub.stack 93 | if stack == nil { 94 | return nil 95 | } 96 | 97 | stackLen := len(*stack) 98 | if stackLen == 0 { 99 | return nil 100 | } 101 | top := (*stack)[stackLen-1] 102 | 103 | return top 104 | } 105 | 106 | // Clone returns a copy of the current Hub with top-most scope and client copied over. 107 | func (hub *Hub) Clone() *Hub { 108 | top := hub.stackTop() 109 | if top == nil { 110 | return nil 111 | } 112 | scope := top.scope 113 | if scope != nil { 114 | scope = scope.Clone() 115 | } 116 | return NewHub(top.Client(), scope) 117 | } 118 | 119 | // Scope returns top-level `Scope` of the current `Hub` or `nil` if no `Scope` is bound. 120 | func (hub *Hub) Scope() *Scope { 121 | top := hub.stackTop() 122 | if top == nil { 123 | return nil 124 | } 125 | return top.scope 126 | } 127 | 128 | // Scope returns top-level `Client` of the current `Hub` or `nil` if no `Client` is bound. 129 | func (hub *Hub) Client() *Client { 130 | top := hub.stackTop() 131 | if top == nil { 132 | return nil 133 | } 134 | return top.Client() 135 | } 136 | 137 | // PushScope pushes a new scope for the current `Hub` and reuses previously bound `Client`. 138 | func (hub *Hub) PushScope() *Scope { 139 | top := hub.stackTop() 140 | 141 | var client *Client 142 | if top != nil { 143 | client = top.Client() 144 | } 145 | 146 | var scope *Scope 147 | if top != nil && top.scope != nil { 148 | scope = top.scope.Clone() 149 | } else { 150 | scope = NewScope() 151 | } 152 | 153 | hub.mu.Lock() 154 | defer hub.mu.Unlock() 155 | 156 | *hub.stack = append(*hub.stack, &layer{ 157 | client: client, 158 | scope: scope, 159 | }) 160 | 161 | return scope 162 | } 163 | 164 | // PushScope pops the most recent scope for the current `Hub`. 165 | func (hub *Hub) PopScope() { 166 | hub.mu.Lock() 167 | defer hub.mu.Unlock() 168 | 169 | stack := *hub.stack 170 | stackLen := len(stack) 171 | if stackLen > 0 { 172 | *hub.stack = stack[0 : stackLen-1] 173 | } 174 | } 175 | 176 | // BindClient binds a new `Client` for the current `Hub`. 177 | func (hub *Hub) BindClient(client *Client) { 178 | top := hub.stackTop() 179 | if top != nil { 180 | top.SetClient(client) 181 | } 182 | } 183 | 184 | // WithScope temporarily pushes a scope for a single call. 185 | // 186 | // A shorthand for: 187 | // PushScope() 188 | // f(scope) 189 | // PopScope() 190 | func (hub *Hub) WithScope(f func(scope *Scope)) { 191 | scope := hub.PushScope() 192 | defer hub.PopScope() 193 | f(scope) 194 | } 195 | 196 | // ConfigureScope invokes a function that can modify the current scope. 197 | // 198 | // The function is passed a mutable reference to the `Scope` so that modifications 199 | // can be performed. 200 | func (hub *Hub) ConfigureScope(f func(scope *Scope)) { 201 | scope := hub.Scope() 202 | f(scope) 203 | } 204 | 205 | // CaptureEvent calls the method of a same name on currently bound `Client` instance 206 | // passing it a top-level `Scope`. 207 | // Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available. 208 | func (hub *Hub) CaptureEvent(event *Event) *EventID { 209 | client, scope := hub.Client(), hub.Scope() 210 | if client == nil || scope == nil { 211 | return nil 212 | } 213 | eventID := client.CaptureEvent(event, nil, scope) 214 | if eventID != nil { 215 | hub.lastEventID = *eventID 216 | } else { 217 | hub.lastEventID = "" 218 | } 219 | return eventID 220 | } 221 | 222 | // CaptureMessage calls the method of a same name on currently bound `Client` instance 223 | // passing it a top-level `Scope`. 224 | // Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available. 225 | func (hub *Hub) CaptureMessage(message string) *EventID { 226 | client, scope := hub.Client(), hub.Scope() 227 | if client == nil || scope == nil { 228 | return nil 229 | } 230 | eventID := client.CaptureMessage(message, nil, scope) 231 | if eventID != nil { 232 | hub.lastEventID = *eventID 233 | } else { 234 | hub.lastEventID = "" 235 | } 236 | return eventID 237 | } 238 | 239 | // CaptureException calls the method of a same name on currently bound `Client` instance 240 | // passing it a top-level `Scope`. 241 | // Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available. 242 | func (hub *Hub) CaptureException(exception error) *EventID { 243 | client, scope := hub.Client(), hub.Scope() 244 | if client == nil || scope == nil { 245 | return nil 246 | } 247 | eventID := client.CaptureException(exception, &EventHint{OriginalException: exception}, scope) 248 | if eventID != nil { 249 | hub.lastEventID = *eventID 250 | } else { 251 | hub.lastEventID = "" 252 | } 253 | return eventID 254 | } 255 | 256 | // AddBreadcrumb records a new breadcrumb. 257 | // 258 | // The total number of breadcrumbs that can be recorded are limited by the 259 | // configuration on the client. 260 | func (hub *Hub) AddBreadcrumb(breadcrumb *Breadcrumb, hint *BreadcrumbHint) { 261 | client := hub.Client() 262 | 263 | // If there's no client, just store it on the scope straight away 264 | if client == nil { 265 | hub.Scope().AddBreadcrumb(breadcrumb, maxBreadcrumbs) 266 | return 267 | } 268 | 269 | options := client.Options() 270 | max := defaultMaxBreadcrumbs 271 | 272 | if options.MaxBreadcrumbs != 0 { 273 | max = options.MaxBreadcrumbs 274 | } 275 | 276 | if max < 0 { 277 | return 278 | } 279 | 280 | if options.BeforeBreadcrumb != nil { 281 | h := &BreadcrumbHint{} 282 | if hint != nil { 283 | h = hint 284 | } 285 | if breadcrumb = options.BeforeBreadcrumb(breadcrumb, h); breadcrumb == nil { 286 | Logger.Println("breadcrumb dropped due to BeforeBreadcrumb callback.") 287 | return 288 | } 289 | } 290 | 291 | if max > maxBreadcrumbs { 292 | max = maxBreadcrumbs 293 | } 294 | hub.Scope().AddBreadcrumb(breadcrumb, max) 295 | } 296 | 297 | // Recover calls the method of a same name on currently bound `Client` instance 298 | // passing it a top-level `Scope`. 299 | // Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available. 300 | func (hub *Hub) Recover(err interface{}) *EventID { 301 | if err == nil { 302 | err = recover() 303 | } 304 | client, scope := hub.Client(), hub.Scope() 305 | if client == nil || scope == nil { 306 | return nil 307 | } 308 | return client.Recover(err, &EventHint{RecoveredException: err}, scope) 309 | } 310 | 311 | // RecoverWithContext calls the method of a same name on currently bound `Client` instance 312 | // passing it a top-level `Scope`. 313 | // Returns `EventID` if successfully, or `nil` if there's no `Scope` or `Client` available. 314 | func (hub *Hub) RecoverWithContext(ctx context.Context, err interface{}) *EventID { 315 | if err == nil { 316 | err = recover() 317 | } 318 | client, scope := hub.Client(), hub.Scope() 319 | if client == nil || scope == nil { 320 | return nil 321 | } 322 | return client.RecoverWithContext(ctx, err, &EventHint{RecoveredException: err}, scope) 323 | } 324 | 325 | // Flush waits until the underlying Transport sends any buffered events to the 326 | // Sentry server, blocking for at most the given timeout. It returns false if 327 | // the timeout was reached. In that case, some events may not have been sent. 328 | // 329 | // Flush should be called before terminating the program to avoid 330 | // unintentionally dropping events. 331 | // 332 | // Do not call Flush indiscriminately after every call to CaptureEvent, 333 | // CaptureException or CaptureMessage. Instead, to have the SDK send events over 334 | // the network synchronously, configure it to use the HTTPSyncTransport in the 335 | // call to Init. 336 | func (hub *Hub) Flush(timeout time.Duration) bool { 337 | client := hub.Client() 338 | 339 | if client == nil { 340 | return false 341 | } 342 | 343 | return client.Flush(timeout) 344 | } 345 | 346 | // HasHubOnContext checks whether `Hub` instance is bound to a given `Context` struct. 347 | func HasHubOnContext(ctx context.Context) bool { 348 | _, ok := ctx.Value(HubContextKey).(*Hub) 349 | return ok 350 | } 351 | 352 | // GetHubFromContext tries to retrieve `Hub` instance from the given `Context` struct 353 | // or return `nil` if one is not found. 354 | func GetHubFromContext(ctx context.Context) *Hub { 355 | if hub, ok := ctx.Value(HubContextKey).(*Hub); ok { 356 | return hub 357 | } 358 | return nil 359 | } 360 | 361 | // SetHubOnContext stores given `Hub` instance on the `Context` struct and returns a new `Context`. 362 | func SetHubOnContext(ctx context.Context, hub *Hub) context.Context { 363 | return context.WithValue(ctx, HubContextKey, hub) 364 | } 365 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/integrations.go: -------------------------------------------------------------------------------- 1 | package sentry 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "runtime" 7 | "runtime/debug" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | // ================================ 13 | // Modules Integration 14 | // ================================ 15 | 16 | type modulesIntegration struct { 17 | once sync.Once 18 | modules map[string]string 19 | } 20 | 21 | func (mi *modulesIntegration) Name() string { 22 | return "Modules" 23 | } 24 | 25 | func (mi *modulesIntegration) SetupOnce(client *Client) { 26 | client.AddEventProcessor(mi.processor) 27 | } 28 | 29 | func (mi *modulesIntegration) processor(event *Event, hint *EventHint) *Event { 30 | if len(event.Modules) == 0 { 31 | mi.once.Do(func() { 32 | info, ok := debug.ReadBuildInfo() 33 | if !ok { 34 | Logger.Print("The Modules integration is not available in binaries built without module support.") 35 | return 36 | } 37 | mi.modules = extractModules(info) 38 | }) 39 | } 40 | event.Modules = mi.modules 41 | return event 42 | } 43 | 44 | func extractModules(info *debug.BuildInfo) map[string]string { 45 | modules := map[string]string{ 46 | info.Main.Path: info.Main.Version, 47 | } 48 | for _, dep := range info.Deps { 49 | ver := dep.Version 50 | if dep.Replace != nil { 51 | ver += fmt.Sprintf(" => %s %s", dep.Replace.Path, dep.Replace.Version) 52 | } 53 | modules[dep.Path] = strings.TrimSuffix(ver, " ") 54 | } 55 | return modules 56 | } 57 | 58 | // ================================ 59 | // Environment Integration 60 | // ================================ 61 | 62 | type environmentIntegration struct{} 63 | 64 | func (ei *environmentIntegration) Name() string { 65 | return "Environment" 66 | } 67 | 68 | func (ei *environmentIntegration) SetupOnce(client *Client) { 69 | client.AddEventProcessor(ei.processor) 70 | } 71 | 72 | func (ei *environmentIntegration) processor(event *Event, hint *EventHint) *Event { 73 | if event.Contexts == nil { 74 | event.Contexts = make(map[string]interface{}) 75 | } 76 | 77 | event.Contexts["device"] = map[string]interface{}{ 78 | "arch": runtime.GOARCH, 79 | "num_cpu": runtime.NumCPU(), 80 | } 81 | 82 | event.Contexts["os"] = map[string]interface{}{ 83 | "name": runtime.GOOS, 84 | } 85 | 86 | event.Contexts["runtime"] = map[string]interface{}{ 87 | "name": "go", 88 | "version": runtime.Version(), 89 | "go_numroutines": runtime.NumGoroutine(), 90 | "go_maxprocs": runtime.GOMAXPROCS(0), 91 | "go_numcgocalls": runtime.NumCgoCall(), 92 | } 93 | 94 | return event 95 | } 96 | 97 | // ================================ 98 | // Ignore Errors Integration 99 | // ================================ 100 | 101 | type ignoreErrorsIntegration struct { 102 | ignoreErrors []*regexp.Regexp 103 | } 104 | 105 | func (iei *ignoreErrorsIntegration) Name() string { 106 | return "IgnoreErrors" 107 | } 108 | 109 | func (iei *ignoreErrorsIntegration) SetupOnce(client *Client) { 110 | iei.ignoreErrors = transformStringsIntoRegexps(client.Options().IgnoreErrors) 111 | client.AddEventProcessor(iei.processor) 112 | } 113 | 114 | func (iei *ignoreErrorsIntegration) processor(event *Event, hint *EventHint) *Event { 115 | suspects := getIgnoreErrorsSuspects(event) 116 | 117 | for _, suspect := range suspects { 118 | for _, pattern := range iei.ignoreErrors { 119 | if pattern.Match([]byte(suspect)) { 120 | Logger.Printf("Event dropped due to being matched by `IgnoreErrors` option."+ 121 | "| Value matched: %s | Filter used: %s", suspect, pattern) 122 | return nil 123 | } 124 | } 125 | } 126 | 127 | return event 128 | } 129 | 130 | func transformStringsIntoRegexps(strings []string) []*regexp.Regexp { 131 | var exprs []*regexp.Regexp 132 | 133 | for _, s := range strings { 134 | r, err := regexp.Compile(s) 135 | if err == nil { 136 | exprs = append(exprs, r) 137 | } 138 | } 139 | 140 | return exprs 141 | } 142 | 143 | func getIgnoreErrorsSuspects(event *Event) []string { 144 | suspects := []string{} 145 | 146 | if event.Message != "" { 147 | suspects = append(suspects, event.Message) 148 | } 149 | 150 | for _, ex := range event.Exception { 151 | suspects = append(suspects, ex.Type) 152 | suspects = append(suspects, ex.Value) 153 | } 154 | 155 | return suspects 156 | } 157 | 158 | // ================================ 159 | // Contextify Frames Integration 160 | // ================================ 161 | 162 | type contextifyFramesIntegration struct { 163 | sr sourceReader 164 | contextLines int 165 | cachedLocations sync.Map 166 | } 167 | 168 | func (cfi *contextifyFramesIntegration) Name() string { 169 | return "ContextifyFrames" 170 | } 171 | 172 | func (cfi *contextifyFramesIntegration) SetupOnce(client *Client) { 173 | cfi.sr = newSourceReader() 174 | cfi.contextLines = 5 175 | 176 | client.AddEventProcessor(cfi.processor) 177 | } 178 | 179 | func (cfi *contextifyFramesIntegration) processor(event *Event, hint *EventHint) *Event { 180 | // Range over all exceptions 181 | for _, ex := range event.Exception { 182 | // If it has no stacktrace, just bail out 183 | if ex.Stacktrace == nil { 184 | continue 185 | } 186 | 187 | // If it does, it should have frames, so try to contextify them 188 | ex.Stacktrace.Frames = cfi.contextify(ex.Stacktrace.Frames) 189 | } 190 | 191 | // Range over all threads 192 | for _, th := range event.Threads { 193 | // If it has no stacktrace, just bail out 194 | if th.Stacktrace == nil { 195 | continue 196 | } 197 | 198 | // If it does, it should have frames, so try to contextify them 199 | th.Stacktrace.Frames = cfi.contextify(th.Stacktrace.Frames) 200 | } 201 | 202 | return event 203 | } 204 | 205 | func (cfi *contextifyFramesIntegration) contextify(frames []Frame) []Frame { 206 | contextifiedFrames := make([]Frame, 0, len(frames)) 207 | 208 | for _, frame := range frames { 209 | if !frame.InApp { 210 | contextifiedFrames = append(contextifiedFrames, frame) 211 | continue 212 | } 213 | 214 | var path string 215 | 216 | if cachedPath, ok := cfi.cachedLocations.Load(frame.AbsPath); ok { 217 | if p, ok := cachedPath.(string); ok { 218 | path = p 219 | } 220 | } else { 221 | // Optimize for happy path here 222 | if fileExists(frame.AbsPath) { 223 | path = frame.AbsPath 224 | } else { 225 | path = cfi.findNearbySourceCodeLocation(frame.AbsPath) 226 | } 227 | } 228 | 229 | if path == "" { 230 | contextifiedFrames = append(contextifiedFrames, frame) 231 | continue 232 | } 233 | 234 | lines, contextLine := cfi.sr.readContextLines(path, frame.Lineno, cfi.contextLines) 235 | contextifiedFrames = append(contextifiedFrames, cfi.addContextLinesToFrame(frame, lines, contextLine)) 236 | } 237 | 238 | return contextifiedFrames 239 | } 240 | 241 | func (cfi *contextifyFramesIntegration) findNearbySourceCodeLocation(originalPath string) string { 242 | trimmedPath := strings.TrimPrefix(originalPath, "/") 243 | components := strings.Split(trimmedPath, "/") 244 | 245 | for len(components) > 0 { 246 | components = components[1:] 247 | possibleLocation := strings.Join(components, "/") 248 | 249 | if fileExists(possibleLocation) { 250 | cfi.cachedLocations.Store(originalPath, possibleLocation) 251 | return possibleLocation 252 | } 253 | } 254 | 255 | cfi.cachedLocations.Store(originalPath, "") 256 | return "" 257 | } 258 | 259 | func (cfi *contextifyFramesIntegration) addContextLinesToFrame(frame Frame, lines [][]byte, contextLine int) Frame { 260 | for i, line := range lines { 261 | switch { 262 | case i < contextLine: 263 | frame.PreContext = append(frame.PreContext, string(line)) 264 | case i == contextLine: 265 | frame.ContextLine = string(line) 266 | default: 267 | frame.PostContext = append(frame.PostContext, string(line)) 268 | } 269 | } 270 | return frame 271 | } 272 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/interfaces.go: -------------------------------------------------------------------------------- 1 | package sentry 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "net" 8 | "net/http" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | // Protocol Docs (kinda) 14 | // https://github.com/getsentry/rust-sentry-types/blob/master/src/protocol/v7.rs 15 | 16 | // Level marks the severity of the event 17 | type Level string 18 | 19 | const ( 20 | LevelDebug Level = "debug" 21 | LevelInfo Level = "info" 22 | LevelWarning Level = "warning" 23 | LevelError Level = "error" 24 | LevelFatal Level = "fatal" 25 | ) 26 | 27 | // https://docs.sentry.io/development/sdk-dev/event-payloads/sdk/ 28 | type SdkInfo struct { 29 | Name string `json:"name,omitempty"` 30 | Version string `json:"version,omitempty"` 31 | Integrations []string `json:"integrations,omitempty"` 32 | Packages []SdkPackage `json:"packages,omitempty"` 33 | } 34 | 35 | type SdkPackage struct { 36 | Name string `json:"name,omitempty"` 37 | Version string `json:"version,omitempty"` 38 | } 39 | 40 | // TODO: This type could be more useful, as map of interface{} is too generic 41 | // and requires a lot of type assertions in beforeBreadcrumb calls 42 | // plus it could just be `map[string]interface{}` then 43 | type BreadcrumbHint map[string]interface{} 44 | 45 | // https://docs.sentry.io/development/sdk-dev/event-payloads/breadcrumbs/ 46 | type Breadcrumb struct { 47 | Category string `json:"category,omitempty"` 48 | Data map[string]interface{} `json:"data,omitempty"` 49 | Level Level `json:"level,omitempty"` 50 | Message string `json:"message,omitempty"` 51 | Timestamp time.Time `json:"timestamp"` 52 | Type string `json:"type,omitempty"` 53 | } 54 | 55 | func (b *Breadcrumb) MarshalJSON() ([]byte, error) { 56 | type alias Breadcrumb 57 | // encoding/json doesn't support the "omitempty" option for struct types. 58 | // See https://golang.org/issues/11939. 59 | // This implementation of MarshalJSON shadows the original Timestamp field 60 | // forcing it to be omitted when the Timestamp is the zero value of 61 | // time.Time. 62 | if b.Timestamp.IsZero() { 63 | return json.Marshal(&struct { 64 | *alias 65 | Timestamp json.RawMessage `json:"timestamp,omitempty"` 66 | }{ 67 | alias: (*alias)(b), 68 | }) 69 | } 70 | return json.Marshal(&struct { 71 | *alias 72 | }{ 73 | alias: (*alias)(b), 74 | }) 75 | } 76 | 77 | // https://docs.sentry.io/development/sdk-dev/event-payloads/user/ 78 | type User struct { 79 | Email string `json:"email,omitempty"` 80 | ID string `json:"id,omitempty"` 81 | IPAddress string `json:"ip_address,omitempty"` 82 | Username string `json:"username,omitempty"` 83 | } 84 | 85 | // https://docs.sentry.io/development/sdk-dev/event-payloads/request/ 86 | type Request struct { 87 | URL string `json:"url,omitempty"` 88 | Method string `json:"method,omitempty"` 89 | Data string `json:"data,omitempty"` 90 | QueryString string `json:"query_string,omitempty"` 91 | Cookies string `json:"cookies,omitempty"` 92 | Headers map[string]string `json:"headers,omitempty"` 93 | Env map[string]string `json:"env,omitempty"` 94 | } 95 | 96 | // NewRequest returns a new Sentry Request from the given http.Request. 97 | // 98 | // NewRequest avoids operations that depend on network access. In particular, it 99 | // does not read r.Body. 100 | func NewRequest(r *http.Request) *Request { 101 | protocol := schemeHTTP 102 | if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" { 103 | protocol = schemeHTTPS 104 | } 105 | url := fmt.Sprintf("%s://%s%s", protocol, r.Host, r.URL.Path) 106 | 107 | // We read only the first Cookie header because of the specification: 108 | // https://tools.ietf.org/html/rfc6265#section-5.4 109 | // When the user agent generates an HTTP request, the user agent MUST NOT 110 | // attach more than one Cookie header field. 111 | cookies := r.Header.Get("Cookie") 112 | 113 | headers := make(map[string]string, len(r.Header)) 114 | for k, v := range r.Header { 115 | headers[k] = strings.Join(v, ",") 116 | } 117 | headers["Host"] = r.Host 118 | 119 | var env map[string]string 120 | if addr, port, err := net.SplitHostPort(r.RemoteAddr); err == nil { 121 | env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port} 122 | } 123 | 124 | return &Request{ 125 | URL: url, 126 | Method: r.Method, 127 | QueryString: r.URL.RawQuery, 128 | Cookies: cookies, 129 | Headers: headers, 130 | Env: env, 131 | } 132 | } 133 | 134 | // https://docs.sentry.io/development/sdk-dev/event-payloads/exception/ 135 | type Exception struct { 136 | Type string `json:"type,omitempty"` 137 | Value string `json:"value,omitempty"` 138 | Module string `json:"module,omitempty"` 139 | ThreadID string `json:"thread_id,omitempty"` 140 | Stacktrace *Stacktrace `json:"stacktrace,omitempty"` 141 | RawStacktrace *Stacktrace `json:"raw_stacktrace,omitempty"` 142 | } 143 | 144 | type EventID string 145 | 146 | // https://docs.sentry.io/development/sdk-dev/event-payloads/ 147 | type Event struct { 148 | Breadcrumbs []*Breadcrumb `json:"breadcrumbs,omitempty"` 149 | Contexts map[string]interface{} `json:"contexts,omitempty"` 150 | Dist string `json:"dist,omitempty"` 151 | Environment string `json:"environment,omitempty"` 152 | EventID EventID `json:"event_id,omitempty"` 153 | Extra map[string]interface{} `json:"extra,omitempty"` 154 | Fingerprint []string `json:"fingerprint,omitempty"` 155 | Level Level `json:"level,omitempty"` 156 | Message string `json:"message,omitempty"` 157 | Platform string `json:"platform,omitempty"` 158 | Release string `json:"release,omitempty"` 159 | Sdk SdkInfo `json:"sdk,omitempty"` 160 | ServerName string `json:"server_name,omitempty"` 161 | Threads []Thread `json:"threads,omitempty"` 162 | Tags map[string]string `json:"tags,omitempty"` 163 | Timestamp time.Time `json:"timestamp"` 164 | Transaction string `json:"transaction,omitempty"` 165 | User User `json:"user,omitempty"` 166 | Logger string `json:"logger,omitempty"` 167 | Modules map[string]string `json:"modules,omitempty"` 168 | Request *Request `json:"request,omitempty"` 169 | Exception []Exception `json:"exception,omitempty"` 170 | } 171 | 172 | func (e *Event) MarshalJSON() ([]byte, error) { 173 | type alias Event 174 | // encoding/json doesn't support the "omitempty" option for struct types. 175 | // See https://golang.org/issues/11939. 176 | // This implementation of MarshalJSON shadows the original Timestamp field 177 | // forcing it to be omitted when the Timestamp is the zero value of 178 | // time.Time. 179 | if e.Timestamp.IsZero() { 180 | return json.Marshal(&struct { 181 | *alias 182 | Timestamp json.RawMessage `json:"timestamp,omitempty"` 183 | }{ 184 | alias: (*alias)(e), 185 | }) 186 | } 187 | return json.Marshal(&struct { 188 | *alias 189 | }{ 190 | alias: (*alias)(e), 191 | }) 192 | } 193 | 194 | func NewEvent() *Event { 195 | event := Event{ 196 | Contexts: make(map[string]interface{}), 197 | Extra: make(map[string]interface{}), 198 | Tags: make(map[string]string), 199 | Modules: make(map[string]string), 200 | } 201 | return &event 202 | } 203 | 204 | type Thread struct { 205 | ID string `json:"id,omitempty"` 206 | Name string `json:"name,omitempty"` 207 | Stacktrace *Stacktrace `json:"stacktrace,omitempty"` 208 | RawStacktrace *Stacktrace `json:"raw_stacktrace,omitempty"` 209 | Crashed bool `json:"crashed,omitempty"` 210 | Current bool `json:"current,omitempty"` 211 | } 212 | 213 | type EventHint struct { 214 | Data interface{} 215 | EventID string 216 | OriginalException error 217 | RecoveredException interface{} 218 | Context context.Context 219 | Request *http.Request 220 | Response *http.Response 221 | } 222 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/sentry.go: -------------------------------------------------------------------------------- 1 | package sentry 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | // Version is the version of the sentry-go SDK. 9 | const Version = "0.6.1" 10 | 11 | // apiVersion is the minimum version of the Sentry API compatible with the 12 | // sentry-go SDK. 13 | const apiVersion = "7" 14 | 15 | // Init initializes whole SDK by creating new `Client` and binding it to the current `Hub` 16 | func Init(options ClientOptions) error { 17 | hub := CurrentHub() 18 | client, err := NewClient(options) 19 | if err != nil { 20 | return err 21 | } 22 | hub.BindClient(client) 23 | return nil 24 | } 25 | 26 | // AddBreadcrumb records a new breadcrumb. 27 | // 28 | // The total number of breadcrumbs that can be recorded are limited by the 29 | // configuration on the client. 30 | func AddBreadcrumb(breadcrumb *Breadcrumb) { 31 | hub := CurrentHub() 32 | hub.AddBreadcrumb(breadcrumb, nil) 33 | } 34 | 35 | // CaptureMessage captures an arbitrary message. 36 | func CaptureMessage(message string) *EventID { 37 | hub := CurrentHub() 38 | return hub.CaptureMessage(message) 39 | } 40 | 41 | // CaptureException captures an error. 42 | func CaptureException(exception error) *EventID { 43 | hub := CurrentHub() 44 | return hub.CaptureException(exception) 45 | } 46 | 47 | // CaptureEvent captures an event on the currently active client if any. 48 | // 49 | // The event must already be assembled. Typically code would instead use 50 | // the utility methods like `CaptureException`. The return value is the 51 | // event ID. In case Sentry is disabled or event was dropped, the return value will be nil. 52 | func CaptureEvent(event *Event) *EventID { 53 | hub := CurrentHub() 54 | return hub.CaptureEvent(event) 55 | } 56 | 57 | // Recover captures a panic. 58 | func Recover() *EventID { 59 | if err := recover(); err != nil { 60 | hub := CurrentHub() 61 | return hub.Recover(err) 62 | } 63 | return nil 64 | } 65 | 66 | // Recover captures a panic and passes relevant context object. 67 | func RecoverWithContext(ctx context.Context) *EventID { 68 | if err := recover(); err != nil { 69 | var hub *Hub 70 | 71 | if HasHubOnContext(ctx) { 72 | hub = GetHubFromContext(ctx) 73 | } else { 74 | hub = CurrentHub() 75 | } 76 | 77 | return hub.RecoverWithContext(ctx, err) 78 | } 79 | return nil 80 | } 81 | 82 | // WithScope temporarily pushes a scope for a single call. 83 | // 84 | // This function takes one argument, a callback that executes 85 | // in the context of that scope. 86 | // 87 | // This is useful when extra data should be send with a single capture call 88 | // for instance a different level or tags 89 | func WithScope(f func(scope *Scope)) { 90 | hub := CurrentHub() 91 | hub.WithScope(f) 92 | } 93 | 94 | // ConfigureScope invokes a function that can modify the current scope. 95 | // 96 | // The function is passed a mutable reference to the `Scope` so that modifications 97 | // can be performed. 98 | func ConfigureScope(f func(scope *Scope)) { 99 | hub := CurrentHub() 100 | hub.ConfigureScope(f) 101 | } 102 | 103 | // PushScope pushes a new scope. 104 | func PushScope() { 105 | hub := CurrentHub() 106 | hub.PushScope() 107 | } 108 | 109 | // PopScope pushes a new scope. 110 | func PopScope() { 111 | hub := CurrentHub() 112 | hub.PopScope() 113 | } 114 | 115 | // Flush waits until the underlying Transport sends any buffered events to the 116 | // Sentry server, blocking for at most the given timeout. It returns false if 117 | // the timeout was reached. In that case, some events may not have been sent. 118 | // 119 | // Flush should be called before terminating the program to avoid 120 | // unintentionally dropping events. 121 | // 122 | // Do not call Flush indiscriminately after every call to CaptureEvent, 123 | // CaptureException or CaptureMessage. Instead, to have the SDK send events over 124 | // the network synchronously, configure it to use the HTTPSyncTransport in the 125 | // call to Init. 126 | func Flush(timeout time.Duration) bool { 127 | hub := CurrentHub() 128 | return hub.Flush(timeout) 129 | } 130 | 131 | // LastEventID returns an ID of last captured event. 132 | func LastEventID() EventID { 133 | hub := CurrentHub() 134 | return hub.LastEventID() 135 | } 136 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/sourcereader.go: -------------------------------------------------------------------------------- 1 | package sentry 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "sync" 7 | ) 8 | 9 | type sourceReader struct { 10 | mu sync.Mutex 11 | cache map[string][][]byte 12 | } 13 | 14 | func newSourceReader() sourceReader { 15 | return sourceReader{ 16 | cache: make(map[string][][]byte), 17 | } 18 | } 19 | 20 | func (sr *sourceReader) readContextLines(filename string, line, context int) ([][]byte, int) { 21 | sr.mu.Lock() 22 | defer sr.mu.Unlock() 23 | 24 | lines, ok := sr.cache[filename] 25 | 26 | if !ok { 27 | data, err := ioutil.ReadFile(filename) 28 | if err != nil { 29 | sr.cache[filename] = nil 30 | return nil, 0 31 | } 32 | lines = bytes.Split(data, []byte{'\n'}) 33 | sr.cache[filename] = lines 34 | } 35 | 36 | return sr.calculateContextLines(lines, line, context) 37 | } 38 | 39 | // `contextLine` points to a line that caused an issue itself, in relation to returned slice 40 | func (sr *sourceReader) calculateContextLines(lines [][]byte, line, context int) ([][]byte, int) { 41 | // Stacktrace lines are 1-indexed, slices are 0-indexed 42 | line-- 43 | 44 | contextLine := context 45 | 46 | if lines == nil || line >= len(lines) || line < 0 { 47 | return nil, 0 48 | } 49 | 50 | if context < 0 { 51 | context = 0 52 | contextLine = 0 53 | } 54 | 55 | start := line - context 56 | 57 | if start < 0 { 58 | contextLine += start 59 | start = 0 60 | } 61 | 62 | end := line + context + 1 63 | 64 | if end > len(lines) { 65 | end = len(lines) 66 | } 67 | 68 | return lines[start:end], contextLine 69 | } 70 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/stacktrace.go: -------------------------------------------------------------------------------- 1 | package sentry 2 | 3 | import ( 4 | "go/build" 5 | "path/filepath" 6 | "reflect" 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | const unknown string = "unknown" 12 | 13 | // The module download is split into two parts: downloading the go.mod and downloading the actual code. 14 | // If you have dependencies only needed for tests, then they will show up in your go.mod, 15 | // and go get will download their go.mods, but it will not download their code. 16 | // The test-only dependencies get downloaded only when you need it, such as the first time you run go test. 17 | // 18 | // https://github.com/golang/go/issues/26913#issuecomment-411976222 19 | 20 | // Stacktrace holds information about the frames of the stack. 21 | type Stacktrace struct { 22 | Frames []Frame `json:"frames,omitempty"` 23 | FramesOmitted []uint `json:"frames_omitted,omitempty"` 24 | } 25 | 26 | // NewStacktrace creates a stacktrace using `runtime.Callers`. 27 | func NewStacktrace() *Stacktrace { 28 | pcs := make([]uintptr, 100) 29 | n := runtime.Callers(1, pcs) 30 | 31 | if n == 0 { 32 | return nil 33 | } 34 | 35 | frames := extractFrames(pcs[:n]) 36 | frames = filterFrames(frames) 37 | 38 | stacktrace := Stacktrace{ 39 | Frames: frames, 40 | } 41 | 42 | return &stacktrace 43 | } 44 | 45 | // ExtractStacktrace creates a new `Stacktrace` based on the given `error` object. 46 | // TODO: Make it configurable so that anyone can provide their own implementation? 47 | // Use of reflection allows us to not have a hard dependency on any given package, so we don't have to import it 48 | func ExtractStacktrace(err error) *Stacktrace { 49 | method := extractReflectedStacktraceMethod(err) 50 | 51 | if !method.IsValid() { 52 | return nil 53 | } 54 | 55 | pcs := extractPcs(method) 56 | 57 | if len(pcs) == 0 { 58 | return nil 59 | } 60 | 61 | frames := extractFrames(pcs) 62 | frames = filterFrames(frames) 63 | 64 | stacktrace := Stacktrace{ 65 | Frames: frames, 66 | } 67 | 68 | return &stacktrace 69 | } 70 | 71 | func extractReflectedStacktraceMethod(err error) reflect.Value { 72 | var method reflect.Value 73 | 74 | // https://github.com/pingcap/errors 75 | methodGetStackTracer := reflect.ValueOf(err).MethodByName("GetStackTracer") 76 | // https://github.com/pkg/errors 77 | methodStackTrace := reflect.ValueOf(err).MethodByName("StackTrace") 78 | // https://github.com/go-errors/errors 79 | methodStackFrames := reflect.ValueOf(err).MethodByName("StackFrames") 80 | 81 | if methodGetStackTracer.IsValid() { 82 | stacktracer := methodGetStackTracer.Call(make([]reflect.Value, 0))[0] 83 | stacktracerStackTrace := reflect.ValueOf(stacktracer).MethodByName("StackTrace") 84 | 85 | if stacktracerStackTrace.IsValid() { 86 | method = stacktracerStackTrace 87 | } 88 | } 89 | 90 | if methodStackTrace.IsValid() { 91 | method = methodStackTrace 92 | } 93 | 94 | if methodStackFrames.IsValid() { 95 | method = methodStackFrames 96 | } 97 | 98 | return method 99 | } 100 | 101 | func extractPcs(method reflect.Value) []uintptr { 102 | var pcs []uintptr 103 | 104 | stacktrace := method.Call(make([]reflect.Value, 0))[0] 105 | 106 | if stacktrace.Kind() != reflect.Slice { 107 | return nil 108 | } 109 | 110 | for i := 0; i < stacktrace.Len(); i++ { 111 | pc := stacktrace.Index(i) 112 | 113 | if pc.Kind() == reflect.Uintptr { 114 | pcs = append(pcs, uintptr(pc.Uint())) 115 | continue 116 | } 117 | 118 | if pc.Kind() == reflect.Struct { 119 | field := pc.FieldByName("ProgramCounter") 120 | if field.IsValid() && field.Kind() == reflect.Uintptr { 121 | pcs = append(pcs, uintptr(field.Uint())) 122 | continue 123 | } 124 | } 125 | } 126 | 127 | return pcs 128 | } 129 | 130 | // https://docs.sentry.io/development/sdk-dev/event-payloads/stacktrace/ 131 | type Frame struct { 132 | Function string `json:"function,omitempty"` 133 | Symbol string `json:"symbol,omitempty"` 134 | Module string `json:"module,omitempty"` 135 | Package string `json:"package,omitempty"` 136 | Filename string `json:"filename,omitempty"` 137 | AbsPath string `json:"abs_path,omitempty"` 138 | Lineno int `json:"lineno,omitempty"` 139 | Colno int `json:"colno,omitempty"` 140 | PreContext []string `json:"pre_context,omitempty"` 141 | ContextLine string `json:"context_line,omitempty"` 142 | PostContext []string `json:"post_context,omitempty"` 143 | InApp bool `json:"in_app,omitempty"` 144 | Vars map[string]interface{} `json:"vars,omitempty"` 145 | } 146 | 147 | // NewFrame assembles a stacktrace frame out of `runtime.Frame`. 148 | func NewFrame(f runtime.Frame) Frame { 149 | abspath := f.File 150 | filename := f.File 151 | function := f.Function 152 | var pkg string 153 | 154 | if filename != "" { 155 | filename = filepath.Base(filename) 156 | } else { 157 | filename = unknown 158 | } 159 | 160 | if abspath == "" { 161 | abspath = unknown 162 | } 163 | 164 | if function != "" { 165 | pkg, function = splitQualifiedFunctionName(function) 166 | } 167 | 168 | frame := Frame{ 169 | AbsPath: abspath, 170 | Filename: filename, 171 | Lineno: f.Line, 172 | Module: pkg, 173 | Function: function, 174 | } 175 | 176 | frame.InApp = isInAppFrame(frame) 177 | 178 | return frame 179 | } 180 | 181 | // splitQualifiedFunctionName splits a package path-qualified function name into 182 | // package name and function name. Such qualified names are found in 183 | // runtime.Frame.Function values. 184 | func splitQualifiedFunctionName(name string) (pkg string, fun string) { 185 | pkg = packageName(name) 186 | fun = strings.TrimPrefix(name, pkg+".") 187 | return 188 | } 189 | 190 | func extractFrames(pcs []uintptr) []Frame { 191 | var frames []Frame 192 | callersFrames := runtime.CallersFrames(pcs) 193 | 194 | for { 195 | callerFrame, more := callersFrames.Next() 196 | 197 | frames = append([]Frame{ 198 | NewFrame(callerFrame), 199 | }, frames...) 200 | 201 | if !more { 202 | break 203 | } 204 | } 205 | 206 | return frames 207 | } 208 | 209 | // filterFrames filters out stack frames that are not meant to be reported to 210 | // Sentry. Those are frames internal to the SDK or Go. 211 | func filterFrames(frames []Frame) []Frame { 212 | if len(frames) == 0 { 213 | return nil 214 | } 215 | 216 | filteredFrames := make([]Frame, 0, len(frames)) 217 | 218 | for _, frame := range frames { 219 | // Skip Go internal frames. 220 | if frame.Module == "runtime" || frame.Module == "testing" { 221 | continue 222 | } 223 | // Skip Sentry internal frames, except for frames in _test packages (for 224 | // testing). 225 | if strings.HasPrefix(frame.Module, "github.com/getsentry/sentry-go") && 226 | !strings.HasSuffix(frame.Module, "_test") { 227 | continue 228 | } 229 | filteredFrames = append(filteredFrames, frame) 230 | } 231 | 232 | return filteredFrames 233 | } 234 | 235 | func isInAppFrame(frame Frame) bool { 236 | if strings.HasPrefix(frame.AbsPath, build.Default.GOROOT) || 237 | strings.Contains(frame.Module, "vendor") || 238 | strings.Contains(frame.Module, "third_party") { 239 | return false 240 | } 241 | 242 | return true 243 | } 244 | 245 | func callerFunctionName() string { 246 | pcs := make([]uintptr, 1) 247 | runtime.Callers(3, pcs) 248 | callersFrames := runtime.CallersFrames(pcs) 249 | callerFrame, _ := callersFrames.Next() 250 | return baseName(callerFrame.Function) 251 | } 252 | 253 | // packageName returns the package part of the symbol name, or the empty string 254 | // if there is none. 255 | // It replicates https://golang.org/pkg/debug/gosym/#Sym.PackageName, avoiding a 256 | // dependency on debug/gosym. 257 | func packageName(name string) string { 258 | // A prefix of "type." and "go." is a compiler-generated symbol that doesn't belong to any package. 259 | // See variable reservedimports in cmd/compile/internal/gc/subr.go 260 | if strings.HasPrefix(name, "go.") || strings.HasPrefix(name, "type.") { 261 | return "" 262 | } 263 | 264 | pathend := strings.LastIndex(name, "/") 265 | if pathend < 0 { 266 | pathend = 0 267 | } 268 | 269 | if i := strings.Index(name[pathend:], "."); i != -1 { 270 | return name[:pathend+i] 271 | } 272 | return "" 273 | } 274 | 275 | // baseName returns the symbol name without the package or receiver name. 276 | // It replicates https://golang.org/pkg/debug/gosym/#Sym.BaseName, avoiding a 277 | // dependency on debug/gosym. 278 | func baseName(name string) string { 279 | if i := strings.LastIndex(name, "."); i != -1 { 280 | return name[i+1:] 281 | } 282 | return name 283 | } 284 | -------------------------------------------------------------------------------- /vendor/github.com/getsentry/sentry-go/util.go: -------------------------------------------------------------------------------- 1 | package sentry 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/hex" 6 | "encoding/json" 7 | "fmt" 8 | "io" 9 | "os" 10 | ) 11 | 12 | func uuid() string { 13 | id := make([]byte, 16) 14 | _, _ = io.ReadFull(rand.Reader, id) 15 | id[6] &= 0x0F // clear version 16 | id[6] |= 0x40 // set version to 4 (random uuid) 17 | id[8] &= 0x3F // clear variant 18 | id[8] |= 0x80 // set to IETF variant 19 | return hex.EncodeToString(id) 20 | } 21 | 22 | func fileExists(fileName string) bool { 23 | if _, err := os.Stat(fileName); err != nil { 24 | return false 25 | } 26 | 27 | return true 28 | } 29 | 30 | //nolint: deadcode, unused 31 | func prettyPrint(data interface{}) { 32 | dbg, _ := json.MarshalIndent(data, "", " ") 33 | fmt.Println(string(dbg)) 34 | } 35 | -------------------------------------------------------------------------------- /vendor/github.com/pmezard/go-difflib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Patrick Mezard 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | The names of its contributors may not be used to endorse or promote 14 | products derived from this software without specific prior written 15 | permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentFormat}} 2 | func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { 3 | if h, ok := t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) 5 | } 6 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { 3 | if h, ok := a.t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 5 | } 6 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_order.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { 9 | switch kind { 10 | case reflect.Int: 11 | { 12 | intobj1 := obj1.(int) 13 | intobj2 := obj2.(int) 14 | if intobj1 > intobj2 { 15 | return -1, true 16 | } 17 | if intobj1 == intobj2 { 18 | return 0, true 19 | } 20 | if intobj1 < intobj2 { 21 | return 1, true 22 | } 23 | } 24 | case reflect.Int8: 25 | { 26 | int8obj1 := obj1.(int8) 27 | int8obj2 := obj2.(int8) 28 | if int8obj1 > int8obj2 { 29 | return -1, true 30 | } 31 | if int8obj1 == int8obj2 { 32 | return 0, true 33 | } 34 | if int8obj1 < int8obj2 { 35 | return 1, true 36 | } 37 | } 38 | case reflect.Int16: 39 | { 40 | int16obj1 := obj1.(int16) 41 | int16obj2 := obj2.(int16) 42 | if int16obj1 > int16obj2 { 43 | return -1, true 44 | } 45 | if int16obj1 == int16obj2 { 46 | return 0, true 47 | } 48 | if int16obj1 < int16obj2 { 49 | return 1, true 50 | } 51 | } 52 | case reflect.Int32: 53 | { 54 | int32obj1 := obj1.(int32) 55 | int32obj2 := obj2.(int32) 56 | if int32obj1 > int32obj2 { 57 | return -1, true 58 | } 59 | if int32obj1 == int32obj2 { 60 | return 0, true 61 | } 62 | if int32obj1 < int32obj2 { 63 | return 1, true 64 | } 65 | } 66 | case reflect.Int64: 67 | { 68 | int64obj1 := obj1.(int64) 69 | int64obj2 := obj2.(int64) 70 | if int64obj1 > int64obj2 { 71 | return -1, true 72 | } 73 | if int64obj1 == int64obj2 { 74 | return 0, true 75 | } 76 | if int64obj1 < int64obj2 { 77 | return 1, true 78 | } 79 | } 80 | case reflect.Uint: 81 | { 82 | uintobj1 := obj1.(uint) 83 | uintobj2 := obj2.(uint) 84 | if uintobj1 > uintobj2 { 85 | return -1, true 86 | } 87 | if uintobj1 == uintobj2 { 88 | return 0, true 89 | } 90 | if uintobj1 < uintobj2 { 91 | return 1, true 92 | } 93 | } 94 | case reflect.Uint8: 95 | { 96 | uint8obj1 := obj1.(uint8) 97 | uint8obj2 := obj2.(uint8) 98 | if uint8obj1 > uint8obj2 { 99 | return -1, true 100 | } 101 | if uint8obj1 == uint8obj2 { 102 | return 0, true 103 | } 104 | if uint8obj1 < uint8obj2 { 105 | return 1, true 106 | } 107 | } 108 | case reflect.Uint16: 109 | { 110 | uint16obj1 := obj1.(uint16) 111 | uint16obj2 := obj2.(uint16) 112 | if uint16obj1 > uint16obj2 { 113 | return -1, true 114 | } 115 | if uint16obj1 == uint16obj2 { 116 | return 0, true 117 | } 118 | if uint16obj1 < uint16obj2 { 119 | return 1, true 120 | } 121 | } 122 | case reflect.Uint32: 123 | { 124 | uint32obj1 := obj1.(uint32) 125 | uint32obj2 := obj2.(uint32) 126 | if uint32obj1 > uint32obj2 { 127 | return -1, true 128 | } 129 | if uint32obj1 == uint32obj2 { 130 | return 0, true 131 | } 132 | if uint32obj1 < uint32obj2 { 133 | return 1, true 134 | } 135 | } 136 | case reflect.Uint64: 137 | { 138 | uint64obj1 := obj1.(uint64) 139 | uint64obj2 := obj2.(uint64) 140 | if uint64obj1 > uint64obj2 { 141 | return -1, true 142 | } 143 | if uint64obj1 == uint64obj2 { 144 | return 0, true 145 | } 146 | if uint64obj1 < uint64obj2 { 147 | return 1, true 148 | } 149 | } 150 | case reflect.Float32: 151 | { 152 | float32obj1 := obj1.(float32) 153 | float32obj2 := obj2.(float32) 154 | if float32obj1 > float32obj2 { 155 | return -1, true 156 | } 157 | if float32obj1 == float32obj2 { 158 | return 0, true 159 | } 160 | if float32obj1 < float32obj2 { 161 | return 1, true 162 | } 163 | } 164 | case reflect.Float64: 165 | { 166 | float64obj1 := obj1.(float64) 167 | float64obj2 := obj2.(float64) 168 | if float64obj1 > float64obj2 { 169 | return -1, true 170 | } 171 | if float64obj1 == float64obj2 { 172 | return 0, true 173 | } 174 | if float64obj1 < float64obj2 { 175 | return 1, true 176 | } 177 | } 178 | case reflect.String: 179 | { 180 | stringobj1 := obj1.(string) 181 | stringobj2 := obj2.(string) 182 | if stringobj1 > stringobj2 { 183 | return -1, true 184 | } 185 | if stringobj1 == stringobj2 { 186 | return 0, true 187 | } 188 | if stringobj1 < stringobj2 { 189 | return 1, true 190 | } 191 | } 192 | } 193 | 194 | return 0, false 195 | } 196 | 197 | // Greater asserts that the first element is greater than the second 198 | // 199 | // assert.Greater(t, 2, 1) 200 | // assert.Greater(t, float64(2), float64(1)) 201 | // assert.Greater(t, "b", "a") 202 | func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 203 | if h, ok := t.(tHelper); ok { 204 | h.Helper() 205 | } 206 | 207 | e1Kind := reflect.ValueOf(e1).Kind() 208 | e2Kind := reflect.ValueOf(e2).Kind() 209 | if e1Kind != e2Kind { 210 | return Fail(t, "Elements should be the same type", msgAndArgs...) 211 | } 212 | 213 | res, isComparable := compare(e1, e2, e1Kind) 214 | if !isComparable { 215 | return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) 216 | } 217 | 218 | if res != -1 { 219 | return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...) 220 | } 221 | 222 | return true 223 | } 224 | 225 | // GreaterOrEqual asserts that the first element is greater than or equal to the second 226 | // 227 | // assert.GreaterOrEqual(t, 2, 1) 228 | // assert.GreaterOrEqual(t, 2, 2) 229 | // assert.GreaterOrEqual(t, "b", "a") 230 | // assert.GreaterOrEqual(t, "b", "b") 231 | func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 232 | if h, ok := t.(tHelper); ok { 233 | h.Helper() 234 | } 235 | 236 | e1Kind := reflect.ValueOf(e1).Kind() 237 | e2Kind := reflect.ValueOf(e2).Kind() 238 | if e1Kind != e2Kind { 239 | return Fail(t, "Elements should be the same type", msgAndArgs...) 240 | } 241 | 242 | res, isComparable := compare(e1, e2, e1Kind) 243 | if !isComparable { 244 | return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) 245 | } 246 | 247 | if res != -1 && res != 0 { 248 | return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...) 249 | } 250 | 251 | return true 252 | } 253 | 254 | // Less asserts that the first element is less than the second 255 | // 256 | // assert.Less(t, 1, 2) 257 | // assert.Less(t, float64(1), float64(2)) 258 | // assert.Less(t, "a", "b") 259 | func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 260 | if h, ok := t.(tHelper); ok { 261 | h.Helper() 262 | } 263 | 264 | e1Kind := reflect.ValueOf(e1).Kind() 265 | e2Kind := reflect.ValueOf(e2).Kind() 266 | if e1Kind != e2Kind { 267 | return Fail(t, "Elements should be the same type", msgAndArgs...) 268 | } 269 | 270 | res, isComparable := compare(e1, e2, e1Kind) 271 | if !isComparable { 272 | return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) 273 | } 274 | 275 | if res != 1 { 276 | return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...) 277 | } 278 | 279 | return true 280 | } 281 | 282 | // LessOrEqual asserts that the first element is less than or equal to the second 283 | // 284 | // assert.LessOrEqual(t, 1, 2) 285 | // assert.LessOrEqual(t, 2, 2) 286 | // assert.LessOrEqual(t, "a", "b") 287 | // assert.LessOrEqual(t, "b", "b") 288 | func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { 289 | if h, ok := t.(tHelper); ok { 290 | h.Helper() 291 | } 292 | 293 | e1Kind := reflect.ValueOf(e1).Kind() 294 | e2Kind := reflect.ValueOf(e2).Kind() 295 | if e1Kind != e2Kind { 296 | return Fail(t, "Elements should be the same type", msgAndArgs...) 297 | } 298 | 299 | res, isComparable := compare(e1, e2, e1Kind) 300 | if !isComparable { 301 | return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) 302 | } 303 | 304 | if res != 1 && res != 0 { 305 | return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...) 306 | } 307 | 308 | return true 309 | } 310 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/doc.go: -------------------------------------------------------------------------------- 1 | // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. 2 | // 3 | // Example Usage 4 | // 5 | // The following is a complete example using assert in a standard test function: 6 | // import ( 7 | // "testing" 8 | // "github.com/stretchr/testify/assert" 9 | // ) 10 | // 11 | // func TestSomething(t *testing.T) { 12 | // 13 | // var a string = "Hello" 14 | // var b string = "Hello" 15 | // 16 | // assert.Equal(t, a, b, "The two words should be the same.") 17 | // 18 | // } 19 | // 20 | // if you assert many times, use the format below: 21 | // 22 | // import ( 23 | // "testing" 24 | // "github.com/stretchr/testify/assert" 25 | // ) 26 | // 27 | // func TestSomething(t *testing.T) { 28 | // assert := assert.New(t) 29 | // 30 | // var a string = "Hello" 31 | // var b string = "Hello" 32 | // 33 | // assert.Equal(a, b, "The two words should be the same.") 34 | // } 35 | // 36 | // Assertions 37 | // 38 | // Assertions allow you to easily write test code, and are global funcs in the `assert` package. 39 | // All assertion functions take, as the first argument, the `*testing.T` object provided by the 40 | // testing framework. This allows the assertion funcs to write the failings and other details to 41 | // the correct place. 42 | // 43 | // Every assertion function also takes an optional string message as the final argument, 44 | // allowing custom error messages to be appended to the message the assertion method outputs. 45 | package assert 46 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/errors.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // AnError is an error instance useful for testing. If the code does not care 8 | // about error specifics, and only needs to return the error for example, this 9 | // error should be used to make the test code more readable. 10 | var AnError = errors.New("assert.AnError general error for testing") 11 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/forward_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs 17 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/http_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "net/url" 8 | "strings" 9 | ) 10 | 11 | // httpCode is a helper that returns HTTP code of the response. It returns -1 and 12 | // an error if building a new request fails. 13 | func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { 14 | w := httptest.NewRecorder() 15 | req, err := http.NewRequest(method, url, nil) 16 | if err != nil { 17 | return -1, err 18 | } 19 | req.URL.RawQuery = values.Encode() 20 | handler(w, req) 21 | return w.Code, nil 22 | } 23 | 24 | // HTTPSuccess asserts that a specified handler returns a success status code. 25 | // 26 | // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) 27 | // 28 | // Returns whether the assertion was successful (true) or not (false). 29 | func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 30 | if h, ok := t.(tHelper); ok { 31 | h.Helper() 32 | } 33 | code, err := httpCode(handler, method, url, values) 34 | if err != nil { 35 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) 36 | return false 37 | } 38 | 39 | isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent 40 | if !isSuccessCode { 41 | Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code)) 42 | } 43 | 44 | return isSuccessCode 45 | } 46 | 47 | // HTTPRedirect asserts that a specified handler returns a redirect status code. 48 | // 49 | // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} 50 | // 51 | // Returns whether the assertion was successful (true) or not (false). 52 | func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 53 | if h, ok := t.(tHelper); ok { 54 | h.Helper() 55 | } 56 | code, err := httpCode(handler, method, url, values) 57 | if err != nil { 58 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) 59 | return false 60 | } 61 | 62 | isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect 63 | if !isRedirectCode { 64 | Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code)) 65 | } 66 | 67 | return isRedirectCode 68 | } 69 | 70 | // HTTPError asserts that a specified handler returns an error status code. 71 | // 72 | // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} 73 | // 74 | // Returns whether the assertion was successful (true) or not (false). 75 | func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 76 | if h, ok := t.(tHelper); ok { 77 | h.Helper() 78 | } 79 | code, err := httpCode(handler, method, url, values) 80 | if err != nil { 81 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) 82 | return false 83 | } 84 | 85 | isErrorCode := code >= http.StatusBadRequest 86 | if !isErrorCode { 87 | Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code)) 88 | } 89 | 90 | return isErrorCode 91 | } 92 | 93 | // HTTPBody is a helper that returns HTTP body of the response. It returns 94 | // empty string if building a new request fails. 95 | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { 96 | w := httptest.NewRecorder() 97 | req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) 98 | if err != nil { 99 | return "" 100 | } 101 | handler(w, req) 102 | return w.Body.String() 103 | } 104 | 105 | // HTTPBodyContains asserts that a specified handler returns a 106 | // body that contains a string. 107 | // 108 | // assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") 109 | // 110 | // Returns whether the assertion was successful (true) or not (false). 111 | func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { 112 | if h, ok := t.(tHelper); ok { 113 | h.Helper() 114 | } 115 | body := HTTPBody(handler, method, url, values) 116 | 117 | contains := strings.Contains(body, fmt.Sprint(str)) 118 | if !contains { 119 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 120 | } 121 | 122 | return contains 123 | } 124 | 125 | // HTTPBodyNotContains asserts that a specified handler returns a 126 | // body that does not contain a string. 127 | // 128 | // assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") 129 | // 130 | // Returns whether the assertion was successful (true) or not (false). 131 | func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { 132 | if h, ok := t.(tHelper); ok { 133 | h.Helper() 134 | } 135 | body := HTTPBody(handler, method, url, values) 136 | 137 | contains := strings.Contains(body, fmt.Sprint(str)) 138 | if contains { 139 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 140 | } 141 | 142 | return !contains 143 | } 144 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/doc.go: -------------------------------------------------------------------------------- 1 | // Package require implements the same assertions as the `assert` package but 2 | // stops test execution when a test fails. 3 | // 4 | // Example Usage 5 | // 6 | // The following is a complete example using require in a standard test function: 7 | // import ( 8 | // "testing" 9 | // "github.com/stretchr/testify/require" 10 | // ) 11 | // 12 | // func TestSomething(t *testing.T) { 13 | // 14 | // var a string = "Hello" 15 | // var b string = "Hello" 16 | // 17 | // require.Equal(t, a, b, "The two words should be the same.") 18 | // 19 | // } 20 | // 21 | // Assertions 22 | // 23 | // The `require` package have same global functions as in the `assert` package, 24 | // but instead of returning a boolean result they call `t.FailNow()`. 25 | // 26 | // Every assertion function also takes an optional string message as the final argument, 27 | // allowing custom error messages to be appended to the message the assertion method outputs. 28 | package require 29 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/forward_requirements.go: -------------------------------------------------------------------------------- 1 | package require 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl -include-format-funcs 17 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/require.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.Comment}} 2 | func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { 3 | if h, ok := t.(tHelper); ok { h.Helper() } 4 | if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } 5 | t.FailNow() 6 | } 7 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/require_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { 3 | if h, ok := a.t.(tHelper); ok { h.Helper() } 4 | {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 5 | } 6 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/requirements.go: -------------------------------------------------------------------------------- 1 | package require 2 | 3 | // TestingT is an interface wrapper around *testing.T 4 | type TestingT interface { 5 | Errorf(format string, args ...interface{}) 6 | FailNow() 7 | } 8 | 9 | type tHelper interface { 10 | Helper() 11 | } 12 | 13 | // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful 14 | // for table driven tests. 15 | type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) 16 | 17 | // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful 18 | // for table driven tests. 19 | type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) 20 | 21 | // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful 22 | // for table driven tests. 23 | type BoolAssertionFunc func(TestingT, bool, ...interface{}) 24 | 25 | // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful 26 | // for table driven tests. 27 | type ErrorAssertionFunc func(TestingT, error, ...interface{}) 28 | 29 | //go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl -include-format-funcs 30 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.4 5 | - 1.5 6 | - 1.6 7 | - 1.7 8 | - 1.8 9 | - 1.9 10 | - tip 11 | 12 | go_import_path: gopkg.in/yaml.v2 13 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/LICENSE.libyaml: -------------------------------------------------------------------------------- 1 | The following files were ported to Go from C files of libyaml, and thus 2 | are still covered by their original copyright and license: 3 | 4 | apic.go 5 | emitterc.go 6 | parserc.go 7 | readerc.go 8 | scannerc.go 9 | writerc.go 10 | yamlh.go 11 | yamlprivateh.go 12 | 13 | Copyright (c) 2006 Kirill Simonov 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy of 16 | this software and associated documentation files (the "Software"), to deal in 17 | the Software without restriction, including without limitation the rights to 18 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 19 | of the Software, and to permit persons to whom the Software is furnished to do 20 | so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2011-2016 Canonical Ltd. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/README.md: -------------------------------------------------------------------------------- 1 | # YAML support for the Go language 2 | 3 | Introduction 4 | ------------ 5 | 6 | The yaml package enables Go programs to comfortably encode and decode YAML 7 | values. It was developed within [Canonical](https://www.canonical.com) as 8 | part of the [juju](https://juju.ubuntu.com) project, and is based on a 9 | pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) 10 | C library to parse and generate YAML data quickly and reliably. 11 | 12 | Compatibility 13 | ------------- 14 | 15 | The yaml package supports most of YAML 1.1 and 1.2, including support for 16 | anchors, tags, map merging, etc. Multi-document unmarshalling is not yet 17 | implemented, and base-60 floats from YAML 1.1 are purposefully not 18 | supported since they're a poor design and are gone in YAML 1.2. 19 | 20 | Installation and usage 21 | ---------------------- 22 | 23 | The import path for the package is *gopkg.in/yaml.v2*. 24 | 25 | To install it, run: 26 | 27 | go get gopkg.in/yaml.v2 28 | 29 | API documentation 30 | ----------------- 31 | 32 | If opened in a browser, the import path itself leads to the API documentation: 33 | 34 | * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) 35 | 36 | API stability 37 | ------------- 38 | 39 | The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). 40 | 41 | 42 | License 43 | ------- 44 | 45 | The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details. 46 | 47 | 48 | Example 49 | ------- 50 | 51 | ```Go 52 | package main 53 | 54 | import ( 55 | "fmt" 56 | "log" 57 | 58 | "gopkg.in/yaml.v2" 59 | ) 60 | 61 | var data = ` 62 | a: Easy! 63 | b: 64 | c: 2 65 | d: [3, 4] 66 | ` 67 | 68 | // Note: struct fields must be public in order for unmarshal to 69 | // correctly populate the data. 70 | type T struct { 71 | A string 72 | B struct { 73 | RenamedC int `yaml:"c"` 74 | D []int `yaml:",flow"` 75 | } 76 | } 77 | 78 | func main() { 79 | t := T{} 80 | 81 | err := yaml.Unmarshal([]byte(data), &t) 82 | if err != nil { 83 | log.Fatalf("error: %v", err) 84 | } 85 | fmt.Printf("--- t:\n%v\n\n", t) 86 | 87 | d, err := yaml.Marshal(&t) 88 | if err != nil { 89 | log.Fatalf("error: %v", err) 90 | } 91 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 92 | 93 | m := make(map[interface{}]interface{}) 94 | 95 | err = yaml.Unmarshal([]byte(data), &m) 96 | if err != nil { 97 | log.Fatalf("error: %v", err) 98 | } 99 | fmt.Printf("--- m:\n%v\n\n", m) 100 | 101 | d, err = yaml.Marshal(&m) 102 | if err != nil { 103 | log.Fatalf("error: %v", err) 104 | } 105 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 106 | } 107 | ``` 108 | 109 | This example will generate the following output: 110 | 111 | ``` 112 | --- t: 113 | {Easy! {2 [3 4]}} 114 | 115 | --- t dump: 116 | a: Easy! 117 | b: 118 | c: 2 119 | d: [3, 4] 120 | 121 | 122 | --- m: 123 | map[a:Easy! b:map[c:2 d:[3 4]]] 124 | 125 | --- m dump: 126 | a: Easy! 127 | b: 128 | c: 2 129 | d: 130 | - 3 131 | - 4 132 | ``` 133 | 134 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/encode.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "encoding" 5 | "fmt" 6 | "io" 7 | "reflect" 8 | "regexp" 9 | "sort" 10 | "strconv" 11 | "strings" 12 | "time" 13 | "unicode/utf8" 14 | ) 15 | 16 | // jsonNumber is the interface of the encoding/json.Number datatype. 17 | // Repeating the interface here avoids a dependency on encoding/json, and also 18 | // supports other libraries like jsoniter, which use a similar datatype with 19 | // the same interface. Detecting this interface is useful when dealing with 20 | // structures containing json.Number, which is a string under the hood. The 21 | // encoder should prefer the use of Int64(), Float64() and string(), in that 22 | // order, when encoding this type. 23 | type jsonNumber interface { 24 | Float64() (float64, error) 25 | Int64() (int64, error) 26 | String() string 27 | } 28 | 29 | type encoder struct { 30 | emitter yaml_emitter_t 31 | event yaml_event_t 32 | out []byte 33 | flow bool 34 | // doneInit holds whether the initial stream_start_event has been 35 | // emitted. 36 | doneInit bool 37 | } 38 | 39 | func newEncoder() *encoder { 40 | e := &encoder{} 41 | yaml_emitter_initialize(&e.emitter) 42 | yaml_emitter_set_output_string(&e.emitter, &e.out) 43 | yaml_emitter_set_unicode(&e.emitter, true) 44 | return e 45 | } 46 | 47 | func newEncoderWithWriter(w io.Writer) *encoder { 48 | e := &encoder{} 49 | yaml_emitter_initialize(&e.emitter) 50 | yaml_emitter_set_output_writer(&e.emitter, w) 51 | yaml_emitter_set_unicode(&e.emitter, true) 52 | return e 53 | } 54 | 55 | func (e *encoder) init() { 56 | if e.doneInit { 57 | return 58 | } 59 | yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) 60 | e.emit() 61 | e.doneInit = true 62 | } 63 | 64 | func (e *encoder) finish() { 65 | e.emitter.open_ended = false 66 | yaml_stream_end_event_initialize(&e.event) 67 | e.emit() 68 | } 69 | 70 | func (e *encoder) destroy() { 71 | yaml_emitter_delete(&e.emitter) 72 | } 73 | 74 | func (e *encoder) emit() { 75 | // This will internally delete the e.event value. 76 | e.must(yaml_emitter_emit(&e.emitter, &e.event)) 77 | } 78 | 79 | func (e *encoder) must(ok bool) { 80 | if !ok { 81 | msg := e.emitter.problem 82 | if msg == "" { 83 | msg = "unknown problem generating YAML content" 84 | } 85 | failf("%s", msg) 86 | } 87 | } 88 | 89 | func (e *encoder) marshalDoc(tag string, in reflect.Value) { 90 | e.init() 91 | yaml_document_start_event_initialize(&e.event, nil, nil, true) 92 | e.emit() 93 | e.marshal(tag, in) 94 | yaml_document_end_event_initialize(&e.event, true) 95 | e.emit() 96 | } 97 | 98 | func (e *encoder) marshal(tag string, in reflect.Value) { 99 | if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { 100 | e.nilv() 101 | return 102 | } 103 | iface := in.Interface() 104 | switch m := iface.(type) { 105 | case jsonNumber: 106 | integer, err := m.Int64() 107 | if err == nil { 108 | // In this case the json.Number is a valid int64 109 | in = reflect.ValueOf(integer) 110 | break 111 | } 112 | float, err := m.Float64() 113 | if err == nil { 114 | // In this case the json.Number is a valid float64 115 | in = reflect.ValueOf(float) 116 | break 117 | } 118 | // fallback case - no number could be obtained 119 | in = reflect.ValueOf(m.String()) 120 | case time.Time, *time.Time: 121 | // Although time.Time implements TextMarshaler, 122 | // we don't want to treat it as a string for YAML 123 | // purposes because YAML has special support for 124 | // timestamps. 125 | case Marshaler: 126 | v, err := m.MarshalYAML() 127 | if err != nil { 128 | fail(err) 129 | } 130 | if v == nil { 131 | e.nilv() 132 | return 133 | } 134 | in = reflect.ValueOf(v) 135 | case encoding.TextMarshaler: 136 | text, err := m.MarshalText() 137 | if err != nil { 138 | fail(err) 139 | } 140 | in = reflect.ValueOf(string(text)) 141 | case nil: 142 | e.nilv() 143 | return 144 | } 145 | switch in.Kind() { 146 | case reflect.Interface: 147 | e.marshal(tag, in.Elem()) 148 | case reflect.Map: 149 | e.mapv(tag, in) 150 | case reflect.Ptr: 151 | if in.Type() == ptrTimeType { 152 | e.timev(tag, in.Elem()) 153 | } else { 154 | e.marshal(tag, in.Elem()) 155 | } 156 | case reflect.Struct: 157 | if in.Type() == timeType { 158 | e.timev(tag, in) 159 | } else { 160 | e.structv(tag, in) 161 | } 162 | case reflect.Slice, reflect.Array: 163 | if in.Type().Elem() == mapItemType { 164 | e.itemsv(tag, in) 165 | } else { 166 | e.slicev(tag, in) 167 | } 168 | case reflect.String: 169 | e.stringv(tag, in) 170 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 171 | if in.Type() == durationType { 172 | e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) 173 | } else { 174 | e.intv(tag, in) 175 | } 176 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 177 | e.uintv(tag, in) 178 | case reflect.Float32, reflect.Float64: 179 | e.floatv(tag, in) 180 | case reflect.Bool: 181 | e.boolv(tag, in) 182 | default: 183 | panic("cannot marshal type: " + in.Type().String()) 184 | } 185 | } 186 | 187 | func (e *encoder) mapv(tag string, in reflect.Value) { 188 | e.mappingv(tag, func() { 189 | keys := keyList(in.MapKeys()) 190 | sort.Sort(keys) 191 | for _, k := range keys { 192 | e.marshal("", k) 193 | e.marshal("", in.MapIndex(k)) 194 | } 195 | }) 196 | } 197 | 198 | func (e *encoder) itemsv(tag string, in reflect.Value) { 199 | e.mappingv(tag, func() { 200 | slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) 201 | for _, item := range slice { 202 | e.marshal("", reflect.ValueOf(item.Key)) 203 | e.marshal("", reflect.ValueOf(item.Value)) 204 | } 205 | }) 206 | } 207 | 208 | func (e *encoder) structv(tag string, in reflect.Value) { 209 | sinfo, err := getStructInfo(in.Type()) 210 | if err != nil { 211 | panic(err) 212 | } 213 | e.mappingv(tag, func() { 214 | for _, info := range sinfo.FieldsList { 215 | var value reflect.Value 216 | if info.Inline == nil { 217 | value = in.Field(info.Num) 218 | } else { 219 | value = in.FieldByIndex(info.Inline) 220 | } 221 | if info.OmitEmpty && isZero(value) { 222 | continue 223 | } 224 | e.marshal("", reflect.ValueOf(info.Key)) 225 | e.flow = info.Flow 226 | e.marshal("", value) 227 | } 228 | if sinfo.InlineMap >= 0 { 229 | m := in.Field(sinfo.InlineMap) 230 | if m.Len() > 0 { 231 | e.flow = false 232 | keys := keyList(m.MapKeys()) 233 | sort.Sort(keys) 234 | for _, k := range keys { 235 | if _, found := sinfo.FieldsMap[k.String()]; found { 236 | panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String())) 237 | } 238 | e.marshal("", k) 239 | e.flow = false 240 | e.marshal("", m.MapIndex(k)) 241 | } 242 | } 243 | } 244 | }) 245 | } 246 | 247 | func (e *encoder) mappingv(tag string, f func()) { 248 | implicit := tag == "" 249 | style := yaml_BLOCK_MAPPING_STYLE 250 | if e.flow { 251 | e.flow = false 252 | style = yaml_FLOW_MAPPING_STYLE 253 | } 254 | yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) 255 | e.emit() 256 | f() 257 | yaml_mapping_end_event_initialize(&e.event) 258 | e.emit() 259 | } 260 | 261 | func (e *encoder) slicev(tag string, in reflect.Value) { 262 | implicit := tag == "" 263 | style := yaml_BLOCK_SEQUENCE_STYLE 264 | if e.flow { 265 | e.flow = false 266 | style = yaml_FLOW_SEQUENCE_STYLE 267 | } 268 | e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 269 | e.emit() 270 | n := in.Len() 271 | for i := 0; i < n; i++ { 272 | e.marshal("", in.Index(i)) 273 | } 274 | e.must(yaml_sequence_end_event_initialize(&e.event)) 275 | e.emit() 276 | } 277 | 278 | // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. 279 | // 280 | // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported 281 | // in YAML 1.2 and by this package, but these should be marshalled quoted for 282 | // the time being for compatibility with other parsers. 283 | func isBase60Float(s string) (result bool) { 284 | // Fast path. 285 | if s == "" { 286 | return false 287 | } 288 | c := s[0] 289 | if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { 290 | return false 291 | } 292 | // Do the full match. 293 | return base60float.MatchString(s) 294 | } 295 | 296 | // From http://yaml.org/type/float.html, except the regular expression there 297 | // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. 298 | var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) 299 | 300 | func (e *encoder) stringv(tag string, in reflect.Value) { 301 | var style yaml_scalar_style_t 302 | s := in.String() 303 | canUsePlain := true 304 | switch { 305 | case !utf8.ValidString(s): 306 | if tag == yaml_BINARY_TAG { 307 | failf("explicitly tagged !!binary data must be base64-encoded") 308 | } 309 | if tag != "" { 310 | failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) 311 | } 312 | // It can't be encoded directly as YAML so use a binary tag 313 | // and encode it as base64. 314 | tag = yaml_BINARY_TAG 315 | s = encodeBase64(s) 316 | case tag == "": 317 | // Check to see if it would resolve to a specific 318 | // tag when encoded unquoted. If it doesn't, 319 | // there's no need to quote it. 320 | rtag, _ := resolve("", s) 321 | canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s) 322 | } 323 | // Note: it's possible for user code to emit invalid YAML 324 | // if they explicitly specify a tag and a string containing 325 | // text that's incompatible with that tag. 326 | switch { 327 | case strings.Contains(s, "\n"): 328 | style = yaml_LITERAL_SCALAR_STYLE 329 | case canUsePlain: 330 | style = yaml_PLAIN_SCALAR_STYLE 331 | default: 332 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 333 | } 334 | e.emitScalar(s, "", tag, style) 335 | } 336 | 337 | func (e *encoder) boolv(tag string, in reflect.Value) { 338 | var s string 339 | if in.Bool() { 340 | s = "true" 341 | } else { 342 | s = "false" 343 | } 344 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 345 | } 346 | 347 | func (e *encoder) intv(tag string, in reflect.Value) { 348 | s := strconv.FormatInt(in.Int(), 10) 349 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 350 | } 351 | 352 | func (e *encoder) uintv(tag string, in reflect.Value) { 353 | s := strconv.FormatUint(in.Uint(), 10) 354 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 355 | } 356 | 357 | func (e *encoder) timev(tag string, in reflect.Value) { 358 | t := in.Interface().(time.Time) 359 | s := t.Format(time.RFC3339Nano) 360 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 361 | } 362 | 363 | func (e *encoder) floatv(tag string, in reflect.Value) { 364 | // Issue #352: When formatting, use the precision of the underlying value 365 | precision := 64 366 | if in.Kind() == reflect.Float32 { 367 | precision = 32 368 | } 369 | 370 | s := strconv.FormatFloat(in.Float(), 'g', -1, precision) 371 | switch s { 372 | case "+Inf": 373 | s = ".inf" 374 | case "-Inf": 375 | s = "-.inf" 376 | case "NaN": 377 | s = ".nan" 378 | } 379 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 380 | } 381 | 382 | func (e *encoder) nilv() { 383 | e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) 384 | } 385 | 386 | func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { 387 | implicit := tag == "" 388 | e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) 389 | e.emit() 390 | } 391 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/go.mod: -------------------------------------------------------------------------------- 1 | module "gopkg.in/yaml.v2" 2 | 3 | require ( 4 | "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 5 | ) 6 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/resolve.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "encoding/base64" 5 | "math" 6 | "regexp" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | type resolveMapItem struct { 13 | value interface{} 14 | tag string 15 | } 16 | 17 | var resolveTable = make([]byte, 256) 18 | var resolveMap = make(map[string]resolveMapItem) 19 | 20 | func init() { 21 | t := resolveTable 22 | t[int('+')] = 'S' // Sign 23 | t[int('-')] = 'S' 24 | for _, c := range "0123456789" { 25 | t[int(c)] = 'D' // Digit 26 | } 27 | for _, c := range "yYnNtTfFoO~" { 28 | t[int(c)] = 'M' // In map 29 | } 30 | t[int('.')] = '.' // Float (potentially in map) 31 | 32 | var resolveMapList = []struct { 33 | v interface{} 34 | tag string 35 | l []string 36 | }{ 37 | {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, 38 | {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, 39 | {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, 40 | {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, 41 | {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, 42 | {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, 43 | {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, 44 | {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, 45 | {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, 46 | {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, 47 | {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, 48 | {"<<", yaml_MERGE_TAG, []string{"<<"}}, 49 | } 50 | 51 | m := resolveMap 52 | for _, item := range resolveMapList { 53 | for _, s := range item.l { 54 | m[s] = resolveMapItem{item.v, item.tag} 55 | } 56 | } 57 | } 58 | 59 | const longTagPrefix = "tag:yaml.org,2002:" 60 | 61 | func shortTag(tag string) string { 62 | // TODO This can easily be made faster and produce less garbage. 63 | if strings.HasPrefix(tag, longTagPrefix) { 64 | return "!!" + tag[len(longTagPrefix):] 65 | } 66 | return tag 67 | } 68 | 69 | func longTag(tag string) string { 70 | if strings.HasPrefix(tag, "!!") { 71 | return longTagPrefix + tag[2:] 72 | } 73 | return tag 74 | } 75 | 76 | func resolvableTag(tag string) bool { 77 | switch tag { 78 | case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG: 79 | return true 80 | } 81 | return false 82 | } 83 | 84 | var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) 85 | 86 | func resolve(tag string, in string) (rtag string, out interface{}) { 87 | if !resolvableTag(tag) { 88 | return tag, in 89 | } 90 | 91 | defer func() { 92 | switch tag { 93 | case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: 94 | return 95 | case yaml_FLOAT_TAG: 96 | if rtag == yaml_INT_TAG { 97 | switch v := out.(type) { 98 | case int64: 99 | rtag = yaml_FLOAT_TAG 100 | out = float64(v) 101 | return 102 | case int: 103 | rtag = yaml_FLOAT_TAG 104 | out = float64(v) 105 | return 106 | } 107 | } 108 | } 109 | failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) 110 | }() 111 | 112 | // Any data is accepted as a !!str or !!binary. 113 | // Otherwise, the prefix is enough of a hint about what it might be. 114 | hint := byte('N') 115 | if in != "" { 116 | hint = resolveTable[in[0]] 117 | } 118 | if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG { 119 | // Handle things we can lookup in a map. 120 | if item, ok := resolveMap[in]; ok { 121 | return item.tag, item.value 122 | } 123 | 124 | // Base 60 floats are a bad idea, were dropped in YAML 1.2, and 125 | // are purposefully unsupported here. They're still quoted on 126 | // the way out for compatibility with other parser, though. 127 | 128 | switch hint { 129 | case 'M': 130 | // We've already checked the map above. 131 | 132 | case '.': 133 | // Not in the map, so maybe a normal float. 134 | floatv, err := strconv.ParseFloat(in, 64) 135 | if err == nil { 136 | return yaml_FLOAT_TAG, floatv 137 | } 138 | 139 | case 'D', 'S': 140 | // Int, float, or timestamp. 141 | // Only try values as a timestamp if the value is unquoted or there's an explicit 142 | // !!timestamp tag. 143 | if tag == "" || tag == yaml_TIMESTAMP_TAG { 144 | t, ok := parseTimestamp(in) 145 | if ok { 146 | return yaml_TIMESTAMP_TAG, t 147 | } 148 | } 149 | 150 | plain := strings.Replace(in, "_", "", -1) 151 | intv, err := strconv.ParseInt(plain, 0, 64) 152 | if err == nil { 153 | if intv == int64(int(intv)) { 154 | return yaml_INT_TAG, int(intv) 155 | } else { 156 | return yaml_INT_TAG, intv 157 | } 158 | } 159 | uintv, err := strconv.ParseUint(plain, 0, 64) 160 | if err == nil { 161 | return yaml_INT_TAG, uintv 162 | } 163 | if yamlStyleFloat.MatchString(plain) { 164 | floatv, err := strconv.ParseFloat(plain, 64) 165 | if err == nil { 166 | return yaml_FLOAT_TAG, floatv 167 | } 168 | } 169 | if strings.HasPrefix(plain, "0b") { 170 | intv, err := strconv.ParseInt(plain[2:], 2, 64) 171 | if err == nil { 172 | if intv == int64(int(intv)) { 173 | return yaml_INT_TAG, int(intv) 174 | } else { 175 | return yaml_INT_TAG, intv 176 | } 177 | } 178 | uintv, err := strconv.ParseUint(plain[2:], 2, 64) 179 | if err == nil { 180 | return yaml_INT_TAG, uintv 181 | } 182 | } else if strings.HasPrefix(plain, "-0b") { 183 | intv, err := strconv.ParseInt("-" + plain[3:], 2, 64) 184 | if err == nil { 185 | if true || intv == int64(int(intv)) { 186 | return yaml_INT_TAG, int(intv) 187 | } else { 188 | return yaml_INT_TAG, intv 189 | } 190 | } 191 | } 192 | default: 193 | panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") 194 | } 195 | } 196 | return yaml_STR_TAG, in 197 | } 198 | 199 | // encodeBase64 encodes s as base64 that is broken up into multiple lines 200 | // as appropriate for the resulting length. 201 | func encodeBase64(s string) string { 202 | const lineLen = 70 203 | encLen := base64.StdEncoding.EncodedLen(len(s)) 204 | lines := encLen/lineLen + 1 205 | buf := make([]byte, encLen*2+lines) 206 | in := buf[0:encLen] 207 | out := buf[encLen:] 208 | base64.StdEncoding.Encode(in, []byte(s)) 209 | k := 0 210 | for i := 0; i < len(in); i += lineLen { 211 | j := i + lineLen 212 | if j > len(in) { 213 | j = len(in) 214 | } 215 | k += copy(out[k:], in[i:j]) 216 | if lines > 1 { 217 | out[k] = '\n' 218 | k++ 219 | } 220 | } 221 | return string(out[:k]) 222 | } 223 | 224 | // This is a subset of the formats allowed by the regular expression 225 | // defined at http://yaml.org/type/timestamp.html. 226 | var allowedTimestampFormats = []string{ 227 | "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. 228 | "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". 229 | "2006-1-2 15:4:5.999999999", // space separated with no time zone 230 | "2006-1-2", // date only 231 | // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" 232 | // from the set of examples. 233 | } 234 | 235 | // parseTimestamp parses s as a timestamp string and 236 | // returns the timestamp and reports whether it succeeded. 237 | // Timestamp formats are defined at http://yaml.org/type/timestamp.html 238 | func parseTimestamp(s string) (time.Time, bool) { 239 | // TODO write code to check all the formats supported by 240 | // http://yaml.org/type/timestamp.html instead of using time.Parse. 241 | 242 | // Quick check: all date formats start with YYYY-. 243 | i := 0 244 | for ; i < len(s); i++ { 245 | if c := s[i]; c < '0' || c > '9' { 246 | break 247 | } 248 | } 249 | if i != 4 || i == len(s) || s[i] != '-' { 250 | return time.Time{}, false 251 | } 252 | for _, format := range allowedTimestampFormats { 253 | if t, err := time.Parse(format, s); err == nil { 254 | return t, true 255 | } 256 | } 257 | return time.Time{}, false 258 | } 259 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/sorter.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "reflect" 5 | "unicode" 6 | ) 7 | 8 | type keyList []reflect.Value 9 | 10 | func (l keyList) Len() int { return len(l) } 11 | func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 12 | func (l keyList) Less(i, j int) bool { 13 | a := l[i] 14 | b := l[j] 15 | ak := a.Kind() 16 | bk := b.Kind() 17 | for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { 18 | a = a.Elem() 19 | ak = a.Kind() 20 | } 21 | for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { 22 | b = b.Elem() 23 | bk = b.Kind() 24 | } 25 | af, aok := keyFloat(a) 26 | bf, bok := keyFloat(b) 27 | if aok && bok { 28 | if af != bf { 29 | return af < bf 30 | } 31 | if ak != bk { 32 | return ak < bk 33 | } 34 | return numLess(a, b) 35 | } 36 | if ak != reflect.String || bk != reflect.String { 37 | return ak < bk 38 | } 39 | ar, br := []rune(a.String()), []rune(b.String()) 40 | for i := 0; i < len(ar) && i < len(br); i++ { 41 | if ar[i] == br[i] { 42 | continue 43 | } 44 | al := unicode.IsLetter(ar[i]) 45 | bl := unicode.IsLetter(br[i]) 46 | if al && bl { 47 | return ar[i] < br[i] 48 | } 49 | if al || bl { 50 | return bl 51 | } 52 | var ai, bi int 53 | var an, bn int64 54 | if ar[i] == '0' || br[i] == '0' { 55 | for j := i-1; j >= 0 && unicode.IsDigit(ar[j]); j-- { 56 | if ar[j] != '0' { 57 | an = 1 58 | bn = 1 59 | break 60 | } 61 | } 62 | } 63 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { 64 | an = an*10 + int64(ar[ai]-'0') 65 | } 66 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { 67 | bn = bn*10 + int64(br[bi]-'0') 68 | } 69 | if an != bn { 70 | return an < bn 71 | } 72 | if ai != bi { 73 | return ai < bi 74 | } 75 | return ar[i] < br[i] 76 | } 77 | return len(ar) < len(br) 78 | } 79 | 80 | // keyFloat returns a float value for v if it is a number/bool 81 | // and whether it is a number/bool or not. 82 | func keyFloat(v reflect.Value) (f float64, ok bool) { 83 | switch v.Kind() { 84 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 85 | return float64(v.Int()), true 86 | case reflect.Float32, reflect.Float64: 87 | return v.Float(), true 88 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 89 | return float64(v.Uint()), true 90 | case reflect.Bool: 91 | if v.Bool() { 92 | return 1, true 93 | } 94 | return 0, true 95 | } 96 | return 0, false 97 | } 98 | 99 | // numLess returns whether a < b. 100 | // a and b must necessarily have the same kind. 101 | func numLess(a, b reflect.Value) bool { 102 | switch a.Kind() { 103 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 104 | return a.Int() < b.Int() 105 | case reflect.Float32, reflect.Float64: 106 | return a.Float() < b.Float() 107 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 108 | return a.Uint() < b.Uint() 109 | case reflect.Bool: 110 | return !a.Bool() && b.Bool() 111 | } 112 | panic("not a number") 113 | } 114 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/writerc.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | // Set the writer error and return false. 4 | func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { 5 | emitter.error = yaml_WRITER_ERROR 6 | emitter.problem = problem 7 | return false 8 | } 9 | 10 | // Flush the output buffer. 11 | func yaml_emitter_flush(emitter *yaml_emitter_t) bool { 12 | if emitter.write_handler == nil { 13 | panic("write handler not set") 14 | } 15 | 16 | // Check if the buffer is empty. 17 | if emitter.buffer_pos == 0 { 18 | return true 19 | } 20 | 21 | if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { 22 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 23 | } 24 | emitter.buffer_pos = 0 25 | return true 26 | } 27 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/yamlprivateh.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | const ( 4 | // The size of the input raw buffer. 5 | input_raw_buffer_size = 512 6 | 7 | // The size of the input buffer. 8 | // It should be possible to decode the whole raw buffer. 9 | input_buffer_size = input_raw_buffer_size * 3 10 | 11 | // The size of the output buffer. 12 | output_buffer_size = 128 13 | 14 | // The size of the output raw buffer. 15 | // It should be possible to encode the whole output buffer. 16 | output_raw_buffer_size = (output_buffer_size*2 + 2) 17 | 18 | // The size of other stacks and queues. 19 | initial_stack_size = 16 20 | initial_queue_size = 16 21 | initial_string_size = 16 22 | ) 23 | 24 | // Check if the character at the specified position is an alphabetical 25 | // character, a digit, '_', or '-'. 26 | func is_alpha(b []byte, i int) bool { 27 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' 28 | } 29 | 30 | // Check if the character at the specified position is a digit. 31 | func is_digit(b []byte, i int) bool { 32 | return b[i] >= '0' && b[i] <= '9' 33 | } 34 | 35 | // Get the value of a digit. 36 | func as_digit(b []byte, i int) int { 37 | return int(b[i]) - '0' 38 | } 39 | 40 | // Check if the character at the specified position is a hex-digit. 41 | func is_hex(b []byte, i int) bool { 42 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' 43 | } 44 | 45 | // Get the value of a hex-digit. 46 | func as_hex(b []byte, i int) int { 47 | bi := b[i] 48 | if bi >= 'A' && bi <= 'F' { 49 | return int(bi) - 'A' + 10 50 | } 51 | if bi >= 'a' && bi <= 'f' { 52 | return int(bi) - 'a' + 10 53 | } 54 | return int(bi) - '0' 55 | } 56 | 57 | // Check if the character is ASCII. 58 | func is_ascii(b []byte, i int) bool { 59 | return b[i] <= 0x7F 60 | } 61 | 62 | // Check if the character at the start of the buffer can be printed unescaped. 63 | func is_printable(b []byte, i int) bool { 64 | return ((b[i] == 0x0A) || // . == #x0A 65 | (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E 66 | (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF 67 | (b[i] > 0xC2 && b[i] < 0xED) || 68 | (b[i] == 0xED && b[i+1] < 0xA0) || 69 | (b[i] == 0xEE) || 70 | (b[i] == 0xEF && // #xE000 <= . <= #xFFFD 71 | !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF 72 | !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) 73 | } 74 | 75 | // Check if the character at the specified position is NUL. 76 | func is_z(b []byte, i int) bool { 77 | return b[i] == 0x00 78 | } 79 | 80 | // Check if the beginning of the buffer is a BOM. 81 | func is_bom(b []byte, i int) bool { 82 | return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF 83 | } 84 | 85 | // Check if the character at the specified position is space. 86 | func is_space(b []byte, i int) bool { 87 | return b[i] == ' ' 88 | } 89 | 90 | // Check if the character at the specified position is tab. 91 | func is_tab(b []byte, i int) bool { 92 | return b[i] == '\t' 93 | } 94 | 95 | // Check if the character at the specified position is blank (space or tab). 96 | func is_blank(b []byte, i int) bool { 97 | //return is_space(b, i) || is_tab(b, i) 98 | return b[i] == ' ' || b[i] == '\t' 99 | } 100 | 101 | // Check if the character at the specified position is a line break. 102 | func is_break(b []byte, i int) bool { 103 | return (b[i] == '\r' || // CR (#xD) 104 | b[i] == '\n' || // LF (#xA) 105 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 106 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 107 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) 108 | } 109 | 110 | func is_crlf(b []byte, i int) bool { 111 | return b[i] == '\r' && b[i+1] == '\n' 112 | } 113 | 114 | // Check if the character is a line break or NUL. 115 | func is_breakz(b []byte, i int) bool { 116 | //return is_break(b, i) || is_z(b, i) 117 | return ( // is_break: 118 | b[i] == '\r' || // CR (#xD) 119 | b[i] == '\n' || // LF (#xA) 120 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 121 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 122 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 123 | // is_z: 124 | b[i] == 0) 125 | } 126 | 127 | // Check if the character is a line break, space, or NUL. 128 | func is_spacez(b []byte, i int) bool { 129 | //return is_space(b, i) || is_breakz(b, i) 130 | return ( // is_space: 131 | b[i] == ' ' || 132 | // is_breakz: 133 | b[i] == '\r' || // CR (#xD) 134 | b[i] == '\n' || // LF (#xA) 135 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 136 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 137 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 138 | b[i] == 0) 139 | } 140 | 141 | // Check if the character is a line break, space, tab, or NUL. 142 | func is_blankz(b []byte, i int) bool { 143 | //return is_blank(b, i) || is_breakz(b, i) 144 | return ( // is_blank: 145 | b[i] == ' ' || b[i] == '\t' || 146 | // is_breakz: 147 | b[i] == '\r' || // CR (#xD) 148 | b[i] == '\n' || // LF (#xA) 149 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 150 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 151 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 152 | b[i] == 0) 153 | } 154 | 155 | // Determine the width of the character. 156 | func width(b byte) int { 157 | // Don't replace these by a switch without first 158 | // confirming that it is being inlined. 159 | if b&0x80 == 0x00 { 160 | return 1 161 | } 162 | if b&0xE0 == 0xC0 { 163 | return 2 164 | } 165 | if b&0xF0 == 0xE0 { 166 | return 3 167 | } 168 | if b&0xF8 == 0xF0 { 169 | return 4 170 | } 171 | return 0 172 | 173 | } 174 | -------------------------------------------------------------------------------- /vendor/launchpad.net/gnuflag/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/DataDog/datadog-go v0.0.0-20160822161430-909c02b65dd8 2 | ## explicit 3 | github.com/DataDog/datadog-go/statsd 4 | # github.com/davecgh/go-spew v1.1.1 5 | github.com/davecgh/go-spew/spew 6 | # github.com/getsentry/sentry-go v0.6.1 7 | ## explicit 8 | github.com/getsentry/sentry-go 9 | # github.com/pmezard/go-difflib v1.0.0 10 | github.com/pmezard/go-difflib/difflib 11 | # github.com/stretchr/testify v1.4.0 12 | ## explicit 13 | github.com/stretchr/testify/assert 14 | github.com/stretchr/testify/require 15 | # gopkg.in/yaml.v2 v2.2.4 16 | gopkg.in/yaml.v2 17 | # launchpad.net/gnuflag v0.0.0-20150127164241-000000000014 18 | ## explicit 19 | launchpad.net/gnuflag 20 | --------------------------------------------------------------------------------