github.com/epsagon/epsagon-go@v1.39.0/wrappers/mongo/common_utils.go (about)

     1  package epsagonmongo
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"github.com/epsagon/epsagon-go/epsagon"
     8  	"github.com/epsagon/epsagon-go/protocol"
     9  	"github.com/epsagon/epsagon-go/tracer"
    10  	"github.com/google/uuid"
    11  	"go.mongodb.org/mongo-driver/mongo"
    12  	"strconv"
    13  
    14  	"reflect"
    15  	"runtime"
    16  	"strings"
    17  )
    18  
    19  // currentFuncName returns the name of the caller function as a string
    20  // for func Foo() { x := currentFuncName() }, x == "Foo"
    21  func currentFuncName() string {
    22  	current := make([]uintptr, 1)
    23  	if level := runtime.Callers(2, current); level == 0 {
    24  		return ""
    25  	}
    26  	caller := runtime.FuncForPC(current[0] - 1)
    27  	if caller == nil {
    28  		return ""
    29  	}
    30  	sysFuncName := caller.Name()
    31  	return partitionByDelimiterAtIndex(sysFuncName, ".", -1)
    32  
    33  }
    34  
    35  func startMongoEvent(opName string, coll *MongoCollectionWrapper) *protocol.Event {
    36  	defer epsagon.GeneralEpsagonRecover("mongo-driver", currentFuncName(), coll.tracer)
    37  	return &protocol.Event{
    38  		Id:        "mongodb-" + uuid.New().String(),
    39  		Origin:    "mongodb",
    40  		ErrorCode: protocol.ErrorCode_OK,
    41  		StartTime: tracer.GetTimestamp(),
    42  		Resource:  createMongoResource(opName, coll),
    43  	}
    44  }
    45  
    46  func completeMongoEvent(currentTracer tracer.Tracer, event *protocol.Event) {
    47  	defer epsagon.GeneralEpsagonRecover("mongo-driver", currentFuncName(), currentTracer)
    48  	event.Duration = tracer.GetTimestamp() - event.StartTime
    49  	currentTracer.AddEvent(event)
    50  
    51  }
    52  
    53  func createMongoResource(opName string, coll *MongoCollectionWrapper) *protocol.Resource {
    54  	defer epsagon.GeneralEpsagonRecover("mongo-driver", currentFuncName(), coll.tracer)
    55  	return &protocol.Resource{
    56  		Name:      coll.Database().Name() + "." + coll.collection.Name(),
    57  		Type:      "mongodb",
    58  		Operation: opName,
    59  		Metadata:  make(map[string]string),
    60  	}
    61  }
    62  
    63  // extractStructFields parses the fields from a struct and adds it to metadata under field name metaField
    64  // attempts to convert to int if possible, else keeps as a string
    65  func extractStructFields(
    66  	metadata map[string]string,
    67  	metaField string,
    68  	s interface{},
    69  ) {
    70  	val := reflect.ValueOf(s)
    71  	if val.Kind() == reflect.Ptr {
    72  		val = val.Elem()
    73  	}
    74  	valuesMap := make(map[string]string, val.NumField())
    75  
    76  	for i := 0; i < val.NumField(); i++ {
    77  		if val.Field(i).CanInterface() {
    78  			fieldVal := val.Field(i)
    79  			v := fmt.Sprintf("%q", fieldVal)
    80  			if _, err := strconv.Atoi(v); err == nil {
    81  				v = strconv.FormatInt(fieldVal.Int(), 10)
    82  			}
    83  			valuesMap[val.Type().Field(i).Name] = v
    84  		}
    85  	}
    86  	doc, _ := json.Marshal(valuesMap)
    87  	metadata[metaField] = string(doc)
    88  }
    89  
    90  // marshalToMetadata marshals any object to JSON and adds it as a string to metadata under metaFielf
    91  func marshalToMetadata(
    92  	metadata map[string]string,
    93  	metaField string,
    94  	s interface{},
    95  	config *tracer.Config,
    96  ) {
    97  	docBytes, err := json.Marshal(s)
    98  	if err != nil {
    99  		epsagon.DebugLog(config.Debug, "Could not Marshal JSON", err)
   100  	}
   101  	docString := string(docBytes)
   102  	if docString == "" {
   103  		return
   104  	}
   105  	metadata[metaField] = docString
   106  }
   107  
   108  // readCursor accepts a cursor and returns a slice of maps
   109  // each map represents a mongo document
   110  func readCursor(cursor *mongo.Cursor) ([]map[string]string, error) {
   111  	var documents []map[string]string
   112  	err := cursor.All(context.Background(), &documents)
   113  	return documents, err
   114  }
   115  
   116  func logOperationFailure(messages ...string) {
   117  	for _, m := range messages {
   118  		epsagon.DebugLog(true, "[MONGO]", m)
   119  	}
   120  }
   121  
   122  // partition a string by delimiter and return the partitioned at the index
   123  func partitionByDelimiterAtIndex(original, delimiter string, index int) string {
   124  	s := strings.Split(original, delimiter)
   125  	i := moduloFloor(len(s), index)
   126  	return s[i]
   127  
   128  }
   129  
   130  // flooring an index by size
   131  // useful for wrapping around negative indices to positive
   132  func moduloFloor(size, index int) int {
   133  	return (index + size) % size
   134  }