Are you building modern apps in Golang? This multi-part blog series will help you instrument Golang faster by providing tips, guidance, and best practices with New Relic.
Typically, the content in this blog would be presented in a workshop, but we've made it a blog series so anyone can follow along "hands on," using sample code, Instruqt, this blog, and videos. For the best experience:
- See and run the full code that I'll be walking through in this multi-part series.
- Follow the hands-on lab in Instruqt.
- Read along with this blog and follow the videos below.
The following two videos will give an overview of Instruqt and some instructions about how to use it if you haven't done that before.
Overview of Instruqt
Instruqt UI navigation
Now that you're set up, let's learn a little more about the instrumentation of Golang.
Tip 4: Transactions and segments
The New Relic Go instrumentation revolves around two fundamental concepts: Transactions and segments. These concepts are essential to understanding Go integrations and examples.
Transactions are a crucial aspect of Go instrumentation. It refers to a logical unit of work in a software application comprising function calls and method calls executed during a specific time frame. A transaction usually represents a key transaction encompassing the activity from when the application receives a request to when it sends the response.
Transactions are crucial because they help developers identify the slowest parts of the code. By tracking these transactions, developers can identify bottlenecks and optimize the performance of the application. This information can then be used to optimize the user experience and improve customer satisfaction.
Here's an example of monitoring a web transaction as seen in our docs:
// Begin a new transaction with a specified name and defer its ending.
txn := app.StartTransaction("transaction_name")
defer txn.End()
// req variable of type *http.Request to mark the transaction as a web transaction.
txn.SetWebRequestHTTP(req)
// writer is a http.ResponseWriter, use the returned writer in place of the original.
writer = txn.SetWebResponse(writer)
writer.WriteHeader(500)
In software applications, a transaction may consist of multiple segments representing a specific part of the process. For instance, a transaction in an ecommerce platform may involve adding items to a shopping cart, processing payment, and updating inventory levels.
To gain insights into these transactions' performance, measuring the time taken by each segment is essential. By instrumenting segments, you can track the time taken by functions and code blocks that make up the segments, such as external calls, datastore calls, adding messages to queues, and background tasks. This information is powerful for gaining insights into the time taken by specific functions and code blocks.
Here's an example of a segment as seen in our docs:
segment := newrelic.Segment{}
segment.Name = "yourSegmentName"
segment.StartTime = txn.StartSegmentNow()
// insert your code logic here
segment.End()
To effectively instrument Go, it is important to understand the three types of segments. Choosing the appropriate segment is essential to ensure that they appear correctly in the UI. Properly ending each transaction or segment is mandatory, as failure to do so will result in the instrumented function not appearing in New Relic.
Function segments: Instrumenting a function as a segment is equivalent to instrumenting any other block of code as a segment. This means that the process of measuring the execution time of a function or any arbitrary code block is essentially the same.
Here's an example of a function segment as seen in our docs:
segment := txn.StartSegment("yourSegmentName")
// insert your code logic here
segment.End()
Datastore Segments: If you want to improve the performance of your datastore calls, you can use a datastore segment to instrument them. By doing so, you'll be able to gather more detailed insights into how your datastore is performing and identify any areas that may be causing issues. A datastore segment allows you to see the resulting datastore segments in the transactions breakdown table and Databases tab of the transactions page within New Relic. This gives you access to more granular data about the performance of your datastore, which can help you make more informed decisions about optimizing it for your specific needs.
Here's an example of a datastore segment as seen in our docs:
s := newrelic.DatastoreSegment{
Product: newrelic.DatastoreMySQL,
Collection: "users",
Operation: "INSERT",
ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
QueryParameters: map[string]interface{}{
"name": "Dracula",
"age": 439,
},
Host: "mysql-server-1",
PortPathOrID: "3306",
DatabaseName: "my_database",
}
s.StartTime = txn.StartSegmentNow()
// insert your code logic here
s.End()
External segments: For your Go application's external service calls, such as web services, cloud resources, and other network requests, you can instrument them with external segments. This allows the resulting external segments to be displayed in the transactions breakdown table and external services page within New Relic.
Here's an example of a external segment as seen in our docs:
func external(txn *newrelic.Transaction, req *http.Request) (*http.Response, error) {
s := newrelic.StartExternalSegment(txn, req)
response, err := http.DefaultClient.Do(req)
s.Response = response
s.End()
return response, err
}
In summary, here's a sample code of how everything fits together:
func randomFormat() string {
//Monitor a transaction
nrTxnTracer := nrApp.StartTransaction("randomFormat")
defer nrTxnTracer.End()
// Random sleep to simulate delays
randomDelayOuter := rand.Intn(40)
time.Sleep(time.Duration(randomDelayOuter) * time.Microsecond)
// Create a segment
nrSegment := nrTxnTracer.StartSegment("Formats")
// Random sleep to simulate delays
randomDelayInner := rand.Intn(80)
time.Sleep(time.Duration(randomDelayInner) * time.Microsecond)
// A slice of message formats.
formats := []string{
"Hi, %v. Welcome!",
"Great to see you, %v!",
"Good day, %v! Well met!",
"%v! Hi there!",
"Greetings %v!",
"Hello there, %v!",
}
// End a segment
nrSegment.End()
// Return a randomly selected message format by specifying
// a random index for the slice of formats.
return formats[rand.Intn(len(formats))]
}
Tip 5: Custom attributes
Utilizing custom attributes can be highly beneficial when using a New Relic such as APM, browser, mobile, infrastructure, and synthetics. This enhancement allows for the addition of personalized metadata to existing events. Custom attributes provide essential business and operational context to existing events, consisting of key-value pairs that offer metadata related to the associated events. This provides the ability to improve custom charts and queries to analyze the data, as discussed in Tip 1: Learn why you should instrument.
Various types of business context can be added as custom attributes, including customer ID, customer market segment, customer value classification, or transaction identifiers. Operational context can also be added as custom attributes, including feature flags that were utilized, datastore used, infrastructure accessed, or errors detected and ignored.
Here's an example of AddAttribute()
to add metadata to your transactions:
txn.AddAttribute("product", "shoes")
txn.AddAttribute("price", 69.90)
txn.AddAttribute("onSale", true)
See additional details using the Go agent API, including AddAttribute().
Here's an example of capturing a custom attribute:
func Hello(name string) (string, error) {
// Monitor a transaction
nrTxnTracer := nrApp.StartTransaction("Hello")
defer nrTxnTracer.End()
// If no name was given, return an error with a message.
if name == "" {
return name, errors.New("empty name")
}
// Create a message using a random format.
message := fmt.Sprintf(randomFormat(), name)
// Custom attributes by using this method in a transaction
nrTxnTracer.AddAttribute("message", message)
return message, nil
}
Tip 6: Modularize your instrumentation
At some point, you will need to attach instrumentation at scale throughout your environment. This may seem daunting, but it' a critical step in ensuring your application performs optimally. Consider discussing with your team or engineering lead to determine the best strategic location for instrumenting all of your request calls.
This could include examining the architecture of your application to identify potential chokepoints and considering the types of metrics you want to collect. You may also want to consider any potential tradeoffs between the depth and breadth of instrumentation. Ultimately, the key is to approach this process thoughtfully and systematically, ensuring that your instrumentation strategy aligns with your broader goals and objectives.
We recommend instrumenting the middleware layer with a wrapper, as demonstrated in this example using the Gin framework. This package, nrgin, instruments https://github.com/gin-gonic/gin applications.
router := gin.Default()
// add the nrgin middleware as the first middleware or route in your application.
router.Use(nrgin.Middleware(app))
The middleware creates a Gin middleware that instrument all requests. Use this package to instrument inbound requests handled by a gin.Engine. Call nrgin.Middleware to get a gin.HandlerFunc, which can be added to your application as a middleware.
router := gin.Default()
// Package nrgin instruments https://github.com/gin-gonic/gin applications.
router.Use(nrgin.Middleware(nrApp))
router.GET("/games", getgames)
router.GET("/games/:id", getgameByID)
router.POST("/games", postgames)
Note these two pieces of helpful documentation:
For more information, don't forget to visit the official page for the New Relic Go agent.
다음 단계
If you're not already using New Relic, sign up for a free account to test out all of our features. Your free account includes 100 GB/month of free data ingest, one free full-access user, and unlimited free basic users.
Watch for the next part in this blog series for more guidance, best practices, and sample code.
이 블로그에 표현된 견해는 저자의 견해이며 반드시 New Relic의 견해를 반영하는 것은 아닙니다. 저자가 제공하는 모든 솔루션은 환경에 따라 다르며 New Relic에서 제공하는 상용 솔루션이나 지원의 일부가 아닙니다. 이 블로그 게시물과 관련된 질문 및 지원이 필요한 경우 Explorers Hub(discuss.newrelic.com)에서만 참여하십시오. 이 블로그에는 타사 사이트의 콘텐츠에 대한 링크가 포함될 수 있습니다. 이러한 링크를 제공함으로써 New Relic은 해당 사이트에서 사용할 수 있는 정보, 보기 또는 제품을 채택, 보증, 승인 또는 보증하지 않습니다.