1// Copyright (c) 2014 Couchbase, Inc. 2// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 3// except in compliance with the License. You may obtain a copy of the License at 4// http://www.apache.org/licenses/LICENSE-2.0 5// Unless required by applicable law or agreed to in writing, software distributed under the 6// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 7// either express or implied. See the License for the specific language governing permissions 8// and limitations under the License. 9 10/* 11 12Package err provides user-visible errors and warnings. These errors 13include error codes and will eventually provide multi-language 14messages. 15 16*/ 17package errors 18 19import ( 20 "encoding/json" 21 "fmt" 22 "path" 23 "runtime" 24 "strings" 25) 26 27const ( 28 EXCEPTION = iota 29 WARNING 30 NOTICE 31 INFO 32 LOG 33 DEBUG 34) 35 36type Errors []Error 37 38// Error will eventually include code, message key, and internal error 39// object (cause) and message 40type Error interface { 41 error 42 Code() int32 43 TranslationKey() string 44 Cause() error 45 Level() int 46 IsFatal() bool 47 IsWarning() bool 48 OnceOnly() bool 49} 50 51type ErrorChannel chan Error 52 53func NewError(e error, internalMsg string) Error { 54 switch e := e.(type) { 55 case Error: // if given error is already an Error, just return it: 56 return e 57 default: 58 return &err{level: EXCEPTION, ICode: 5000, IKey: "Internal Error", ICause: e, 59 InternalMsg: internalMsg, InternalCaller: CallerN(1)} 60 } 61} 62 63func NewWarning(internalMsg string) Error { 64 return &err{level: WARNING, InternalMsg: internalMsg, InternalCaller: CallerN(1)} 65} 66 67func NewNotice(internalMsg string) Error { 68 return &err{level: NOTICE, InternalMsg: internalMsg, InternalCaller: CallerN(1)} 69} 70 71func NewInfo(internalMsg string) Error { 72 return &err{level: INFO, InternalMsg: internalMsg, InternalCaller: CallerN(1)} 73} 74 75func NewLog(internalMsg string) Error { 76 return &err{level: LOG, InternalMsg: internalMsg, InternalCaller: CallerN(1)} 77} 78 79func NewDebug(internalMsg string) Error { 80 return &err{level: DEBUG, InternalMsg: internalMsg, InternalCaller: CallerN(1)} 81} 82 83type err struct { 84 ICode int32 85 IKey string 86 ICause error 87 InternalMsg string 88 InternalCaller string 89 level int 90 onceOnly bool 91} 92 93func (e *err) Error() string { 94 switch { 95 default: 96 return "Unspecified error." 97 case e.InternalMsg != "" && e.ICause != nil: 98 return e.InternalMsg + " - cause: " + e.ICause.Error() 99 case e.InternalMsg != "": 100 return e.InternalMsg 101 case e.ICause != nil: 102 return e.ICause.Error() 103 } 104} 105 106func (e *err) MarshalJSON() ([]byte, error) { 107 m := map[string]interface{}{ 108 "code": e.ICode, 109 "key": e.IKey, 110 "message": e.InternalMsg, 111 } 112 if e.ICause != nil { 113 m["cause"] = e.ICause.Error() 114 } 115 if e.InternalCaller != "" && 116 !strings.HasPrefix("e.InternalCaller", "unknown:") { 117 m["caller"] = e.InternalCaller 118 } 119 return json.Marshal(m) 120} 121 122func (e *err) UnmarshalJSON(body []byte) error { 123 var _unmarshalled struct { 124 Caller string `json:"caller"` 125 Code int32 `json:"code"` 126 Key string `json:"key"` 127 Message string `json:"message"` 128 } 129 130 unmarshalErr := json.Unmarshal(body, &_unmarshalled) 131 if unmarshalErr != nil { 132 return unmarshalErr 133 } 134 135 e.ICode = _unmarshalled.Code 136 e.IKey = _unmarshalled.Key 137 e.InternalMsg = _unmarshalled.Message 138 e.InternalCaller = _unmarshalled.Caller 139 return nil 140} 141 142func (e *err) Level() int { 143 return e.level 144} 145 146func (e *err) IsFatal() bool { 147 return e.level == EXCEPTION 148} 149 150func (e *err) IsWarning() bool { 151 return e.level == WARNING 152} 153 154func (e *err) Code() int32 { 155 return e.ICode 156} 157 158func (e *err) TranslationKey() string { 159 return e.IKey 160} 161 162func (e *err) Cause() error { 163 return e.ICause 164} 165 166func (e *err) OnceOnly() bool { 167 return e.onceOnly 168} 169 170// only put errors in the reserved range here (7000-9999) 171func NewNotImplemented(feature string) Error { 172 return &err{level: EXCEPTION, ICode: 9999, IKey: "not_implemented", InternalMsg: fmt.Sprintf("Not yet implemented: %v", feature), InternalCaller: CallerN(1)} 173} 174 175// Returns "FileName:LineNum" of caller. 176func Caller() string { 177 return CallerN(1) 178} 179 180// Returns "FileName:LineNum" of the Nth caller on the call stack, 181// where level of 0 is the caller of CallerN. 182func CallerN(level int) string { 183 _, fname, lineno, ok := runtime.Caller(1 + level) 184 if !ok { 185 return "unknown:0" 186 } 187 return fmt.Sprintf("%s:%d", 188 strings.Split(path.Base(fname), ".")[0], lineno) 189} 190