-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathutils.go
168 lines (149 loc) · 4.52 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errors
import (
"fmt"
"log"
"runtime"
"strings"
)
var (
// Change this method if you want errors to log somehow else
LogMethod = log.Printf
ErrorGroupError = NewClass("Error Group Error")
ErrorGroupNoCaptureStackError = NewClass("Error Group Error", NoCaptureStack())
)
// LogWithStack will log the given messages with the current stack
func LogWithStack(messages ...interface{}) {
buf := make([]byte, Config.Stacklogsize)
buf = buf[:runtime.Stack(buf, false)]
LogMethod("%s\n%s", fmt.Sprintln(messages...), buf)
}
// CatchPanic can be used to catch panics and turn them into errors. See the
// example.
func CatchPanic(err_ref *error) {
r := recover()
if r == nil {
return
}
err, ok := r.(error)
if ok {
*err_ref = PanicError.Wrap(err)
return
}
*err_ref = PanicError.New("%v", r)
}
// ErrorGroup is a type for collecting errors from a bunch of independent
// tasks. ErrorGroups are not threadsafe. See the example for usage.
type ErrorGroup struct {
Errors []error
noCaptureStack bool
limit int
excess int
}
// NewErrorGroup makes a new ErrorGroup
func NewErrorGroup() *ErrorGroup { return &ErrorGroup{} }
// NewErrorGroup make a new ErrorGroup that doesn't print the stacktrace
func NewErrorGroupNoCaptureStack() *ErrorGroup {
return &ErrorGroup{
noCaptureStack: true,
}
}
// NewBoundedErrorGroup makes a new ErrorGroup that will not track more than
// limit errors. Once the limit is reached, the ErrorGroup will track
// additional errors as excess.
func NewBoundedErrorGroup(limit int) *ErrorGroup {
return &ErrorGroup{
limit: limit,
}
}
// Add is called with errors. nil errors are ignored.
func (e *ErrorGroup) Add(err error) {
if err == nil {
return
}
if e.limit > 0 && len(e.Errors) == e.limit {
e.excess++
} else {
e.Errors = append(e.Errors, err)
}
}
// Finalize will collate all the found errors. If no errors were found, it will
// return nil. If one error was found, it will be returned directly. Otherwise
// an ErrorGroupError will be returned.
func (e *ErrorGroup) Finalize() error {
if len(e.Errors) == 0 {
return nil
}
if len(e.Errors) == 1 && e.excess == 0 {
return e.Errors[0]
}
msgs := make([]string, 0, len(e.Errors))
for _, err := range e.Errors {
msgs = append(msgs, err.Error())
}
if e.excess > 0 {
msgs = append(msgs, fmt.Sprintf("... and %d more.", e.excess))
e.excess = 0
}
e.Errors = nil
if e.noCaptureStack {
return ErrorGroupNoCaptureStackError.New(strings.Join(msgs, "\n"))
}
return ErrorGroupError.New(strings.Join(msgs, "\n"))
}
// LoggingErrorGroup is similar to ErrorGroup except that instead of collecting
// all of the errors, it logs the errors immediately and just counts how many
// non-nil errors have been seen. See the ErrorGroup example for usage.
type LoggingErrorGroup struct {
name string
total int
failed int
}
// NewLoggingErrorGroup returns a new LoggingErrorGroup with the given name.
func NewLoggingErrorGroup(name string) *LoggingErrorGroup {
return &LoggingErrorGroup{name: name}
}
// Add will handle a given error. If the error is non-nil, total and failed
// are both incremented and the error is logged. If the error is nil, only
// total is incremented.
func (e *LoggingErrorGroup) Add(err error) {
e.total++
if err != nil {
LogMethod("%s: %s", e.name, err)
e.failed++
}
}
// Finalize returns no error if no failures were observed, otherwise it will
// return an ErrorGroupError with statistics about the observed errors.
func (e *LoggingErrorGroup) Finalize() (err error) {
if e.failed > 0 {
err = ErrorGroupError.New("%s: %d of %d failed.", e.name, e.failed,
e.total)
}
e.total = 0
e.failed = 0
return err
}
type Finalizer interface {
Finalize() error
}
// Finalize takes a group of ErrorGroups and joins them together into one error
func Finalize(finalizers ...Finalizer) error {
var errs ErrorGroup
for _, finalizer := range finalizers {
errs.Add(finalizer.Finalize())
}
return errs.Finalize()
}