SDKs
...
Go SDK
v2
Error Handling and Logging
9min
in our sdk, we divide all errors into two primary categories server errors – returned directly from the licensespring backend internal sdk errors – generated within the sdk itself error type we define a unified error type for all internal and server errors type error struct { 	status int `json "status"` 	code string `json "code"` 	message string `json "message"` 	details map\[string]interface{} `json "details,omitempty"` 	inner error `json " "` 	stacktrace string `json " "` } server originated errors these errors are returned directly from the licensespring api we do not wrap them multiple times instead, we handle them as follows at the lowest point where the api response is received, we wrap the error and add a stack trace using debug stack() // resp is the server's response if resp error != nil { 	 err = core errors error{ 	 status resp error status, 	 code resp error code, 	 message resp error message, 	 inner resp error, 	 stacktrace string(debug stack()) 	 } these errors are returned as is up the call chain logging occurs only once, at the licensehandler level internal sdk errors for errors caused by logic inside the sdk, we have predefined constructor functions these are always called at the lowest layer where the error originates , and a stack trace is attached example if !ld isfloatingorfloatingcloud() { 	return ld, errors licensenotfloating(string(debug stack())) } all such error constructors follow the format func licensenotfloating(stack string) error { 	e = \&error{ 	 status http statusbadrequest, 	 code "license not floating", 	 message "the license is not a floating license", 	} 	if len(stack) > 0 { 	 e stacktrace = stack\[0] 	} 	return e } licensehandler layer logging and safe return at the topmost layer (usually inside licensehandler ), we log the complete error including stack trace return a safe version of the error (excluding stack and internal details) ld, err = lh licensemanager releasefloatinglicense(ctx) if err != nil { 	logger error(fmt errorf("floating license release failed %w", err)) 	return ld, errors sanitizeerror(err) } the sanitizeerror function func sanitizeerror(err errors error) map\[string]interface{} { 	return map\[string]interface{}{ 	 "status" err status, 	 "code" err code, 	 "message" err message, 	} } example full error flow for floating license release func (ls licensemanager) releasefloatinglicense(ctx context context) ( types licensedata, error) { 	if err = ls validate(); err != nil { 	 // validation errors are returned immediately, no need to wrap 	 return nil, err 	} 	ld = ls licensedata 	isactive, isenabled, isexpired = ls checklicensestatus() 	// this is the lowest level where the internal sdk error occurs 	// we attach a stack trace using debug stack() and create a 	// custom error object 	if !isenabled { 	 return ld, errors licensenotenabled(string(debug stack())) 	} 	if !isactive { 	 return ld, errors licensenotactive(string(debug stack())) 	} 	if isexpired { 	 return ld, errors licenseexpired(string(debug stack())) 	} 	if !ld isfloatingorfloatingcloud() { 	 return ld, errors licensenotfloating(string(debug stack())) 	} 	config = ls licenseclient licenseclientconfiguration 	authdata = ld authenticationdata 	request = core request licenserequest{ 	 hardwareid config hardwareid, 	 product config productcode, 	 auth authdata, 	} 	// this error comes from the server 	// the licenseclient layer has already captured the stack trace 	// we return it as is so that the outer layer (licensehandler) 	// can log and sanitize it 	err = ls licenseclient floatingrelease(ctx, request) 	if err != nil { 	 return ld, err 	} 	ld revokefloatinglicense() 	logger info("floating license released successfully") 	return ld, nil } func (lh licensehandler) releasefloatinglicense(ctx context context) ( types licensedata, error) { 	ld, err = lh licensemanager releasefloatinglicense(ctx) 	if err != nil { 	 // we do all logging at the outermost layer 	 logger error(fmt errorf("license floating release failed %w", err)) 	 // we return a sanitized version of the error to avoid leaking internals 	 return ld, errors sanitizeerror(err) 	} 	s = lh storage 	err = s savelicense(ld) 	if err != nil { 	 logger error(fmt errorf("license floating release failed %w", err)) 	 return nil, errors sanitizeerror(err) 	} 	return ld, nil }