Last active
May 27, 2024 14:27
-
-
Save shakahl/3d57e9d1089b574995da2d5ccdc313dc to your computer and use it in GitHub Desktop.
Go - function that parses key-value map context information from an error string
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"errors" | |
"fmt" | |
"reflect" | |
"regexp" | |
"strings" | |
) | |
// ExtendErrorWithContext extends an error with context information. | |
func ExtendErrorWithContext(err error, context any) error { | |
if err == nil { | |
return nil | |
} | |
contextStr := extractContext(context) | |
return fmt.Errorf("%w %s", err, contextStr) | |
} | |
// extractContext extracts context information from map, struct, or pointers. | |
func extractContext(context any) string { | |
var sb strings.Builder | |
v := reflect.ValueOf(context) | |
if v.Kind() == reflect.Ptr { | |
v = v.Elem() | |
} | |
switch v.Kind() { | |
case reflect.Map: | |
for _, key := range v.MapKeys() { | |
val := v.MapIndex(key) | |
fmt.Fprintf(&sb, "%v=%v ", key, val) | |
} | |
case reflect.Struct: | |
for i := 0; i < v.NumField(); i++ { | |
field := v.Type().Field(i) | |
val := v.Field(i) | |
fmt.Fprintf(&sb, "%s=%v ", field.Name, val) | |
} | |
default: | |
return "" | |
} | |
return strings.TrimSpace(sb.String()) | |
} | |
// ParseErrorContext parses key-value pairs from an error string and returns them as a nested map. | |
func ParseErrorContext(e any) map[string]any { | |
errStr := fmt.Sprint(e) | |
re := regexp.MustCompile(`(\w+(\.\w+)*)=("[^"]*"|\S+)`) | |
matches := re.FindAllStringSubmatch(errStr, -1) | |
result := make(map[string]any) | |
for _, match := range matches { | |
keys := strings.Split(match[1], ".") | |
value := match[3] | |
if strings.HasPrefix(value, `"`) && strings.HasSuffix(value, `"`) { | |
value = value[1 : len(value)-1] | |
} | |
// Build the nested map structure | |
currentMap := result | |
for i, key := range keys { | |
if i == len(keys)-1 { | |
currentMap[key] = value | |
} else { | |
if _, exists := currentMap[key]; !exists { | |
currentMap[key] = make(map[string]any) | |
} | |
currentMap = currentMap[key].(map[string]any) | |
} | |
} | |
} | |
return result | |
} | |
// ExtractErrorMessage extracts the original error message without context. | |
func ExtractErrorMessage(v any) string { | |
errStr := fmt.Sprint(v) | |
// Define the regular expression pattern to match key-value pairs. | |
re := regexp.MustCompile(`((\w+(\.\w+)*)\=.*)$`) | |
return strings.TrimSpace(re.ReplaceAllString(errStr, "")) | |
} | |
// Example struct | |
type InnerStruct struct { | |
Level3 string | |
} | |
type OuterStruct struct { | |
Inner *InnerStruct | |
Code int | |
} | |
func main() { | |
// Example error string with key-value pairs. | |
errStr := `Error: something went wrong code=500 msg="Internal Server Error" detail="User not found"` | |
// Parse the error context. | |
context := ParseErrorContext(errStr) | |
// Print the parsed context. | |
for key, value := range context { | |
fmt.Printf("%s: %s\n", key, value) | |
} | |
// Example error | |
err := errors.New("original error") | |
// Example context as map | |
contextMap := map[string]any{ | |
"level1": map[string]any{ | |
"level2": "some_value", | |
}, | |
} | |
// Example context as struct | |
contextStruct := OuterStruct{ | |
Inner: &InnerStruct{ | |
Level3: "struct_value", | |
}, | |
Code: 500, | |
} | |
// Extend error with map context | |
errWithMapContext := ExtendErrorWithContext(err, contextMap) | |
fmt.Println(errWithMapContext) | |
// Outputs: original error level1=map[level2:some_value] | |
// Extend error with struct context | |
errWithStructContext := ExtendErrorWithContext(err, contextStruct) | |
fmt.Println(errWithStructContext) | |
// Outputs: original error Inner=&{Level3:struct_value} Code=500 | |
// Parse error context | |
parsedContext := ParseErrorContext(errWithStructContext.Error()) | |
fmt.Println(parsedContext) | |
// Outputs: map[Code:500 Inner:map[Level3:struct_value]] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment