github.com/aarzilli/tools@v0.0.0-20151123112009-0d27094f75e0/net/http/loghttp/log.go (about) 1 package loghttp 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "log" 9 "net/http" 10 "os" 11 "strings" 12 13 "github.com/pbberlin/tools/appengine/util_appengine" 14 "github.com/pbberlin/tools/runtimepb" 15 "github.com/pbberlin/tools/stringspb" 16 aelog "google.golang.org/appengine/log" 17 ) 18 19 /* 20 21 A generic error function 22 23 Utility functions pass errors up to the caller 24 25 26 Higher level "request functions" handle errors directly 27 often we want to abort further request processing 28 and issue an message into the http response AND into the logs 29 30 Sometimes we only want to write the error into the logs and 31 continue operation => continueExecution true 32 33 In addition to the generic error messages 34 we may add specific error explanations or values 35 via parameter vs - for display and logging 36 We also show the source file+location. 37 38 A "global panic catcher" in util_err.Adapter() ...defer(){} 39 cooperates - suppressing the stacktrace and 40 healing the panic 41 42 */ 43 func E(w http.ResponseWriter, r *http.Request, 44 bool_or_err interface{}, 45 continueExecution bool, 46 vs ...interface{}) { 47 48 var err error 49 50 switch bool_or_err.(type) { 51 default: 52 type_unknown := fmt.Sprintf("%T", bool_or_err) 53 err = errors.New("only bool or error - instead: -" + type_unknown + "-") 54 panic(err) 55 case nil: 56 return 57 case bool: 58 if bool_or_err.(bool) { 59 return 60 } 61 err = errors.New("Not OK (type conv?)") 62 case error: 63 err = bool_or_err.(error) 64 } 65 66 if err != nil { 67 line, file := runtimepb.LineFileXUp(1) 68 // we cannot determine, whether html is already sent 69 // we cannot determine, whether we are in plaintext or html context 70 // thus we need the <br> 71 s := fmt.Sprintf("ERR: %v <br>\n\t /%s:%d \n", err, file, line) 72 if len(vs) > 0 { 73 s = s + "\t" + fmt.Sprint(vs...) + "\n" 74 } 75 76 if continueExecution { 77 c, _ := util_appengine.SafelyExtractGaeCtxError(r) 78 if c == nil { 79 log.Printf(s) 80 } else { 81 aelog.Infof(c, s) 82 } 83 } else { 84 c, _ := util_appengine.SafelyExtractGaeCtxError(r) 85 if c == nil { 86 log.Printf(s) 87 } else { 88 aelog.Errorf(c, s) 89 } 90 w.Header().Set("Content-Type", "text/plain") 91 http.Error(w, s, http.StatusInternalServerError) 92 panic("abort_handler_processing") 93 } 94 } 95 96 } 97 98 func Pf(w io.Writer, r *http.Request, f string, vs ...interface{}) { 99 100 for idx, v := range vs { 101 switch v := v.(type) { 102 case []byte: 103 if len(v) > 1024*5 { 104 appdx := append([]byte(" ...omitted... "), v[len(v)-100:]...) 105 vs[idx] = append(v[:1024*5], appdx...) 106 } 107 case string: 108 if len(v) > 1024*5 { 109 appdx := " ...omitted... " + v[len(v)-100:] 110 vs[idx] = v[:1024*5] + appdx 111 } 112 } 113 } 114 115 // Prepare the string 116 var s string 117 if len(vs) > 0 { 118 s = fmt.Sprintf(f, vs...) 119 } else { 120 s = f 121 } 122 123 if s == "" { 124 return 125 } 126 127 // Write it to http response or bytes.Buffer 128 // unless prefixed with 'lo ' - log only. 129 // Thread-safety could be introduced by syncing/locking w. 130 if w != nil && !strings.HasPrefix(s, "lo ") { 131 w.Write([]byte(s)) 132 w.Write([]byte{'\n'}) 133 } 134 135 // Write to log/gae-log 136 // Adding src code info 137 138 line, file := runtimepb.LineFileXUp(1) 139 // if strings.HasSuffix(file, "log.go") 140 if strings.HasSuffix(file, runtimepb.ThisFile()) { // change 141 line, file = runtimepb.LineFileXUp(2) 142 } 143 144 if len(s) < 60 { 145 s = stringspb.ToLen(s, 60) 146 } 147 s = fmt.Sprintf("%v - %v:%v", s, file, line) 148 149 // Log it 150 c, _ := util_appengine.SafelyExtractGaeCtxError(r) 151 if c == nil { 152 lnp.Printf(s) 153 } else { 154 aelog.Infof(c, s) 155 } 156 157 } 158 159 type tLogFunc func(format string, is ...interface{}) 160 type tErrFunc func(error) 161 162 var lnp = log.New(os.Stderr, "", 0) // logger no prefix; os.Stderr shows up in appengine devserver; os.Stdout does not 163 164 const maxPref = 32 165 166 func Logger(w http.ResponseWriter, r *http.Request) (tLogFunc, tErrFunc) { 167 168 fLog := func(format string, is ...interface{}) { 169 Pf(w, r, format, is...) 170 } 171 fErr := func(err error) { 172 if err != nil { 173 Pf(w, r, "Err %v", err) 174 } 175 } 176 return fLog, fErr 177 178 } 179 180 // universal logger func, for log(err) and log("format", ...) 181 type FuncBufUniv func(...interface{}) 182 183 func BuffLoggerUniversal(w http.ResponseWriter, r *http.Request) (FuncBufUniv, *bytes.Buffer) { 184 185 b := new(bytes.Buffer) 186 187 fLog1 := func(a ...interface{}) { 188 189 if len(a) > 0 { 190 191 if a[0] == nil { 192 return 193 } 194 195 switch t := a[0].(type) { 196 197 case string: 198 if len(a) == 1 { 199 Pf(b, r, t) 200 } else { 201 Pf(b, r, t, a[1:]...) 202 } 203 204 case []byte: 205 if len(a) == 1 { 206 Pf(b, r, string(t)) 207 } else { 208 Pf(b, r, string(t), a[1:]...) 209 } 210 211 case interface{}: 212 if err, ok := t.(error); ok { 213 if err != nil { 214 Pf(b, r, "Err %v", err) 215 } 216 } else { 217 log.Printf("first argument must be string or error occ1; is %T\n", t) 218 } 219 220 default: 221 log.Printf("first argument must be string or error occ2; is %T\n", t) 222 } 223 224 } 225 226 } 227 return fLog1, b 228 229 }