Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

elasticapm: add Context.SetFramework #252

Merged
merged 3 commits into from
Oct 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 35 additions & 9 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,25 @@ import (

// Context provides methods for setting transaction and error context.
type Context struct {
model model.Context
request model.Request
requestBody model.RequestBody
requestHeaders model.RequestHeaders
requestSocket model.RequestSocket
response model.Response
responseHeaders model.ResponseHeaders
user model.User
captureBodyMask CaptureBodyMode
model model.Context
request model.Request
requestBody model.RequestBody
requestHeaders model.RequestHeaders
requestSocket model.RequestSocket
response model.Response
responseHeaders model.ResponseHeaders
user model.User
service model.Service
serviceFramework model.Framework
captureBodyMask CaptureBodyMode
}

func (c *Context) build() *model.Context {
switch {
case c.model.Request != nil:
case c.model.Response != nil:
case c.model.User != nil:
case c.model.Service != nil:
case len(c.model.Custom) != 0:
case len(c.model.Tags) != 0:
default:
Expand Down Expand Up @@ -75,6 +78,29 @@ func (c *Context) SetTag(key, value string) {
}
}

// SetFramework sets the framework name and version in the context.
//
// This is used for identifying the framework in which the context
// was created, such as Gin or Echo.
//
// If the name is empty, this is a no-op. If version is empty, then
// it will be set to "unspecified".
func (c *Context) SetFramework(name, version string) {
if name == "" {
return
}
if version == "" {
// Framework version is required.
version = "unspecified"
}
c.serviceFramework = model.Framework{
Name: truncateKeyword(name),
Version: truncateKeyword(version),
}
c.service.Framework = &c.serviceFramework
c.model.Service = &c.service
}

// SetHTTPRequest sets details of the HTTP request in the context.
//
// This function relates to server-side requests. Various proxy
Expand Down
49 changes: 38 additions & 11 deletions context_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package elasticapm_test

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/elastic/apm-agent-go"
"github.com/elastic/apm-agent-go/apmtest"
"github.com/elastic/apm-agent-go/model"
"github.com/elastic/apm-agent-go/transport/transporttest"
)

func TestContextUser(t *testing.T) {
Expand All @@ -31,15 +33,40 @@ func TestContextUser(t *testing.T) {
})
}

