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  }