Skip to content

Commit 2f6e0df

Browse files
committed
Register partial transaction in aws extn
1 parent 4d07ba8 commit 2f6e0df

File tree

4 files changed

+115
-34
lines changed

4 files changed

+115
-34
lines changed

modelwriter.go

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type modelWriter struct {
4848
// writeTransaction encodes tx as JSON to the buffer, and then resets tx.
4949
func (w *modelWriter) writeTransaction(tx *Transaction, td *TransactionData) {
5050
var modelTx model.Transaction
51-
w.buildModelTransaction(&modelTx, tx, td)
51+
BuildModelTransaction(&modelTx, tx, td)
5252
w.json.RawString(`{"transaction":`)
5353
modelTx.MarshalFastJSON(&w.json)
5454
w.json.RawByte('}')
@@ -103,39 +103,6 @@ func (w *modelWriter) writeMetrics(m *Metrics) {
103103
m.reset()
104104
}
105105

106-
func (w *modelWriter) buildModelTransaction(out *model.Transaction, tx *Transaction, td *TransactionData) {
107-
out.ID = model.SpanID(tx.traceContext.Span)
108-
out.TraceID = model.TraceID(tx.traceContext.Trace)
109-
sampled := tx.traceContext.Options.Recorded()
110-
if !sampled {
111-
out.Sampled = &notSampled
112-
}
113-
if tx.traceContext.State.haveSampleRate {
114-
out.SampleRate = &tx.traceContext.State.sampleRate
115-
}
116-
117-
out.ParentID = model.SpanID(tx.parentID)
118-
out.Name = truncateString(td.Name)
119-
out.Type = truncateString(td.Type)
120-
out.Result = truncateString(td.Result)
121-
out.Outcome = normalizeOutcome(td.Outcome)
122-
out.Timestamp = model.Time(td.timestamp.UTC())
123-
out.Duration = td.Duration.Seconds() * 1000
124-
out.SpanCount.Started = td.spansCreated
125-
out.SpanCount.Dropped = td.spansDropped
126-
out.OTel = td.Context.otel
127-
for _, sl := range td.links {
128-
out.Links = append(out.Links, model.SpanLink{TraceID: model.TraceID(sl.Trace), SpanID: model.SpanID(sl.Span)})
129-
}
130-
if dss := buildDroppedSpansStats(td.droppedSpansStats); len(dss) > 0 {
131-
out.DroppedSpansStats = dss
132-
}
133-
134-
if sampled {
135-
out.Context = td.Context.build()
136-
}
137-
}
138-
139106
func (w *modelWriter) buildModelSpan(out *model.Span, span *Span, sd *SpanData) {
140107
w.modelStacktrace = w.modelStacktrace[:0]
141108
out.ID = model.SpanID(span.traceContext.Span)
@@ -258,6 +225,40 @@ func (w *modelWriter) buildModelError(out *model.Error, e *ErrorData) {
258225
out.Culprit = truncateString(out.Culprit)
259226
}
260227

228+
// BuildModelTransaction converts apm transaction to model transaction
229+
func BuildModelTransaction(out *model.Transaction, tx *Transaction, td *TransactionData) {
230+
out.ID = model.SpanID(tx.traceContext.Span)
231+
out.TraceID = model.TraceID(tx.traceContext.Trace)
232+
sampled := tx.traceContext.Options.Recorded()
233+
if !sampled {
234+
out.Sampled = &notSampled
235+
}
236+
if tx.traceContext.State.haveSampleRate {
237+
out.SampleRate = &tx.traceContext.State.sampleRate
238+
}
239+
240+
out.ParentID = model.SpanID(tx.parentID)
241+
out.Name = truncateString(td.Name)
242+
out.Type = truncateString(td.Type)
243+
out.Result = truncateString(td.Result)
244+
out.Outcome = normalizeOutcome(td.Outcome)
245+
out.Timestamp = model.Time(td.timestamp.UTC())
246+
out.Duration = td.Duration.Seconds() * 1000
247+
out.SpanCount.Started = td.spansCreated
248+
out.SpanCount.Dropped = td.spansDropped
249+
out.OTel = td.Context.otel
250+
for _, sl := range td.links {
251+
out.Links = append(out.Links, model.SpanLink{TraceID: model.TraceID(sl.Trace), SpanID: model.SpanID(sl.Span)})
252+
}
253+
if dss := buildDroppedSpansStats(td.droppedSpansStats); len(dss) > 0 {
254+
out.DroppedSpansStats = dss
255+
}
256+
257+
if sampled {
258+
out.Context = td.Context.build()
259+
}
260+
}
261+
261262
func stacktraceCulprit(frames []model.StacktraceFrame) string {
262263
for _, frame := range frames {
263264
if !frame.LibraryFrame {

module/apmlambda/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module go.elastic.co/apm/module/apmlambda/v2
33
require (
44
github.com/aws/aws-lambda-go v1.8.0
55
go.elastic.co/apm/v2 v2.1.0
6+
go.elastic.co/fastjson v1.1.0
67
)
78

89
replace go.elastic.co/apm/v2 => ../..

module/apmlambda/lambda.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
package apmlambda // import "go.elastic.co/apm/module/apmlambda/v2"
1919

2020
import (
21+
"bytes"
2122
"log"
2223
"net"
24+
"net/http"
2325
"net/rpc"
2426
"os"
2527
"unicode/utf8"
@@ -28,7 +30,9 @@ import (
2830
"github.com/aws/aws-lambda-go/lambdacontext"
2931

3032
"go.elastic.co/apm/v2"
33+
"go.elastic.co/apm/v2/model"
3134
"go.elastic.co/apm/v2/stacktrace"
35+
"go.elastic.co/fastjson"
3236
)
3337

3438
const (
@@ -51,6 +55,10 @@ var (
5155
Request string `json:"request,omitempty"`
5256
Response string `json:"response,omitempty"`
5357
}
58+
59+
jsonw fastjson.Writer
60+
61+
ignoreTxnRegistration bool
5462
)
5563

5664
func init() {
@@ -72,6 +80,17 @@ func (f *Function) Ping(req *messages.PingRequest, response *messages.PingRespon
7280
return f.client.Call("Function.Ping", req, response)
7381
}
7482

83+
func createPartialTransactionJSON(apmTx *apm.Transaction, w *fastjson.Writer) error {
84+
var tx model.Transaction
85+
apm.BuildModelTransaction(&tx, apmTx, apmTx.TransactionData)
86+
w.RawString(`{"transaction":`)
87+
if err := tx.MarshalFastJSON(w); err != nil {
88+
return err
89+
}
90+
w.RawByte('}')
91+
return nil
92+
}
93+
7594
// Invoke invokes the Lambda function. This is our main trace point.
7695
func (f *Function) Invoke(req *messages.InvokeRequest, response *messages.InvokeResponse) error {
7796
tx := f.tracer.StartTransaction(lambdacontext.FunctionName, "function")
@@ -92,6 +111,30 @@ func (f *Function) Invoke(req *messages.InvokeRequest, response *messages.Invoke
92111
lambdaContext.Request = formatPayload(req.Payload)
93112
lambdaContext.Response = ""
94113

114+
if !ignoreTxnRegistration {
115+
defer jsonw.Reset()
116+
if err := createPartialTransactionJSON(tx, &jsonw); err != nil {
117+
log.Printf("failed to create partial transaction for registration: %v", err)
118+
} else {
119+
resp, err := http.Post(
120+
// TODO: @lahsivjar better way to get base URI
121+
"http://localhost:8200/register/transaction",
122+
"application/vnd.elastic.apm.transaction+json",
123+
bytes.NewReader(jsonw.Bytes()),
124+
)
125+
// Don't attempt registration for next invocations if network
126+
// error or the registration endpoint is not found.
127+
if err != nil || resp.StatusCode == 404 {
128+
ignoreTxnRegistration = true
129+
}
130+
if err != nil {
131+
log.Printf("failed to register transaction, req failed with error: %v", err)
132+
}
133+
if resp.StatusCode/100 != 2 {
134+
log.Printf("failed to register transaction, req failed with status code: %d", resp.StatusCode)
135+
}
136+
}
137+
}
95138
err := f.client.Call("Function.Invoke", req, response)
96139
if err != nil {
97140
e := f.tracer.NewError(err)

module/apmlambda/lambda_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package apmlambda
19+
20+
import (
21+
"encoding/json"
22+
"testing"
23+
24+
"github.com/stretchr/testify/assert"
25+
"go.elastic.co/apm/v2"
26+
"go.elastic.co/fastjson"
27+
)
28+
29+
func TestCreatePartialTransactionJSON(t *testing.T) {
30+
var w fastjson.Writer
31+
tx := apm.DefaultTracer().StartTransaction("test", "function")
32+
defer tx.End()
33+
assert.NoError(t, createPartialTransactionJSON(tx, &w))
34+
assert.True(t, json.Valid(w.Bytes()))
35+
assert.Equal(t, "test", string(w.Bytes()))
36+
}

0 commit comments

Comments
 (0)