func testSendTransaction(t *testing.T, f func(tx *elasticapm.Transaction)) model.Transaction {
tracer, r := transporttest.NewRecorderTracer()
defer tracer.Close()

tx := tracer.StartTransaction("name", "type")
f(tx)
tx.End()
tracer.Flush(nil)
func TestContextFramework(t *testing.T) {
t.Run("name_unspecified", func(t *testing.T) {
tx := testSendTransaction(t, func(tx *elasticapm.Transaction) {
tx.Context.SetFramework("", "1.0")
})
assert.Nil(t, tx.Context)
})
t.Run("version_specified", func(t *testing.T) {
tx := testSendTransaction(t, func(tx *elasticapm.Transaction) {
tx.Context.SetFramework("framework", "1.0")
})
require.NotNil(t, tx.Context)
require.NotNil(t, tx.Context.Service)
assert.Equal(t, &model.Framework{
Name: "framework",
Version: "1.0",
}, tx.Context.Service.Framework)
})
t.Run("version_unspecified", func(t *testing.T) {
tx := testSendTransaction(t, func(tx *elasticapm.Transaction) {
tx.Context.SetFramework("framework", "")
})
require.NotNil(t, tx.Context)
require.NotNil(t, tx.Context.Service)
assert.Equal(t, &model.Framework{
Name: "framework",
Version: "unspecified",
}, tx.Context.Service.Framework)
})
}

payloads := r.Payloads()
return payloads.Transactions[0]
func testSendTransaction(t *testing.T, f func(tx *elasticapm.Transaction)) model.Transaction {
transaction, _, _ := apmtest.WithTransaction(func(ctx context.Context) {
f(elasticapm.TransactionFromContext(ctx))
})
return transaction
}
75 changes: 66 additions & 9 deletions model/marshal_fastjson.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion model/marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ func TestMarshalTransaction(t *testing.T) {
"duration": 123.456,
"result": "418",
"context": map[string]interface{}{
"service": map[string]interface{}{
"framework": map[string]interface{}{
"name": "framework-name",
"version": "framework-version",
},
},
"request": map[string]interface{}{
"url": map[string]interface{}{
"full": "https://testing.invalid/foo/bar?baz#qux",
Expand Down Expand Up @@ -539,6 +545,12 @@ func fakeTransaction() model.Transaction {
Tags: map[string]string{
"tag": "urit",
},
Service: &model.Service{
Framework: &model.Framework{
Name: "framework-name",
Version: "framework-version",
},
},
},
SpanCount: model.SpanCount{
Started: 99,
Expand Down Expand Up @@ -597,7 +609,7 @@ func fakeService() *model.Service {
Name: "fake-service",
Version: "1.0.0-rc1",
Environment: "dev",
Agent: model.Agent{
Agent: &model.Agent{
Name: "go",
Version: "0.1.0",
},
Expand Down
7 changes: 5 additions & 2 deletions model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
// Service represents the service handling transactions being traced.
type Service struct {
// Name is the immutable name of the service.
Name string `json:"name"`
Name string `json:"name,omitempty"`

// Version is the version of the service, if it has one.
Version string `json:"version,omitempty"`
Expand All @@ -20,7 +20,7 @@ type Service struct {

// Agent holds information about the Elastic APM agent tracing this
// service's transactions.
Agent Agent `json:"agent"`
Agent *Agent `json:"agent,omitempty"`

// Framework holds information about the service's framework, if any.
Framework *Framework `json:"framework,omitempty"`
Expand Down Expand Up @@ -248,6 +248,9 @@ type Context struct {

// Tags holds user-defined key/value pairs.
Tags map[string]string `json:"tags,omitempty"`

// Service holds values to overrides service-level metadata.
Service *Service `json:"service,omitempty"`
}

// User holds information about an authenticated user.
Expand Down
1 change: 1 addition & 0 deletions module/apmecho/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func (m *middleware) handle(c echo.Context) error {
}

func setContext(ctx *elasticapm.Context, req *http.Request, resp *echo.Response, body *elasticapm.BodyCapturer) {
ctx.SetFramework("echo", echo.Version)
ctx.SetHTTPRequest(req)
ctx.SetHTTPRequestBody(body)
if resp.Committed {
Expand Down
6 changes: 6 additions & 0 deletions module/apmecho/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ func TestEchoMiddleware(t *testing.T) {
assert.Equal(t, "HTTP 4xx", transaction.Result)

assert.Equal(t, &model.Context{
Service: &model.Service{
Framework: &model.Framework{
Name: "echo",
Version: echo.Version,
},
},
Request: &model.Request{
Socket: &model.RequestSocket{
RemoteAddress: "client.testing",
Expand Down
1 change: 1 addition & 0 deletions module/apmgin/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func (m *middleware) handle(c *gin.Context) {
}

func setContext(ctx *elasticapm.Context, c *gin.Context, body *elasticapm.BodyCapturer) {
ctx.SetFramework("gin", gin.Version)
ctx.SetHTTPRequest(c.Request)
ctx.SetHTTPRequestBody(body)
if c.Writer.Written() {
Expand Down
6 changes: 6 additions & 0 deletions module/apmgin/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ func TestMiddleware(t *testing.T) {
assert.Equal(t, "HTTP 2xx", transaction.Result)

assert.Equal(t, &model.Context{
Service: &model.Service{
Framework: &model.Framework{
Name: "gin",
Version: gin.Version,
},
},
Request: &model.Request{
Socket: &model.RequestSocket{
RemoteAddress: "client.testing",
Expand Down
2 changes: 2 additions & 0 deletions module/apmgrpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func NewUnaryServerInterceptor(o ...ServerOption) grpc.UnaryServerInterceptor {
tx := opts.tracer.StartTransaction(info.FullMethod, "grpc")
ctx = elasticapm.ContextWithTransaction(ctx, tx)
defer tx.End()
tx.Context.SetFramework("grpc", grpc.Version)

if tx.Sampled() {
p, ok := peer.FromContext(ctx)
Expand All @@ -61,6 +62,7 @@ func NewUnaryServerInterceptor(o ...ServerOption) grpc.UnaryServerInterceptor {
if r != nil {
e := opts.tracer.Recovered(r)
e.SetTransaction(tx)
e.Context.SetFramework("grpc", grpc.Version)
e.Handled = opts.recover
e.Send()
if opts.recover {
Expand Down
6 changes: 6 additions & 0 deletions module/apmgrpc/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ func testServerTransactionHappy(t *testing.T, p testParams) {
assert.Contains(t, grpcContext, "peer.address")
delete(grpcContext, "peer.address")
assert.Equal(t, &model.Context{
Service: &model.Service{
Framework: &model.Framework{
Name: "grpc",
Version: grpc.Version,
},
},
Custom: model.IfaceMap{{
Key: "grpc",
Value: map[string]interface{}{},
Expand Down
2 changes: 1 addition & 1 deletion utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func makeService(name, version, environment string) model.Service {
Name: truncateKeyword(name),
Version: truncateKeyword(version),
Environment: truncateKeyword(environment),
Agent: goAgent,
Agent: &goAgent,
Language: &goLanguage,
Runtime: &goRuntime,
}
Expand Down