...

Source file src/golang.conradwood.net/go-easyops/server/error-handler.go

Documentation: golang.conradwood.net/go-easyops/server

     1  package server
     2  
     3  import (
     4  	"context"
     5  	"flag"
     6  	"fmt"
     7  	"time"
     8  
     9  	el "golang.conradwood.net/apis/errorlogger"
    10  	fw "golang.conradwood.net/apis/framework"
    11  	ge "golang.conradwood.net/apis/goeasyops"
    12  	"golang.conradwood.net/go-easyops/auth"
    13  	"golang.conradwood.net/go-easyops/authremote"
    14  	"golang.conradwood.net/go-easyops/client"
    15  	"golang.conradwood.net/go-easyops/cmdline"
    16  	gctx "golang.conradwood.net/go-easyops/ctx"
    17  	"golang.conradwood.net/go-easyops/errors"
    18  	"golang.conradwood.net/go-easyops/utils"
    19  	"google.golang.org/grpc/status"
    20  	proto2 "google.golang.org/protobuf/proto"
    21  	"google.golang.org/protobuf/protoadapt"
    22  )
    23  
    24  var (
    25  	logChan              = make(chan *le, 200)
    26  	els                  el.ErrorLoggerClient
    27  	error_looping        = false
    28  	send_to_error_logger = flag.Bool("ge_use_errorlogger", true, "if false, do not send stuff to error logger")
    29  	debug_elog           = flag.Bool("ge_debug_error_log", false, "if true debug what is being sent to the error logger")
    30  )
    31  
    32  type le struct {
    33  	ts  time.Time
    34  	sd  *serverDef
    35  	rc  *rpccall
    36  	ctx context.Context
    37  	err error
    38  }
    39  
    40  func (sd *serverDef) logError(ctx context.Context, rc *rpccall, err error) {
    41  	if cmdline.IsStandalone() {
    42  		fmt.Printf("[go-easyops] ERROR: %s\n", err)
    43  	}
    44  	if len(logChan) > 100 {
    45  		fmt.Printf("[go-easyops] Dropping errorlog (not logging \"%s\")\n", errors.ErrorString(err))
    46  		return
    47  	}
    48  	l := &le{sd: sd, ctx: ctx, rc: rc, err: err, ts: time.Now()}
    49  	logChan <- l
    50  }
    51  func error_handler_startup() {
    52  	if cmdline.IsStandalone() {
    53  		return
    54  	}
    55  	if error_looping {
    56  		return
    57  	}
    58  	error_looping = true
    59  	if els == nil {
    60  		els = el.NewErrorLoggerClient(client.Connect("errorlogger.ErrorLogger"))
    61  	}
    62  	go logLoop()
    63  }
    64  func logLoop() {
    65  	for {
    66  		l := <-logChan
    67  		log(l)
    68  	}
    69  }
    70  func log(l *le) {
    71  	u := auth.GetUser(l.ctx)
    72  	uid := ""
    73  	if u != nil {
    74  		uid = u.ID
    75  	}
    76  	reqid := "norequestidinerrorhandler"
    77  	if l.ctx != nil {
    78  		reqid = gctx.GetRequestID(l.ctx)
    79  	}
    80  	svc := auth.GetService(l.ctx)
    81  	st := status.Convert(l.err)
    82  	e := &el.ErrorLogRequest{
    83  		UserID:         uid,
    84  		ErrorCode:      uint32(st.Code()),
    85  		ErrorMessage:   fmt.Sprintf("%s", l.err),
    86  		LogMessage:     utils.ErrorString(l.err),
    87  		ServiceName:    l.rc.ServiceName,
    88  		MethodName:     l.rc.MethodName,
    89  		Timestamp:      uint32(l.ts.Unix()),
    90  		RequestID:      reqid,
    91  		CallingService: svc,
    92  		Errors:         &ge.GRPCErrorList{},
    93  	}
    94  	/*
    95  		for _, a := range st.Details() {
    96  			if a == nil {
    97  				continue
    98  			}
    99  			fmd, ok := a.(*fw.FrameworkMessageDetail)
   100  			if !ok {
   101  				continue
   102  			}
   103  			e.Messages = append(e.Messages, fmd)
   104  		}
   105  	*/
   106  	for _, a := range st.Details() {
   107  		if a == nil {
   108  			continue
   109  		}
   110  		fmd, ok := a.(*ge.GRPCError)
   111  		if !ok {
   112  			continue
   113  		}
   114  		e.Errors.Errors = append(e.Errors.Errors, fmd)
   115  	}
   116  	ctx := authremote.Context()
   117  	if *debug_elog {
   118  		fmt.Printf("[go-easyops] errorlog: %v\n", e)
   119  	}
   120  	if *send_to_error_logger {
   121  		els.Log(ctx, e)
   122  	}
   123  }
   124  
   125  // this will result status detail with grpcerrorlist, with a single GRPCErrorList.
   126  func AddErrorDetail(st *status.Status, ct *ge.GRPCError) *status.Status {
   127  	// add details (and keep previous)
   128  	odet := st.Details()
   129  	if cmdline.IsDebugRPCServer() {
   130  		fancyPrintf("errorhandler Error %s (%s) (%s)\n", st.Err(), st.Message(), errors.ErrorString(st.Err()))
   131  	}
   132  	// find existing grpcerrorlist...
   133  	var gel *ge.GRPCErrorList
   134  	for _, d := range odet {
   135  		mgel, ok := d.(*ge.GRPCErrorList)
   136  		if ok {
   137  			gel = mgel
   138  			break
   139  		}
   140  		proto2m, ok := d.(proto2.Message)
   141  		if ok {
   142  			msgname := proto2.MessageName(proto2m)
   143  			//	msg := proto2m.ProtoReflect()
   144  			pv1 := protoadapt.MessageV1Of(proto2m)
   145  			//fmt.Printf("Proto2 (%s): %#v %v %v\n", msgname, proto2m, msg, pv1)
   146  			if msgname == "goeasyops.GRPCErrorList" {
   147  				xgel, ok := pv1.(*ge.GRPCErrorList)
   148  				if ok {
   149  					gel = xgel
   150  					break
   151  				}
   152  			}
   153  		}
   154  	}
   155  	// none found, add a list
   156  	var stn *status.Status
   157  	if gel == nil {
   158  		gel = &ge.GRPCErrorList{}
   159  	}
   160  	gel.Errors = append(gel.Errors, ct)
   161  
   162  	stn, errx := st.WithDetails(gel)
   163  
   164  	// if adding details failed, just return the undecorated error message
   165  	if errx != nil {
   166  		if cmdline.IsDebugRPCServer() {
   167  			fancyPrintf("failed to get status with detail: %s", errx)
   168  		}
   169  		return st
   170  	}
   171  	/*
   172  		for i, d := range stn.Details() {
   173  			fmt.Printf("%d: %v %v\n", i+1, d, reflect.TypeOf(d))
   174  		}
   175  	*/
   176  	return stn
   177  }
   178  func AddStatusDetail(st *status.Status, ct *fw.CallTrace) *status.Status {
   179  	return st
   180  	/*
   181  		// add details (and keep previous)
   182  		add := &fw.FrameworkMessageDetail{Message: ct.Message}
   183  		odet := st.Details()
   184  		if cmdline.IsDebugRPCServer() {
   185  			fancyPrintf("Error %s (%s) (%s)\n", st.Err(), st.Message(), utils.ErrorString(st.Err()))
   186  		}
   187  		for _, d := range odet {
   188  			if cmdline.IsDebugRPCServer() {
   189  				fancyPrintf("keeping error %v\n", d)
   190  			}
   191  			fmd, ok := d.(*fw.FrameworkMessageDetail)
   192  			if ok {
   193  				add.CallTraces = append(add.CallTraces, fmd.CallTraces...)
   194  			} else {
   195  				add.CallTraces = append(add.CallTraces, &fw.CallTrace{Message: fmt.Sprintf("%v", d)})
   196  
   197  			}
   198  		}
   199  		add.CallTraces = append(add.CallTraces, ct)
   200  		stn, errx := st.WithDetails(add)
   201  
   202  		// if adding details failed, just return the undecorated error message
   203  		if errx != nil {
   204  			if cmdline.IsDebugRPCServer() {
   205  				fancyPrintf("failed to get status with detail: %s", errx)
   206  			}
   207  			return st
   208  		}
   209  		return stn
   210  	*/
   211  }
   212  

View as plain text