Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / go.uber.org / zap / sink.go
1 // Copyright (c) 2016 Uber Technologies, 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
11 // all 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
19 // THE SOFTWARE.
20
21 package zap
22
23 import (
24         "errors"
25         "fmt"
26         "io"
27         "net/url"
28         "os"
29         "strings"
30         "sync"
31
32         "go.uber.org/zap/zapcore"
33 )
34
35 const schemeFile = "file"
36
37 var (
38         _sinkMutex     sync.RWMutex
39         _sinkFactories map[string]func(*url.URL) (Sink, error) // keyed by scheme
40 )
41
42 func init() {
43         resetSinkRegistry()
44 }
45
46 func resetSinkRegistry() {
47         _sinkMutex.Lock()
48         defer _sinkMutex.Unlock()
49
50         _sinkFactories = map[string]func(*url.URL) (Sink, error){
51                 schemeFile: newFileSink,
52         }
53 }
54
55 // Sink defines the interface to write to and close logger destinations.
56 type Sink interface {
57         zapcore.WriteSyncer
58         io.Closer
59 }
60
61 type nopCloserSink struct{ zapcore.WriteSyncer }
62
63 func (nopCloserSink) Close() error { return nil }
64
65 type errSinkNotFound struct {
66         scheme string
67 }
68
69 func (e *errSinkNotFound) Error() string {
70         return fmt.Sprintf("no sink found for scheme %q", e.scheme)
71 }
72
73 // RegisterSink registers a user-supplied factory for all sinks with a
74 // particular scheme.
75 //
76 // All schemes must be ASCII, valid under section 3.1 of RFC 3986
77 // (https://tools.ietf.org/html/rfc3986#section-3.1), and must not already
78 // have a factory registered. Zap automatically registers a factory for the
79 // "file" scheme.
80 func RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {
81         _sinkMutex.Lock()
82         defer _sinkMutex.Unlock()
83
84         if scheme == "" {
85                 return errors.New("can't register a sink factory for empty string")
86         }
87         normalized, err := normalizeScheme(scheme)
88         if err != nil {
89                 return fmt.Errorf("%q is not a valid scheme: %v", scheme, err)
90         }
91         if _, ok := _sinkFactories[normalized]; ok {
92                 return fmt.Errorf("sink factory already registered for scheme %q", normalized)
93         }
94         _sinkFactories[normalized] = factory
95         return nil
96 }
97
98 func newSink(rawURL string) (Sink, error) {
99         u, err := url.Parse(rawURL)
100         if err != nil {
101                 return nil, fmt.Errorf("can't parse %q as a URL: %v", rawURL, err)
102         }
103         if u.Scheme == "" {
104                 u.Scheme = schemeFile
105         }
106
107         _sinkMutex.RLock()
108         factory, ok := _sinkFactories[u.Scheme]
109         _sinkMutex.RUnlock()
110         if !ok {
111                 return nil, &errSinkNotFound{u.Scheme}
112         }
113         return factory(u)
114 }
115
116 func newFileSink(u *url.URL) (Sink, error) {
117         if u.User != nil {
118                 return nil, fmt.Errorf("user and password not allowed with file URLs: got %v", u)
119         }
120         if u.Fragment != "" {
121                 return nil, fmt.Errorf("fragments not allowed with file URLs: got %v", u)
122         }
123         if u.RawQuery != "" {
124                 return nil, fmt.Errorf("query parameters not allowed with file URLs: got %v", u)
125         }
126         // Error messages are better if we check hostname and port separately.
127         if u.Port() != "" {
128                 return nil, fmt.Errorf("ports not allowed with file URLs: got %v", u)
129         }
130         if hn := u.Hostname(); hn != "" && hn != "localhost" {
131                 return nil, fmt.Errorf("file URLs must leave host empty or use localhost: got %v", u)
132         }
133         switch u.Path {
134         case "stdout":
135                 return nopCloserSink{os.Stdout}, nil
136         case "stderr":
137                 return nopCloserSink{os.Stderr}, nil
138         }
139         return os.OpenFile(u.Path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
140 }
141
142 func normalizeScheme(s string) (string, error) {
143         // https://tools.ietf.org/html/rfc3986#section-3.1
144         s = strings.ToLower(s)
145         if first := s[0]; 'a' > first || 'z' < first {
146                 return "", errors.New("must start with a letter")
147         }
148         for i := 1; i < len(s); i++ { // iterate over bytes, not runes
149                 c := s[i]
150                 switch {
151                 case 'a' <= c && c <= 'z':
152                         continue
153                 case '0' <= c && c <= '9':
154                         continue
155                 case c == '.' || c == '+' || c == '-':
156                         continue
157                 }
158                 return "", fmt.Errorf("may not contain %q", c)
159         }
160         return s, nil
161 }