Skip to content

Commit

Permalink
checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
swi-jared committed Jun 11, 2024
1 parent 3b5712d commit ea5573d
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ import (
"context"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-lambda-go/lambdacontext"
"github.com/solarwinds/apm-go/internal/config"
"github.com/solarwinds/apm-go/internal/log"
"github.com/solarwinds/apm-go/swo"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
"go.opentelemetry.io/otel/trace"
"os"
"strings"
"sync"
)

Expand All @@ -33,7 +35,9 @@ var tracer trace.Tracer
var once sync.Once

type wrappedHandler struct {
base lambda.Handler
base lambda.Handler
fnName string
txnName string
}

var _ lambda.Handler = &wrappedHandler{}
Expand All @@ -45,8 +49,8 @@ func (w *wrappedHandler) Invoke(ctx context.Context, payload []byte) ([]byte, er
} else if lc != nil {
attrs = append(attrs, semconv.FaaSInvocationID(lc.AwsRequestID))
}
name := os.Getenv("AWS_LAMBDA_FUNCTION_NAME")
ctx, span := tracer.Start(ctx, name, trace.WithSpanKind(trace.SpanKindServer), trace.WithAttributes(attrs...))
attrs = append(attrs, attribute.String("sw.transaction", w.txnName))
ctx, span := tracer.Start(ctx, w.fnName, trace.WithSpanKind(trace.SpanKindServer), trace.WithAttributes(attrs...))
defer func() {
span.End()
if flusher != nil {
Expand All @@ -66,7 +70,14 @@ func WrapHandler(f interface{}) lambda.Handler {
}
tracer = otel.GetTracerProvider().Tracer("swolambda")
})
fnName := os.Getenv("AWS_LAMBDA_FUNCTION_NAME")
txnName := strings.TrimSpace(config.GetTransactionName())
if txnName == "" {
txnName = fnName
}
return &wrappedHandler{
base: lambda.NewHandler(f),
base: lambda.NewHandler(f),
fnName: fnName,
txnName: txnName,
}
}
9 changes: 5 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package config is responsible for loading the configuration from various
// sources, e.g., environment variables, configuration files and user input.
// It also accepts dynamic settings from the collector server.
Expand Down Expand Up @@ -340,8 +341,8 @@ const (
may be different from your setting.`
)

// hasLambdaEnv checks if the AWS Lambda env var is set.
func hasLambdaEnv() bool {
// HasLambdaEnv checks if the AWS Lambda env var is set.
func HasLambdaEnv() bool {
return os.Getenv("AWS_LAMBDA_FUNCTION_NAME") != "" && os.Getenv("LAMBDA_TASK_ROOT") != ""
}

Expand All @@ -362,12 +363,12 @@ func (c *Config) validate() error {
c.Ec2MetadataTimeout = t
}

if c.TransactionName != "" && !hasLambdaEnv() {
if c.TransactionName != "" && !HasLambdaEnv() {
log.Info(InvalidEnv("TransactionName", c.TransactionName))
c.TransactionName = getFieldDefaultValue(c, "TransactionName")
}

if !hasLambdaEnv() {
if !HasLambdaEnv() {
if c.ServiceKey != "" {
c.ServiceKey = ToServiceKey(c.ServiceKey)
if ok := IsValidServiceKey(c.ServiceKey); !ok {
Expand Down
58 changes: 48 additions & 10 deletions internal/entryspans/entryspans.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,62 @@ package entryspans
import (
"fmt"
"github.com/pkg/errors"
"github.com/solarwinds/apm-go/internal/config"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
"sync"
)

var (
state = &entrySpans{
spans: make(map[trace.TraceID][]*entrySpan),
}
state = makeManagerFromEnv()

NotEntrySpan = errors.New("span is not an entry span")
NotEntrySpan = errors.New("span is not an entry span")
CannotSetTransaction = errors.New("cannot set transaction, likely due to lambda enviroment")

nullSpanID = trace.SpanID{}
nullEntrySpan = &entrySpan{spanId: nullSpanID}
)

type manager interface {
push(tid trace.TraceID, sid trace.SpanID)
delete(tid trace.TraceID, sid trace.SpanID) error
current(tid trace.TraceID) (*entrySpan, bool)
setTransactionName(tid trace.TraceID, name string) error
}

type entrySpan struct {
spanId trace.SpanID
txnName string
}

type entrySpans struct {
type stdManager struct {
mut sync.RWMutex

spans map[trace.TraceID][]*entrySpan
}

func (e *entrySpans) push(tid trace.TraceID, sid trace.SpanID) {
type noopManager struct{}

func (n noopManager) push(trace.TraceID, trace.SpanID) {}

func (n noopManager) delete(trace.TraceID, trace.SpanID) error {
return nil
}

func (n noopManager) current(trace.TraceID) (*entrySpan, bool) {
return nil, false
}

func (n noopManager) setTransactionName(trace.TraceID, string) error {
return CannotSetTransaction
}

var (
_ manager = &stdManager{}
_ manager = &noopManager{}
)

func (e *stdManager) push(tid trace.TraceID, sid trace.SpanID) {
e.mut.Lock()
defer e.mut.Unlock()
var list []*entrySpan
Expand All @@ -56,14 +84,14 @@ func (e *entrySpans) push(tid trace.TraceID, sid trace.SpanID) {
e.spans[tid] = list
}

func (e *entrySpans) current(tid trace.TraceID) (*entrySpan, bool) {
func (e *stdManager) current(tid trace.TraceID) (*entrySpan, bool) {
e.mut.Lock()
defer e.mut.Unlock()
a, ok := e.currentUnsafe(tid)
return a, ok
}

func (e *entrySpans) currentUnsafe(tid trace.TraceID) (*entrySpan, bool) {
func (e *stdManager) currentUnsafe(tid trace.TraceID) (*entrySpan, bool) {
if list, ok := e.spans[tid]; ok {
l := len(list)
if len(list) == 0 {
Expand All @@ -85,7 +113,7 @@ func Push(span sdktrace.ReadOnlySpan) error {
return nil
}

func (e *entrySpans) delete(tid trace.TraceID, sid trace.SpanID) error {
func (e *stdManager) delete(tid trace.TraceID, sid trace.SpanID) error {
e.mut.Lock()
defer e.mut.Unlock()

Expand Down Expand Up @@ -125,7 +153,7 @@ func Current(tid trace.TraceID) (trace.SpanID, bool) {
return curr.spanId, ok
}

func (e *entrySpans) setTransactionName(tid trace.TraceID, name string) error {
func (e *stdManager) setTransactionName(tid trace.TraceID, name string) error {
e.mut.Lock()
defer e.mut.Unlock()

Expand All @@ -152,3 +180,13 @@ func IsEntrySpan(span sdktrace.ReadOnlySpan) bool {
parent := span.Parent()
return !parent.IsValid() || parent.IsRemote()
}

func makeManagerFromEnv() manager {
if config.HasLambdaEnv() {
return &noopManager{}
} else {
return &stdManager{
spans: make(map[trace.TraceID][]*entrySpan),
}
}
}
10 changes: 7 additions & 3 deletions internal/entryspans/entryspans_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var (
span4 = trace.SpanID{0x4}
)

func (e *entrySpans) pop(tid trace.TraceID) (trace.SpanID, bool) {
func (e *stdManager) pop(tid trace.TraceID) (trace.SpanID, bool) {
e.mut.Lock()
defer e.mut.Unlock()

Expand All @@ -57,6 +57,7 @@ func (e *entrySpans) pop(tid trace.TraceID) (trace.SpanID, bool) {
}

func TestCurrent(t *testing.T) {
state := state.(*stdManager)
sid, ok := Current(traceA)
require.False(t, ok)
require.False(t, sid.IsValid())
Expand Down Expand Up @@ -109,6 +110,7 @@ func TestCurrent(t *testing.T) {
}

func TestPush(t *testing.T) {
state := state.(*stdManager)
var err error
tr, teardown := testutils.TracerSetup()
defer teardown()
Expand All @@ -133,7 +135,8 @@ func TestPush(t *testing.T) {

func TestSetTransactionName(t *testing.T) {
// reset state
state = &entrySpans{spans: make(map[trace.TraceID][]*entrySpan)}
state = &stdManager{spans: make(map[trace.TraceID][]*entrySpan)}
state := state.(*stdManager)

err := SetTransactionName(traceA, "foo bar")
require.Error(t, err)
Expand Down Expand Up @@ -174,7 +177,8 @@ func TestSetTransactionName(t *testing.T) {

func TestDelete(t *testing.T) {
// reset state
state = &entrySpans{spans: make(map[trace.TraceID][]*entrySpan)}
state = &stdManager{spans: make(map[trace.TraceID][]*entrySpan)}
state := state.(*stdManager)

err := state.delete(traceA, span1)
require.Error(t, err)
Expand Down
4 changes: 2 additions & 2 deletions internal/exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"github.com/solarwinds/apm-go/internal/log"
"github.com/solarwinds/apm-go/internal/reporter"
"github.com/solarwinds/apm-go/internal/swotel/semconv"
"github.com/solarwinds/apm-go/internal/utils"
"github.com/solarwinds/apm-go/internal/txn"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
Expand All @@ -45,7 +45,7 @@ func (e *exporter) exportSpan(_ context.Context, s sdktrace.ReadOnlySpan) {
attribute.String("otel.scope.version", s.InstrumentationScope().Version),
})
if entryspans.IsEntrySpan(s) {
evt.AddKV(attribute.String("TransactionName", utils.GetTransactionName(s)))
evt.AddKV(attribute.String("TransactionName", txn.GetTransactionName(s)))
// We MUST clear the entry span here. The SpanProcessor only clears entry spans when they are `RecordOnly`
if err := entryspans.Delete(s); err != nil {
log.Warningf(
Expand Down
9 changes: 5 additions & 4 deletions internal/metrics/otel.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ package metrics
import (
"context"
"github.com/solarwinds/apm-go/internal/log"
"github.com/solarwinds/apm-go/internal/utils"
"github.com/solarwinds/apm-go/internal/txn"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/metric"
Expand All @@ -28,14 +29,13 @@ import (
)

type otelRegistry struct {
meterProvider metric.MeterProvider
}

func (o *otelRegistry) RecordSpan(span sdktrace.ReadOnlySpan, isAppoptics bool) {
// TODO DRY with legacy registry?
var attrs = []attribute.KeyValue{
attribute.Bool("sw.is_error", span.Status().Code == codes.Error),
attribute.String("sw.transaction", utils.GetTransactionName(span)),
attribute.String("sw.transaction", txn.GetTransactionName(span)),
}
for _, attr := range span.Attributes() {
// TODO use semconv?
Expand All @@ -48,7 +48,8 @@ func (o *otelRegistry) RecordSpan(span sdktrace.ReadOnlySpan, isAppoptics bool)
}
}
// TODO service.name?
meter := o.meterProvider.Meter("sw.apm.request.metrics")

meter := otel.GetMeterProvider().Meter("sw.apm.request.metrics")
histo, err := meter.Int64Histogram(
"trace.service.response_time",
metric.WithExplicitBucketBoundaries(),
Expand Down
4 changes: 2 additions & 2 deletions internal/metrics/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"github.com/solarwinds/apm-go/internal/bson"
"github.com/solarwinds/apm-go/internal/log"
"github.com/solarwinds/apm-go/internal/swotel/semconv"
"github.com/solarwinds/apm-go/internal/utils"
"github.com/solarwinds/apm-go/internal/txn"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/sdk/trace"
trace2 "go.opentelemetry.io/otel/trace"
Expand Down Expand Up @@ -192,7 +192,7 @@ func (r *registry) RecordSpan(span trace.ReadOnlySpan, isAppoptics bool) {
}

swoTags["sw.is_error"] = strconv.FormatBool(isError)
txnName := utils.GetTransactionName(span)
txnName := txn.GetTransactionName(span)
swoTags["sw.transaction"] = txnName

duration := span.EndTime().Sub(span.StartTime())
Expand Down
14 changes: 10 additions & 4 deletions internal/utils/otel.go → internal/txn/txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package utils
package txn

import (
"github.com/solarwinds/apm-go/internal/config"
"github.com/solarwinds/apm-go/internal/entryspans"
"github.com/solarwinds/apm-go/internal/swotel/semconv"
"go.opentelemetry.io/otel/attribute"
Expand All @@ -34,8 +35,13 @@ func GetTransactionName(span sdktrace.ReadOnlySpan) string {
}

// deriveTransactionName returns transaction name from given span name and attributes, falling back to "unknown"
func deriveTransactionName(name string, attrs []attribute.KeyValue) string {
var httpRoute, httpUrl, txnName = "", "", ""
func deriveTransactionName(name string, attrs []attribute.KeyValue) (txnName string) {
// TODO: add test
if txnName = config.GetTransactionName(); txnName != "" {
return
}

var httpRoute, httpUrl = "", ""
for _, attr := range attrs {
if attr.Key == semconv.HTTPRouteKey {
httpRoute = attr.Value.AsString()
Expand Down Expand Up @@ -69,5 +75,5 @@ func deriveTransactionName(name string, attrs []attribute.KeyValue) string {
if len(txnName) > 255 {
txnName = txnName[:255]
}
return txnName
return
}
2 changes: 1 addition & 1 deletion internal/utils/otel_test.go → internal/txn/txn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package utils
package txn

import (
"context"
Expand Down
1 change: 0 additions & 1 deletion swo/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ func StartLambda(lambdaLogStreamName string) (Flusher, error) {
} else {
tpOpts = append(tpOpts, sdktrace.WithSyncer(exprtr))
}

proc := processor.NewInboundMetricsSpanProcessor(registry, false)
prop := propagation.NewCompositeTextMapPropagator(
&propagation.TraceContext{},
Expand Down

0 comments on commit ea5573d

Please sign in to comment.