github.com/epsagon/epsagon-go@v1.39.0/README.md (about) 1 <p align="center"> 2 <a href="https://epsagon.com" target="_blank" align="center"> 3 <img src="https://cdn2.hubspot.net/hubfs/4636301/Positive%20RGB_Logo%20Horizontal%20-01.svg" width="300"> 4 </a> 5 <br /> 6 </p> 7 8 [](https://travis-ci.com/epsagon/epsagon-go) 9 [](https://godoc.org/github.com/epsagon/epsagon-go) 10 11 # Epsagon Tracing for Go 12 13  14 15 16 This package provides tracing to Go applications for the collection of distributed tracing and performance metrics in [Epsagon](https://app.epsagon.com/?utm_source=github). 17 18 19 ## Contents 20 21 - [Installation](#installation) 22 - [Usage](#usage) 23 - [Tagging Traces](#tagging-traces) 24 - [Custom Errors](#custom-errors) 25 - [Ignored Keys](#ignored-keys) 26 - [Frameworks](#frameworks) 27 - [Integrations](#integrations) 28 - [Configuration](#configuration) 29 - [Getting Help](#getting-help) 30 - [Opening Issues](#opening-issues) 31 - [License](#license) 32 33 34 ## Installation 35 36 To install Epsagon, simply run: 37 ```sh 38 go get github.com/epsagon/epsagon-go 39 ``` 40 41 Or using `dep`: 42 ```sh 43 dep ensure -add github.com/epsagon/epsagon-go 44 ``` 45 46 ## Usage 47 48 ### Tagging Traces 49 50 You can add custom tags to your traces, for easier filtering and aggregations. 51 52 Add the following call inside your code: 53 ```go 54 epsagon.Label("key", "value") 55 epsagon.Label("user_id", user_id) 56 ``` 57 58 You can also use it to ship custom metrics: 59 ```go 60 epsagon.Label("key", "metric") 61 epsagon.Label("items_in_cart", items_in_cart) 62 ``` 63 64 Valid types are `string`, `bool`, `int` and `float`. 65 Custom labels are not trimmed with the trace events in case the trace is too big 66 67 ### Custom Errors 68 69 You can set a trace as an error (although handled correctly) to get an alert or just follow it on the dashboard. 70 Add the following call inside your code: 71 ```go 72 epsagon.TypeError("My custom error", "Custom Error Type") 73 # Or manually add an error 74 epsagon.TypeError(errors.New("My custom error"), "Custom Error Type") 75 ``` 76 77 You can also set a tracer as an error with a default error type: 78 ```go 79 epsagon.Error("My custom error") 80 # Or manually add an error 81 epsagon.Error(errors.New("My custom error")) 82 ``` 83 84 Valid types are `string` and `error`. 85 86 ### Ignored Keys 87 88 You can set keys that will be masked in the sent trace from the events metadata to hide selected information: 89 ```go 90 config.IgnoredKeys = []string{"password"} 91 client := http.Client{Transport: epsagonhttp.NewTracingTransport(ctx)} 92 // This password will be masked in the sent trace: 93 decodedJSON, err := json.Marshal(map[string]string{"password": "abcde", "animal": "lion"}) 94 resp, err := client.Post("http://example.com/upload", "application/json", bytes.NewReader(decodedJSON)) 95 ``` 96 97 ## Frameworks 98 99 The following frameworks are supported by Epsagon: 100 101 |Framework |Supported Version | 102 |----------------------------------------|---------------------------| 103 |[AWS Lambda](#aws-lambda) |All | 104 |[Generic Function](#generic) |All | 105 |[HTTP](#http) |All | 106 |[Gin](#gin) |All | 107 |[Fiber](#fiber) | >= 2.11.0 | 108 |[go-redis](#go-redis) | >= 8.0.0 | 109 |[mongo](#mongo) |>=1.0 | 110 111 112 ### AWS Lambda 113 114 Tracing Lambda functions can be done in the following method: 115 116 ```go 117 package main 118 119 import ( 120 "github.com/aws/aws-lambda-go/events" 121 "github.com/aws/aws-lambda-go/lambda" 122 "github.com/epsagon/epsagon-go/epsagon" 123 "log" 124 ) 125 126 func myHandler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { 127 log.Println("In myHandler, received body: ", request.Body) 128 return events.APIGatewayProxyResponse{Body: request.Body, StatusCode: 200}, nil 129 } 130 131 func main() { 132 log.Println("enter main") 133 lambda.Start(epsagon.WrapLambdaHandler( 134 epsagon.NewTracerConfig("app-name-stage","epsagon-token"), 135 myHandler)) 136 } 137 ``` 138 139 ### Generic 140 141 You can instrument a single function, this function can use go routines inside and their operations will still be traced. 142 143 ```go 144 func doTask(a int, b string) (int, error) { 145 log.Printf("inside doTask: b = %s", b) 146 return a + 1, fmt.Errorf("boom") 147 } 148 func main() { 149 // With Epsagon instrumentation 150 config := epsagon.NewTracerConfig("generic-go-wrapper", "") 151 config.Debug = true 152 response := epsagon 153 154 .GoWrapper(config, doTask)(5, "hello") 155 res2 := response[0].Int() 156 errInterface := response[1].Interface() 157 } 158 ``` 159 Optionally, you can pass a custom name for your wrapped function. In the epsagon dashboard, 160 your wrapped function will be displayed with your configured name in all the relevant screens: 161 traces search, service map and more. 162 ``` 163 response := epsagon.GoWrapper(config, doTask, "<MyInstrumentedFuncName>")(5, "hello") 164 ``` 165 166 ### Concurrent Generic 167 In order to support more than one function being traced in the same environment (using different goroutines), use this wrapper as shown in the example below. The wrapped function has to receive a context as its first parameter, and pass it to the relevant wrapped operations. 168 169 170 ```go 171 func doTask(ctx context.Context, a int, b string, wg *sync.WaitGroup) (int, error) { 172 defer wg.Done() 173 log.Printf("inside doTask: b = %s", b) 174 client := epsagonhttp.Wrap(http.Client{}, ctx) 175 client.Get("https://epsagon.com/") 176 return a + 1, fmt.Errorf("boom") 177 } 178 179 func main() { 180 config := epsagon.NewTracerConfig("generic-go-wrapper", "") 181 config.Debug = true 182 var wg sync.WaitGroup 183 for i := 0; i < 5; i++ { 184 go epsagon.ConcurrentGoWrapper(config, doTask)(i, "hello", &wg) 185 } 186 wg.Wait() 187 time.Sleep(2 * time.Second) 188 } 189 ``` 190 Optionally, you can pass a custom name for your wrapped function. In the epsagon dashboard, 191 your wrapped function will be displayed with your configured name in all the relevant screens: 192 traces search, service map and more. 193 ``` 194 go epsagon.ConcurrentGoWrapper(config, doTask, "<MyInstrumentedFuncName>")(i, "hello", &wg) 195 ``` 196 197 ### http 198 199 Wrapping http handlers with Epsagon: 200 ```go 201 import ( 202 "github.com/epsagon/epsagon-go/epsagon" 203 epsagonhttp "github.com/epsagon/epsagon-go/wrappers/net/http" 204 ) 205 206 func main() { 207 mux := http.NewServeMux() 208 mux.HandleFunc("/ping", epsagonhttp.WrapHandleFunc( 209 epsagon.NewTracerConfig("test-http-mux", ""), 210 func(rw http.ResponseWriter, req *http.Request) { 211 epsagon.Error("Ping Endpoint Called", req.Context()) 212 rw.Write([]byte("Pong.\n")) 213 }), 214 "my-handler-name", 215 ) 216 217 http.ListenAndServe(":8080", mux) 218 } 219 ``` 220 221 The third and fourth arguments to `epsagonhttp.WrapHandleFunc` are optional and set the resource name and the hostname. If the resource name is not set then the wrapped funcdtion name is used and the hostname is taken from the request URL if omitted. 222 ```go 223 mux.HandleFunc("/ping", epsagonhttp.WrapHandleFunc( 224 epsagon.NewTracerConfig("test-http-mux", ""), 225 func(rw http.ResponseWriter, req *http.Request) { 226 epsagon.Error("Ping Endpoint Called", req.Context()) 227 rw.Write([]byte("Pong.\n")) 228 }), 229 "my-handler-name", 230 "test.hostname.com", 231 ) 232 ``` 233 234 To wrap nested libraries you can get the epsagon context from the request context: 235 ```go 236 client := http.Client{ 237 Transport: epsagonhttp.NewTracingTransport(req.Context())} 238 resp, err := client.Get("http://example.com") 239 ``` 240 241 ### gin 242 243 You can easily instrument gin applications with Epsagon: 244 245 ```go 246 import ( 247 "github.com/epsagon/epsagon-go/epsagon" 248 epsagongin "github.com/epsagon/epsagon-go/wrappers/gin" 249 "github.com/gin-gonic/gin" 250 ) 251 252 func main() { 253 r := epsagongin.GinRouterWrapper{ 254 IRouter: gin.Default(), 255 Hostname: "my_site", 256 Config: epsagon.NewTracerConfig( 257 "test-gin-application", "", 258 ), 259 } 260 261 r.GET("/ping", func(c *gin.Context) { 262 c.JSON(200, gin.H{ 263 "message": "pong", 264 }) 265 }) 266 r.IRouter.(*gin.Engine).Run() 267 ``` 268 269 If you want to instument other integrated libraries inside the gin handler you can get the Epsagon context from the gin.Context to do that: 270 271 ```go 272 client := http.Client{ 273 Transport: epsagonhttp.NewTracingTransport(epsagongin.EpsagonContext(c))} 274 resp, err := client.Get("http://example.com") 275 ``` 276 277 ### fiber 278 279 You can easily instrument fiber applications with Epsagon middleware: 280 281 ```go 282 import ( 283 "github.com/epsagon/epsagon-go/epsagon" 284 epsagonfiber "github.com/epsagon/epsagon-go/wrappers/fiber" 285 "github.com/gofiber/fiber/v2" 286 ) 287 288 func main() { 289 config := epsagon.NewTracerConfig( 290 "fiber-example", "", 291 ) 292 config.MetadataOnly = false 293 app := fiber.New() 294 // Match all routes 295 epsagonMiddleware := &epsagonfiber.FiberEpsagonMiddleware{ 296 Config: config, 297 } 298 app.Use(epsagonMiddleware.HandlerFunc()) 299 app.Post("/", func(c *fiber.Ctx) error { 300 return c.SendString("Hello, World 👋!") 301 }) 302 303 app.Listen("0.0.0.0:3000") 304 } 305 app.Post("/", func(c *fiber.Ctx) error { 306 return c.SendString("Hello, World 👋!") 307 }) 308 ``` 309 310 If you want to instument other integrated libraries inside the fiber handler you can get the Epsagon context from the fiber.Ctx UserContext function to do that: 311 312 ```go 313 client := http.Client{ 314 Transport: epsagonhttp.NewTracingTransport(c.UserContext() 315 )} 316 resp, err := client.Get("http://example.com") 317 ``` 318 319 ### go-redis 320 321 The go-redis instrumentation supports both single and pipeline operations ([full example](https://github.com/epsagon/epsagon-go/tree/master/example)): 322 323 ```go 324 func main() { 325 config := epsagon.NewTracerConfig("redis-wrapper-test", "") 326 config.MetadataOnly = false 327 328 mux := http.NewServeMux() 329 mux.HandleFunc("/ping", epsagonhttp.WrapHandleFunc( 330 config, 331 func(w http.ResponseWriter, req *http.Request) { 332 // initialize the redis client as usual 333 // make sure to pass in the epsagon tracer context 334 rdb := epsagonredis.NewClient(&redis.Options{ 335 Addr: "localhost:6379", 336 Password: "", 337 DB: 0, 338 }, req.Context()) 339 340 value, _ := rdb.Get(context.Background(), "somekey").Result() 341 io.WriteString(w, value) 342 }), 343 ) 344 http.ListenAndServe(":8080", mux) 345 } 346 ``` 347 348 ### mongo 349 350 Trace through all Mongo collection operations by wrapping the Collection: 351 352 ```go 353 354 package main 355 356 import ( 357 "context" 358 "github.com/epsagon/epsagon-go/epsagon" 359 epsagonmongo "github.com/epsagon/epsagon-go/wrappers/mongo" 360 "time" 361 362 ) 363 364 365 func main() { 366 367 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 368 defer cancel() 369 client, err := mongo.Connect( 370 ctx, 371 options.Client().ApplyURI("mongodb://..."), 372 ) 373 defer func() { 374 if err = client.Disconnect(ctx); err != nil { 375 panic(err) 376 } 377 }() 378 379 db := client.Database("DB") 380 coll := epsagonmongo.WrapMongoCollection( 381 db.Collection("COLL"), 382 ) 383 } 384 385 ``` 386 387 ## Integrations 388 389 Epsagon provides out-of-the-box instrumentation (tracing) for many popular frameworks and libraries. 390 391 |Library |Supported Version | 392 |---------------------|---------------------------| 393 |[net/http](#net/http)|Fully supported | 394 |aws-sdk-go |`>=1.10.0` | 395 |aws-sdk-go-v2 |`>=0.23.0` | 396 397 398 ### net/http 399 400 Any http request can be traced using a custom RoundTripper implementation and using that in an http.Client: 401 402 ```go 403 import ( 404 "github.com/epsagon/epsagon-go/wrappers/net/http" 405 ... 406 client := http.Client{Transport: epsagonhttp.NewTracingTransport()} 407 resp, err := client.Get(anyurl) 408 ``` 409 410 If you are already using a custom RoundTripper implementation, such as for AWS V4 request signing, you can wrap it: 411 412 ```go 413 import ( 414 "github.com/epsagon/epsagon-go/wrappers/net/http" 415 ... 416 rt := &custom.Roundtripper{} 417 client := http.Client{Transport: epsagonhttp.NewWrappedTracingTransport(rt)} 418 resp, err := client.Get(anyurl) 419 ``` 420 421 The wrapped http.Client functionality exists for backwards compatibility: 422 423 ```go 424 import ( 425 "github.com/epsagon/epsagon-go/wrappers/net/http" 426 ... 427 client := epsagonhttp.Wrap(http.Client{}) 428 resp, err := client.Get(anyurl) 429 ``` 430 431 If you want to disable data collection only for the calls made by this client set `client.MetadataOnly = true` 432 433 ### aws-sdk-go 434 435 Wrapping of aws-sdk-go is done through the Session object that has to be created to communicate with AWS: 436 437 ```go 438 import ( 439 ... 440 "github.com/epsagon/epsagon-go/wrappers/aws/aws-sdk-go/aws" 441 ) 442 ... 443 sess := epsagonawswrapper.WrapSession(session.Must(session.NewSession())) 444 svcSQS := sqs.New(sess) 445 ``` 446 447 448 ### aws-sdk-go-v2 449 450 Wrapping of aws-sdk-go-v2 is done through the service object: 451 ```go 452 import ( 453 ... 454 "github.com/aws/aws-sdk-go-v2/service/dynamodb" 455 "github.com/epsagon/epsagon-go/epsagon" 456 ) 457 458 svc := epsagon.WrapAwsV2Service(dynamodb.New(cfg)).(*dynamodb.Client) 459 ... 460 ``` 461 462 463 464 465 466 ## Configuration 467 468 Advanced options can be configured as a parameter to the `Config` struct to the `WrapLambdaHandler` or as environment variables. 469 470 |Parameter |Environment Variable |Type |Default |Description | 471 |----------------------|------------------------------------|-------|-------------|-----------------------------------------------------------------------------------| 472 |Token |EPSAGON_TOKEN |String |- |Epsagon account token | 473 |ApplicationName |- |String |- |Application name that will be set for traces | 474 |MetadataOnly |EPSAGON_METADATA |Boolean|`true` |Whether to send only the metadata (`True`) or also the payloads (`False`) | 475 |CollectorURL |EPSAGON_COLLECTOR_URL |String |- |The address of the trace collector to send trace to | 476 |Debug |EPSAGON_DEBUG |Boolean|`False` |Enable debug prints for troubleshooting | 477 |SendTimeout |EPSAGON_SEND_TIMEOUT_SEC |String |`1s` |The timeout duration to send the traces to the trace collector | 478 |MaxTraceSize |EPSAGON_MAX_TRACE_SIZE |Integer|`1287936` |The max allowed trace size (in bytes). Defaults to 64KB, max allowed size - 512KB | 479 |_ |EPSAGON_LAMBDA_TIMEOUT_THRESHOLD_MS |Integer|`200` |The threshold in milliseconds to send the trace before a Lambda timeout occurs | 480 481 482 ## Getting Help 483 484 If you have any issue around using the library or the product, please don't hesitate to: 485 486 * Use the [documentation](https://docs.epsagon.com). 487 * Use the help widget inside the product. 488 * Open an issue in GitHub. 489 490 491 ## Opening Issues 492 493 If you encounter a bug with the Epsagon library for Go, we want to hear about it. 494 495 When opening a new issue, please provide as much information about the environment: 496 * Library version, Go runtime version, dependencies, etc. 497 * Snippet of the usage. 498 * A reproducible example can really help. 499 500 The GitHub issues are intended for bug reports and feature requests. 501 For help and questions about Epsagon, use the help widget inside the product. 502 503 ## License 504 505 Provided under the MIT license. See LICENSE for details. 506 507 Copyright 2020, Epsagon