input/elasticapm/internal/modeldecoder/v2/decoder.go (1,689 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, 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 v2 import ( "encoding/json" "fmt" "io" "net/http" "net/textproto" "regexp" "strconv" "strings" "time" "github.com/elastic/apm-data/input/elasticapm/internal/decoder" "github.com/elastic/apm-data/input/elasticapm/internal/modeldecoder" "github.com/elastic/apm-data/input/elasticapm/internal/modeldecoder/modeldecoderutil" "github.com/elastic/apm-data/input/elasticapm/internal/modeldecoder/netutil" "github.com/elastic/apm-data/input/elasticapm/internal/modeldecoder/nullable" "github.com/elastic/apm-data/input/otlp" "github.com/elastic/apm-data/model/modelpb" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" ) var compressionStrategyText = map[string]modelpb.CompressionStrategy{ "exact_match": modelpb.CompressionStrategy_COMPRESSION_STRATEGY_EXACT_MATCH, "same_kind": modelpb.CompressionStrategy_COMPRESSION_STRATEGY_SAME_KIND, } var metricTypeText = map[string]modelpb.MetricType{ "gauge": modelpb.MetricType_METRIC_TYPE_GAUGE, "counter": modelpb.MetricType_METRIC_TYPE_COUNTER, "histogram": modelpb.MetricType_METRIC_TYPE_HISTOGRAM, "summary": modelpb.MetricType_METRIC_TYPE_SUMMARY, } var ( // reForServiceTargetExpr regex will capture service target type and name // Service target type comprises of only lowercase alphabets // Service target name comprises of all word characters reForServiceTargetExpr = regexp.MustCompile(`^([a-z0-9]+)(?:/(\w+))?$`) ) // DecodeMetadata decodes metadata from d, updating out. // // DecodeMetadata should be used when the the stream in the decoder does not contain the // `metadata` key, but only the metadata data. func DecodeMetadata(d decoder.Decoder, out *modelpb.APMEvent) error { return decodeMetadata(decodeIntoMetadata, d, out) } // DecodeNestedMetadata decodes metadata from d, updating out. // // DecodeNestedMetadata should be used when the stream in the decoder contains the `metadata` key func DecodeNestedMetadata(d decoder.Decoder, out *modelpb.APMEvent) error { return decodeMetadata(decodeIntoMetadataRoot, d, out) } // DecodeNestedError decodes an error from d, appending it to batch. // // DecodeNestedError should be used when the stream in the decoder contains the `error` key func DecodeNestedError(d decoder.Decoder, input *modeldecoder.Input, batch *modelpb.Batch) error { root := &errorRoot{} err := d.Decode(root) if err != nil && err != io.EOF { return modeldecoder.NewDecoderErrFromJSONIter(err) } if err := root.validate(); err != nil { return modeldecoder.NewValidationErr(err) } event := input.Base.CloneVT() mapToErrorModel(&root.Error, event) *batch = append(*batch, event) return err } // DecodeNestedMetricset decodes a metricset from d, appending it to batch. // // DecodeNestedMetricset should be used when the stream in the decoder contains the `metricset` key func DecodeNestedMetricset(d decoder.Decoder, input *modeldecoder.Input, batch *modelpb.Batch) error { root := &metricsetRoot{} var err error if err = d.Decode(root); err != nil && err != io.EOF { return modeldecoder.NewDecoderErrFromJSONIter(err) } if err := root.validate(); err != nil { return modeldecoder.NewValidationErr(err) } event := input.Base.CloneVT() if mapToMetricsetModel(&root.Metricset, event) { *batch = append(*batch, event) } return err } // DecodeNestedSpan decodes a span from d, appending it to batch. // // DecodeNestedSpan should be used when the stream in the decoder contains the `span` key func DecodeNestedSpan(d decoder.Decoder, input *modeldecoder.Input, batch *modelpb.Batch) error { root := &spanRoot{} var err error if err = d.Decode(root); err != nil && err != io.EOF { return modeldecoder.NewDecoderErrFromJSONIter(err) } if err := root.validate(); err != nil { return modeldecoder.NewValidationErr(err) } event := input.Base.CloneVT() mapToSpanModel(&root.Span, event) *batch = append(*batch, event) return err } // DecodeNestedTransaction decodes a transaction from d, appending it to batch. // // DecodeNestedTransaction should be used when the stream in the decoder contains the `transaction` key func DecodeNestedTransaction(d decoder.Decoder, input *modeldecoder.Input, batch *modelpb.Batch) error { root := &transactionRoot{} var err error if err = d.Decode(root); err != nil && err != io.EOF { return modeldecoder.NewDecoderErrFromJSONIter(err) } if err := root.validate(); err != nil { return modeldecoder.NewValidationErr(err) } event := input.Base.CloneVT() mapToTransactionModel(&root.Transaction, event) *batch = append(*batch, event) return err } // DecodeNestedLog decodes a log event from d, appending it to batch. // // DecodeNestedLog should be used when the stream in the decoder contains the `log` key func DecodeNestedLog(d decoder.Decoder, input *modeldecoder.Input, batch *modelpb.Batch) error { root := &logRoot{} var err error if err = d.Decode(root); err != nil && err != io.EOF { return modeldecoder.NewDecoderErrFromJSONIter(err) } // Flatten any nested source to set the values for the flat fields if err := root.processNestedSource(); err != nil { return err } if err := root.validate(); err != nil { return modeldecoder.NewValidationErr(err) } event := input.Base.CloneVT() mapToLogModel(&root.Log, event) *batch = append(*batch, event) return err } func decodeMetadata(decFn func(d decoder.Decoder, m *metadataRoot) error, d decoder.Decoder, out *modelpb.APMEvent) error { m := &metadataRoot{} var err error if err = decFn(d, m); err != nil && err != io.EOF { return modeldecoder.NewDecoderErrFromJSONIter(err) } if err := m.validate(); err != nil { return modeldecoder.NewValidationErr(err) } mapToMetadataModel(&m.Metadata, out) return err } func decodeIntoMetadata(d decoder.Decoder, m *metadataRoot) error { return d.Decode(&m.Metadata) } func decodeIntoMetadataRoot(d decoder.Decoder, m *metadataRoot) error { return d.Decode(m) } func mapToFAASModel(from faas, faas *modelpb.Faas) { if from.ID.IsSet() { faas.Id = from.ID.Val } if from.Coldstart.IsSet() { valCopy := from.Coldstart faas.ColdStart = &valCopy.Val } if from.Execution.IsSet() { faas.Execution = from.Execution.Val } if from.Trigger.Type.IsSet() { faas.TriggerType = from.Trigger.Type.Val } if from.Trigger.RequestID.IsSet() { faas.TriggerRequestId = from.Trigger.RequestID.Val } if from.Name.IsSet() { faas.Name = from.Name.Val } if from.Version.IsSet() { faas.Version = from.Version.Val } } func mapToDroppedSpansModel(from []transactionDroppedSpanStats, tx *modelpb.Transaction) { for _, f := range from { to := modelpb.DroppedSpanStats{} if f.DestinationServiceResource.IsSet() { to.DestinationServiceResource = f.DestinationServiceResource.Val } if f.Outcome.IsSet() { to.Outcome = f.Outcome.Val } if f.Duration.IsSet() { to.Duration = &modelpb.AggregatedDuration{} to.Duration.Count = uint64(f.Duration.Count.Val) sum := f.Duration.Sum if sum.IsSet() { to.Duration.Sum = uint64(time.Duration(sum.Us.Val) * time.Microsecond) } } if f.ServiceTargetType.IsSet() { to.ServiceTargetType = f.ServiceTargetType.Val } if f.ServiceTargetName.IsSet() { to.ServiceTargetName = f.ServiceTargetName.Val } tx.DroppedSpansStats = append(tx.DroppedSpansStats, &to) } } func mapToCloudModel(from contextCloud, cloud *modelpb.Cloud) { cloudOrigin := modelpb.CloudOrigin{} if from.Origin.Account.ID.IsSet() { cloudOrigin.AccountId = from.Origin.Account.ID.Val } if from.Origin.Provider.IsSet() { cloudOrigin.Provider = from.Origin.Provider.Val } if from.Origin.Region.IsSet() { cloudOrigin.Region = from.Origin.Region.Val } if from.Origin.Service.Name.IsSet() { cloudOrigin.ServiceName = from.Origin.Service.Name.Val } cloud.Origin = &cloudOrigin } func mapToClientModel(from contextRequest, source **modelpb.Source, client **modelpb.Client) { // http.Request.Headers and http.Request.Socket are only set for backend events. if (*source).GetIp() == nil { ip, port := netutil.SplitAddrPort(from.Socket.RemoteAddress.Val) if ip.IsValid() { if *source == nil { *source = &modelpb.Source{} } (*source).Ip, (*source).Port = modelpb.Addr2IP(ip), uint32(port) } } if (*client).GetIp() == nil { if (*source).GetIp() != nil { if *client == nil { *client = &modelpb.Client{} } (*client).Ip = (*source).Ip.CloneVT() } if addr, port := netutil.ClientAddrFromHeaders(from.Headers.Val); addr.IsValid() { if (*source).GetIp() != nil { (*source).Nat = &modelpb.NAT{} (*source).Nat.Ip = (*source).Ip.CloneVT() } if *client == nil { *client = &modelpb.Client{} } clientIp := modelpb.Addr2IP(addr) sourceIp := clientIp.CloneVT() (*client).Ip, (*client).Port = clientIp, uint32(port) if *source == nil { *source = &modelpb.Source{} } (*source).Ip, (*source).Port = sourceIp, uint32(port) } } } func mapToErrorModel(from *errorEvent, event *modelpb.APMEvent) { out := modelpb.Error{} event.Error = &out // overwrite metadata with event specific information mapToServiceModel(from.Context.Service, &event.Service) mapToAgentModel(from.Context.Service.Agent, &event.Agent) overwriteUserInMetadataModel(from.Context.User, event) mapToUserAgentModel(from.Context.Request.Headers, &event.UserAgent) mapToClientModel(from.Context.Request, &event.Source, &event.Client) // map errorEvent specific data if from.Context.IsSet() { if len(from.Context.Tags) > 0 { modeldecoderutil.MergeLabels(from.Context.Tags, event) } if from.Context.Request.IsSet() { if event.Http == nil { event.Http = &modelpb.HTTP{} } event.Http.Request = &modelpb.HTTPRequest{} mapToRequestModel(from.Context.Request, event.Http.Request) if from.Context.Request.HTTPVersion.IsSet() { event.Http.Version = from.Context.Request.HTTPVersion.Val } } if from.Context.Response.IsSet() { if event.Http == nil { event.Http = &modelpb.HTTP{} } event.Http.Response = &modelpb.HTTPResponse{} mapToResponseModel(from.Context.Response, event.Http.Response) } if from.Context.Request.URL.IsSet() { if event.Url == nil { event.Url = &modelpb.URL{} } mapToRequestURLModel(from.Context.Request.URL, event.Url) } if from.Context.Page.IsSet() { if from.Context.Page.URL.IsSet() && !from.Context.Request.URL.IsSet() { event.Url = modelpb.ParseURL(from.Context.Page.URL.Val, "", "") } if from.Context.Page.Referer.IsSet() { if event.Http == nil { event.Http = &modelpb.HTTP{} } if event.Http.Request == nil { event.Http.Request = &modelpb.HTTPRequest{} } if event.Http.Request.Referrer == "" { event.Http.Request.Referrer = from.Context.Page.Referer.Val } } } if len(from.Context.Custom) > 0 { out.Custom = modeldecoderutil.ToKv(from.Context.Custom, out.Custom) } } if from.Culprit.IsSet() { out.Culprit = from.Culprit.Val } if from.Exception.IsSet() { out.Exception = &modelpb.Exception{} mapToExceptionModel(&from.Exception, out.Exception) } if from.ID.IsSet() { out.Id = from.ID.Val } if from.Log.IsSet() { log := modelpb.ErrorLog{Level: "error"} if from.Log.Level.IsSet() { log.Level = from.Log.Level.Val } if from.Log.LoggerName.IsSet() { log.LoggerName = from.Log.LoggerName.Val } if from.Log.Message.IsSet() { log.Message = from.Log.Message.Val } if from.Log.ParamMessage.IsSet() { log.ParamMessage = from.Log.ParamMessage.Val } if len(from.Log.Stacktrace) > 0 { log.Stacktrace = modeldecoderutil.ResliceAndPopulateNil( log.Stacktrace, len(from.Log.Stacktrace), modeldecoderutil.NewType[modelpb.StacktraceFrame], ) mapToStracktraceModel(from.Log.Stacktrace, log.Stacktrace) } out.Log = &log } if from.ParentID.IsSet() { event.ParentId = from.ParentID.Val } if !from.Timestamp.Val.IsZero() { event.Timestamp = modelpb.FromTime(from.Timestamp.Val) } if from.TraceID.IsSet() { event.Trace = &modelpb.Trace{} event.Trace.Id = from.TraceID.Val } if from.TransactionID.IsSet() || from.Transaction.IsSet() { event.Transaction = &modelpb.Transaction{} } if from.TransactionID.IsSet() { event.Transaction.Id = from.TransactionID.Val event.Span = &modelpb.Span{} event.Span.Id = from.TransactionID.Val } if from.Transaction.IsSet() { if from.Transaction.Sampled.IsSet() { event.Transaction.Sampled = from.Transaction.Sampled.Val } if from.Transaction.Name.IsSet() { event.Transaction.Name = from.Transaction.Name.Val } if from.Transaction.Type.IsSet() { event.Transaction.Type = from.Transaction.Type.Val } } } func mapToExceptionModel(from *errorException, out *modelpb.Exception) { if len(from.Attributes) > 0 { out.Attributes = modeldecoderutil.ToKv(from.Attributes, out.Attributes) } if from.Code.IsSet() { out.Code = modeldecoderutil.ExceptionCodeString(from.Code.Val) } if len(from.Cause) > 0 { out.Cause = modeldecoderutil.ResliceAndPopulateNil( out.Cause, len(from.Cause), modeldecoderutil.NewType[modelpb.Exception], ) for i := 0; i < len(from.Cause); i++ { mapToExceptionModel(&from.Cause[i], out.Cause[i]) } } if from.Handled.IsSet() { handled := from.Handled.Val out.Handled = &handled } if from.Message.IsSet() { out.Message = from.Message.Val } if from.Module.IsSet() { out.Module = from.Module.Val } if len(from.Stacktrace) > 0 { out.Stacktrace = modeldecoderutil.ResliceAndPopulateNil( out.Stacktrace, len(from.Stacktrace), modeldecoderutil.NewType[modelpb.StacktraceFrame], ) mapToStracktraceModel(from.Stacktrace, out.Stacktrace) } if from.Type.IsSet() { out.Type = from.Type.Val } } func mapToMetadataModel(from *metadata, out *modelpb.APMEvent) { // Cloud if from.Cloud.IsSet() { if out.Cloud == nil { out.Cloud = &modelpb.Cloud{} } if from.Cloud.Account.ID.IsSet() { out.Cloud.AccountId = from.Cloud.Account.ID.Val } if from.Cloud.Account.Name.IsSet() { out.Cloud.AccountName = from.Cloud.Account.Name.Val } if from.Cloud.AvailabilityZone.IsSet() { out.Cloud.AvailabilityZone = from.Cloud.AvailabilityZone.Val } if from.Cloud.Instance.ID.IsSet() { out.Cloud.InstanceId = from.Cloud.Instance.ID.Val } if from.Cloud.Instance.Name.IsSet() { out.Cloud.InstanceName = from.Cloud.Instance.Name.Val } if from.Cloud.Machine.Type.IsSet() { out.Cloud.MachineType = from.Cloud.Machine.Type.Val } if from.Cloud.Project.ID.IsSet() { out.Cloud.ProjectId = from.Cloud.Project.ID.Val } if from.Cloud.Project.Name.IsSet() { out.Cloud.ProjectName = from.Cloud.Project.Name.Val } if from.Cloud.Provider.IsSet() { out.Cloud.Provider = from.Cloud.Provider.Val } if from.Cloud.Region.IsSet() { out.Cloud.Region = from.Cloud.Region.Val } if from.Cloud.Service.Name.IsSet() { out.Cloud.ServiceName = from.Cloud.Service.Name.Val } } // Labels if len(from.Labels) > 0 { modeldecoderutil.GlobalLabelsFrom(from.Labels, out) } // Process if len(from.Process.Argv) > 0 { if out.Process == nil { out.Process = &modelpb.Process{} } out.Process.Argv = append(out.Process.Argv[:0], from.Process.Argv...) } if from.Process.Pid.IsSet() { if out.Process == nil { out.Process = &modelpb.Process{} } out.Process.Pid = uint32(from.Process.Pid.Val) } if from.Process.Ppid.IsSet() { var pid = uint32(from.Process.Ppid.Val) if out.Process == nil { out.Process = &modelpb.Process{} } out.Process.Ppid = pid } if from.Process.Title.IsSet() { if out.Process == nil { out.Process = &modelpb.Process{} } out.Process.Title = from.Process.Title.Val } // Service if from.Service.Agent.IsSet() { if out.Agent == nil { out.Agent = &modelpb.Agent{} } if from.Service.Agent.ActivationMethod.IsSet() { out.Agent.ActivationMethod = from.Service.Agent.ActivationMethod.Val } if from.Service.Agent.EphemeralID.IsSet() { out.Agent.EphemeralId = from.Service.Agent.EphemeralID.Val } if from.Service.Agent.Name.IsSet() { out.Agent.Name = from.Service.Agent.Name.Val } if from.Service.Agent.Version.IsSet() { out.Agent.Version = from.Service.Agent.Version.Val } } if from.Service.IsSet() { if out.Service == nil { out.Service = &modelpb.Service{} } if from.Service.Environment.IsSet() { out.Service.Environment = from.Service.Environment.Val } if from.Service.Framework.IsSet() { if out.Service.Framework == nil { out.Service.Framework = &modelpb.Framework{} } if from.Service.Framework.Name.IsSet() { out.Service.Framework.Name = from.Service.Framework.Name.Val } if from.Service.Framework.Version.IsSet() { out.Service.Framework.Version = from.Service.Framework.Version.Val } } if from.Service.Language.IsSet() { if out.Service.Language == nil { out.Service.Language = &modelpb.Language{} } if from.Service.Language.Name.IsSet() { out.Service.Language.Name = from.Service.Language.Name.Val } if from.Service.Language.Version.IsSet() { out.Service.Language.Version = from.Service.Language.Version.Val } } if from.Service.Name.IsSet() { out.Service.Name = from.Service.Name.Val } if from.Service.Node.Name.IsSet() { if out.Service.Node == nil { out.Service.Node = &modelpb.ServiceNode{} } out.Service.Node.Name = from.Service.Node.Name.Val } if from.Service.Runtime.IsSet() { if out.Service.Runtime == nil { out.Service.Runtime = &modelpb.Runtime{} } if from.Service.Runtime.Name.IsSet() { out.Service.Runtime.Name = from.Service.Runtime.Name.Val } if from.Service.Runtime.Version.IsSet() { out.Service.Runtime.Version = from.Service.Runtime.Version.Val } } if from.Service.Version.IsSet() { out.Service.Version = from.Service.Version.Val } } // System if from.System.Architecture.IsSet() { if out.Host == nil { out.Host = &modelpb.Host{} } out.Host.Architecture = from.System.Architecture.Val } if from.System.ConfiguredHostname.IsSet() { if out.Host == nil { out.Host = &modelpb.Host{} } out.Host.Name = from.System.ConfiguredHostname.Val } if from.System.Container.ID.IsSet() { if out.Container == nil { out.Container = &modelpb.Container{} } out.Container.Id = from.System.Container.ID.Val } if from.System.DetectedHostname.IsSet() { if out.Host == nil { out.Host = &modelpb.Host{} } out.Host.Hostname = from.System.DetectedHostname.Val } if from.System.HostID.IsSet() { if out.Host == nil { out.Host = &modelpb.Host{} } out.Host.Id = from.System.HostID.Val } if !from.System.ConfiguredHostname.IsSet() && !from.System.DetectedHostname.IsSet() && from.System.DeprecatedHostname.IsSet() { if out.Host == nil { out.Host = &modelpb.Host{} } out.Host.Hostname = from.System.DeprecatedHostname.Val } if from.System.Kubernetes.Namespace.IsSet() { if out.Kubernetes == nil { out.Kubernetes = &modelpb.Kubernetes{} } out.Kubernetes.Namespace = from.System.Kubernetes.Namespace.Val } if from.System.Kubernetes.Node.Name.IsSet() { if out.Kubernetes == nil { out.Kubernetes = &modelpb.Kubernetes{} } out.Kubernetes.NodeName = from.System.Kubernetes.Node.Name.Val } if from.System.Kubernetes.Pod.Name.IsSet() { if out.Kubernetes == nil { out.Kubernetes = &modelpb.Kubernetes{} } out.Kubernetes.PodName = from.System.Kubernetes.Pod.Name.Val } if from.System.Kubernetes.Pod.UID.IsSet() { if out.Kubernetes == nil { out.Kubernetes = &modelpb.Kubernetes{} } out.Kubernetes.PodUid = from.System.Kubernetes.Pod.UID.Val } if from.System.Platform.IsSet() { if out.Host == nil { out.Host = &modelpb.Host{} } if out.Host.Os == nil { out.Host.Os = &modelpb.OS{} } out.Host.Os.Platform = from.System.Platform.Val } // User if from.User.Domain.IsSet() { if out.User == nil { out.User = &modelpb.User{} } out.User.Domain = from.User.Domain.Val } if from.User.ID.IsSet() { if out.User == nil { out.User = &modelpb.User{} } out.User.Id = fmt.Sprint(from.User.ID.Val) } if from.User.Email.IsSet() { if out.User == nil { out.User = &modelpb.User{} } out.User.Email = from.User.Email.Val } if from.User.Name.IsSet() { if out.User == nil { out.User = &modelpb.User{} } out.User.Name = from.User.Name.Val } // Network if from.Network.Connection.Type.IsSet() { if out.Network == nil { out.Network = &modelpb.Network{} } if out.Network.Connection == nil { out.Network.Connection = &modelpb.NetworkConnection{} } out.Network.Connection.Type = from.Network.Connection.Type.Val } } func mapToMetricsetModel(from *metricset, event *modelpb.APMEvent) bool { event.Metricset = &modelpb.Metricset{} event.Metricset.Name = "app" if !from.Timestamp.Val.IsZero() { event.Timestamp = modelpb.FromTime(from.Timestamp.Val) } if len(from.Samples) > 0 { event.Metricset.Samples = modeldecoderutil.ResliceAndPopulateNil( event.Metricset.Samples, len(from.Samples), modeldecoderutil.NewType[modelpb.MetricsetSample], ) i := 0 for name, sample := range from.Samples { ms := event.Metricset.Samples[i] if len(sample.Counts) != 0 || len(sample.Values) != 0 { ms.Histogram = &modelpb.Histogram{} if n := len(sample.Values); n > 0 { ms.Histogram.Values = modeldecoderutil.Reslice(ms.Histogram.Values, n) copy(ms.Histogram.Values, sample.Values) } if n := len(sample.Counts); n > 0 { ms.Histogram.Counts = modeldecoderutil.Reslice(ms.Histogram.Counts, n) copy(ms.Histogram.Counts, sample.Counts) } } ms.Type = metricTypeText[sample.Type.Val] ms.Name = name ms.Unit = sample.Unit.Val ms.Value = sample.Value.Val i++ } } if len(from.Tags) > 0 { modeldecoderutil.MergeLabels(from.Tags, event) } if from.Span.IsSet() { event.Span = &modelpb.Span{} if from.Span.Subtype.IsSet() { event.Span.Subtype = from.Span.Subtype.Val } if from.Span.Type.IsSet() { event.Span.Type = from.Span.Type.Val } } ok := true if from.Transaction.IsSet() { event.Transaction = &modelpb.Transaction{} if from.Transaction.Name.IsSet() { event.Transaction.Name = from.Transaction.Name.Val } if from.Transaction.Type.IsSet() { event.Transaction.Type = from.Transaction.Type.Val } // Transaction fields specified: this is an APM-internal metricset. // If there are no known metric samples, we return false so the // metricset is not added to the batch. ok = modeldecoderutil.SetInternalMetrics(event) } if from.Service.Name.IsSet() { if event.Service == nil { event.Service = &modelpb.Service{} } event.Service.Name = from.Service.Name.Val event.Service.Version = from.Service.Version.Val } if from.FAAS.IsSet() { if event.Faas == nil { event.Faas = &modelpb.Faas{} } mapToFAASModel(from.FAAS, event.Faas) } return ok } func mapToRequestModel(from contextRequest, out *modelpb.HTTPRequest) { if from.Method.IsSet() { out.Method = from.Method.Val } if len(from.Env) > 0 { out.Env = modeldecoderutil.ToKv(from.Env, out.Env) } if from.Body.IsSet() { out.Body = modeldecoderutil.ToValue(modeldecoderutil.NormalizeHTTPRequestBody(from.Body.Val)) } if len(from.Cookies) > 0 { out.Cookies = modeldecoderutil.ToKv(from.Cookies, out.Cookies) } if from.Headers.IsSet() { out.Headers = modeldecoderutil.HTTPHeadersToModelpb(from.Headers.Val, out.Headers) } } func mapToRequestURLModel(from contextRequestURL, out *modelpb.URL) { if from.Raw.IsSet() { out.Original = from.Raw.Val } if from.Full.IsSet() { out.Full = from.Full.Val } if from.Hostname.IsSet() { out.Domain = from.Hostname.Val } if from.Path.IsSet() { out.Path = from.Path.Val } if from.Search.IsSet() { out.Query = from.Search.Val } if from.Hash.IsSet() { out.Fragment = from.Hash.Val } if from.Protocol.IsSet() { out.Scheme = strings.TrimSuffix(from.Protocol.Val, ":") } if from.Port.IsSet() { // should never result in an error, type is checked when decoding port, err := strconv.Atoi(fmt.Sprint(from.Port.Val)) if err == nil { out.Port = uint32(port) } } } func mapToResponseModel(from contextResponse, out *modelpb.HTTPResponse) { if from.Finished.IsSet() { val := from.Finished.Val out.Finished = &val } if from.Headers.IsSet() { out.Headers = modeldecoderutil.HTTPHeadersToModelpb(from.Headers.Val, out.Headers) } if from.HeadersSent.IsSet() { val := from.HeadersSent.Val out.HeadersSent = &val } if from.StatusCode.IsSet() { out.StatusCode = uint32(from.StatusCode.Val) } if from.TransferSize.IsSet() { val := uint64(from.TransferSize.Val) out.TransferSize = &val } if from.EncodedBodySize.IsSet() { val := uint64(from.EncodedBodySize.Val) out.EncodedBodySize = &val } if from.DecodedBodySize.IsSet() { val := uint64(from.DecodedBodySize.Val) out.DecodedBodySize = &val } } func mapToServiceModel(from contextService, outPtr **modelpb.Service) { var out *modelpb.Service if *outPtr == nil { *outPtr = &modelpb.Service{} } out = *outPtr if from.Environment.IsSet() { out.Environment = from.Environment.Val } if from.Framework.IsSet() { if out.Framework == nil { out.Framework = &modelpb.Framework{} } if from.Framework.Name.IsSet() { out.Framework.Name = from.Framework.Name.Val } if from.Framework.Version.IsSet() { out.Framework.Version = from.Framework.Version.Val } } if from.Language.IsSet() { if out.Language == nil { out.Language = &modelpb.Language{} } if from.Language.Name.IsSet() { out.Language.Name = from.Language.Name.Val } if from.Language.Version.IsSet() { out.Language.Version = from.Language.Version.Val } } if from.Name.IsSet() { out.Name = from.Name.Val out.Version = from.Version.Val } if from.Node.Name.IsSet() { if out.Node == nil { out.Node = &modelpb.ServiceNode{} } out.Node.Name = from.Node.Name.Val } if from.Runtime.IsSet() { if out.Runtime == nil { out.Runtime = &modelpb.Runtime{} } if from.Runtime.Name.IsSet() { out.Runtime.Name = from.Runtime.Name.Val } if from.Runtime.Version.IsSet() { out.Runtime.Version = from.Runtime.Version.Val } } if from.Origin.IsSet() { outOrigin := modelpb.ServiceOrigin{} if from.Origin.ID.IsSet() { outOrigin.Id = from.Origin.ID.Val } if from.Origin.Name.IsSet() { outOrigin.Name = from.Origin.Name.Val } if from.Origin.Version.IsSet() { outOrigin.Version = from.Origin.Version.Val } out.Origin = &outOrigin } if from.Target.IsSet() { outTarget := modelpb.ServiceTarget{} if from.Target.Name.IsSet() { outTarget.Name = from.Target.Name.Val } if from.Target.Type.IsSet() { outTarget.Type = from.Target.Type.Val } out.Target = &outTarget } } func mapToAgentModel(from contextServiceAgent, out **modelpb.Agent) { if *out == nil { *out = &modelpb.Agent{} } if from.Name.IsSet() { (*out).Name = from.Name.Val } if from.Version.IsSet() { (*out).Version = from.Version.Val } if from.EphemeralID.IsSet() { (*out).EphemeralId = from.EphemeralID.Val } } func mapToSpanModel(from *span, event *modelpb.APMEvent) { out := modelpb.Span{} event.Span = &out // map span specific data if !from.Action.IsSet() && !from.Subtype.IsSet() { sep := "." before, after, ok := strings.Cut(from.Type.Val, sep) out.Type = before if ok { out.Subtype, out.Action, _ = strings.Cut(after, sep) } } else { if from.Action.IsSet() { out.Action = from.Action.Val } if from.Subtype.IsSet() { out.Subtype = from.Subtype.Val } if from.Type.IsSet() { out.Type = from.Type.Val } } if from.Composite.IsSet() { composite := modelpb.Composite{} if from.Composite.Count.IsSet() { composite.Count = uint32(from.Composite.Count.Val) } if from.Composite.Sum.IsSet() { composite.Sum = from.Composite.Sum.Val } if strategy, ok := compressionStrategyText[from.Composite.CompressionStrategy.Val]; ok { composite.CompressionStrategy = strategy } out.Composite = &composite } if len(from.ChildIDs) > 0 { event.ChildIds = modeldecoderutil.Reslice(event.ChildIds, len(from.ChildIDs)) copy(event.ChildIds, from.ChildIDs) } if from.Context.Database.IsSet() { db := modelpb.DB{} if from.Context.Database.Instance.IsSet() { db.Instance = from.Context.Database.Instance.Val } if from.Context.Database.Link.IsSet() { db.Link = from.Context.Database.Link.Val } if from.Context.Database.RowsAffected.IsSet() { val := uint32(from.Context.Database.RowsAffected.Val) db.RowsAffected = &val } if from.Context.Database.Statement.IsSet() { db.Statement = from.Context.Database.Statement.Val } if from.Context.Database.Type.IsSet() { db.Type = from.Context.Database.Type.Val } if from.Context.Database.User.IsSet() { db.UserName = from.Context.Database.User.Val } out.Db = &db } if from.Context.Destination.Address.IsSet() || from.Context.Destination.Port.IsSet() { if from.Context.Destination.Address.IsSet() { if event.Destination == nil { event.Destination = &modelpb.Destination{} } event.Destination.Address = from.Context.Destination.Address.Val } if from.Context.Destination.Port.IsSet() { if event.Destination == nil { event.Destination = &modelpb.Destination{} } event.Destination.Port = uint32(from.Context.Destination.Port.Val) } } if from.Context.Destination.Service.IsSet() { service := modelpb.DestinationService{} if from.Context.Destination.Service.Name.IsSet() { service.Name = from.Context.Destination.Service.Name.Val } if from.Context.Destination.Service.Resource.IsSet() { service.Resource = from.Context.Destination.Service.Resource.Val } if from.Context.Destination.Service.Type.IsSet() { service.Type = from.Context.Destination.Service.Type.Val } out.DestinationService = &service } if from.Context.HTTP.IsSet() { if from.Context.HTTP.Method.IsSet() { if event.Http == nil { event.Http = &modelpb.HTTP{} } if event.Http.Request == nil { event.Http.Request = &modelpb.HTTPRequest{} } event.Http.Request.Method = from.Context.HTTP.Method.Val } if from.Context.HTTP.Request.IsSet() { if event.Http == nil { event.Http = &modelpb.HTTP{} } if event.Http.Request == nil { event.Http.Request = &modelpb.HTTPRequest{} } if from.Context.HTTP.Request.ID.IsSet() { event.Http.Request.Id = from.Context.HTTP.Request.ID.Val } if from.Context.HTTP.Request.Body.IsSet() { event.Http.Request.Body = modeldecoderutil.ToValue(from.Context.HTTP.Request.Body.Val) } } if from.Context.HTTP.Response.IsSet() { if event.Http == nil { event.Http = &modelpb.HTTP{} } response := modelpb.HTTPResponse{} if from.Context.HTTP.Response.DecodedBodySize.IsSet() { val := uint64(from.Context.HTTP.Response.DecodedBodySize.Val) response.DecodedBodySize = &val } if from.Context.HTTP.Response.EncodedBodySize.IsSet() { val := uint64(from.Context.HTTP.Response.EncodedBodySize.Val) response.EncodedBodySize = &val } if from.Context.HTTP.Response.Headers.IsSet() { response.Headers = modeldecoderutil.HTTPHeadersToModelpb(from.Context.HTTP.Response.Headers.Val, response.Headers) } if from.Context.HTTP.Response.StatusCode.IsSet() { response.StatusCode = uint32(from.Context.HTTP.Response.StatusCode.Val) } if from.Context.HTTP.Response.TransferSize.IsSet() { val := uint64(from.Context.HTTP.Response.TransferSize.Val) response.TransferSize = &val } event.Http.Response = &response } if from.Context.HTTP.StatusCode.IsSet() { if event.Http == nil { event.Http = &modelpb.HTTP{} } if event.Http.Response == nil { event.Http.Response = &modelpb.HTTPResponse{} } event.Http.Response.StatusCode = uint32(from.Context.HTTP.StatusCode.Val) } if from.Context.HTTP.URL.IsSet() { if event.Url == nil { event.Url = &modelpb.URL{} } event.Url.Original = from.Context.HTTP.URL.Val } } if from.Context.Message.IsSet() { message := modelpb.Message{} if from.Context.Message.Body.IsSet() { message.Body = from.Context.Message.Body.Val } if from.Context.Message.Headers.IsSet() { message.Headers = modeldecoderutil.HTTPHeadersToModelpb(from.Context.Message.Headers.Val, message.Headers) } if from.Context.Message.Age.Milliseconds.IsSet() { val := uint64(from.Context.Message.Age.Milliseconds.Val) message.AgeMillis = &val } if from.Context.Message.Queue.Name.IsSet() { message.QueueName = from.Context.Message.Queue.Name.Val } if from.Context.Message.RoutingKey.IsSet() { message.RoutingKey = from.Context.Message.RoutingKey.Val } out.Message = &message } if from.Context.Service.IsSet() { mapToServiceModel(from.Context.Service, &event.Service) mapToAgentModel(from.Context.Service.Agent, &event.Agent) } if !from.Context.Service.Target.Type.IsSet() && from.Context.Destination.Service.Resource.IsSet() { if event.Service == nil { event.Service = &modelpb.Service{} } outTarget := targetFromDestinationResource(from.Context.Destination.Service.Resource.Val) event.Service.Target = outTarget } if len(from.Context.Tags) > 0 { modeldecoderutil.MergeLabels(from.Context.Tags, event) } if from.Duration.IsSet() { if event.Event == nil { event.Event = &modelpb.Event{} } event.Event.Duration = uint64(from.Duration.Val * float64(time.Millisecond)) } if from.ID.IsSet() { out.Id = from.ID.Val } if from.Name.IsSet() { out.Name = from.Name.Val } if event.Event == nil { event.Event = &modelpb.Event{} } if from.Outcome.IsSet() { event.Event.Outcome = from.Outcome.Val } else { if from.Context.HTTP.StatusCode.IsSet() { statusCode := from.Context.HTTP.StatusCode.Val if statusCode >= http.StatusBadRequest { event.Event.Outcome = "failure" } else { event.Event.Outcome = "success" } } else { event.Event.Outcome = "unknown" } } if from.ParentID.IsSet() { event.ParentId = from.ParentID.Val } if from.SampleRate.IsSet() { if from.SampleRate.Val > 0 { out.RepresentativeCount = 1 / from.SampleRate.Val } } else { // NOTE: we don't know the sample rate, so we need to make an assumption. // // Agents never send spans for non-sampled transactions, so assuming a // representative count of 1 (i.e. sampling rate of 100%) may be invalid; but // this is more useful than producing no metrics at all (i.e. assuming 0). out.RepresentativeCount = 1 } if len(from.Stacktrace) > 0 { out.Stacktrace = modeldecoderutil.ResliceAndPopulateNil( out.Stacktrace, len(from.Stacktrace), modeldecoderutil.NewType[modelpb.StacktraceFrame], ) mapToStracktraceModel(from.Stacktrace, out.Stacktrace) } if from.Sync.IsSet() { val := from.Sync.Val out.Sync = &val } if !from.Timestamp.Val.IsZero() { event.Timestamp = modelpb.FromTime(from.Timestamp.Val) } else if from.Start.IsSet() { // event.Timestamp should have been initialized to the time the // payload was received; offset that by "start" milliseconds for // RUM. event.Timestamp += uint64(float64(time.Millisecond) * from.Start.Val) } if from.TraceID.IsSet() { event.Trace = &modelpb.Trace{} event.Trace.Id = from.TraceID.Val } if from.TransactionID.IsSet() { event.Transaction = &modelpb.Transaction{} event.Transaction.Id = from.TransactionID.Val } if from.OTel.IsSet() { mapOTelAttributesSpan(from.OTel, event) } if len(from.Links) > 0 { out.Links = modeldecoderutil.ResliceAndPopulateNil( out.Links, len(from.Links), modeldecoderutil.NewType[modelpb.SpanLink], ) mapSpanLinks(from.Links, out.Links) } if out.Type == "" { out.Type = "unknown" } } func mapToStracktraceModel(from []stacktraceFrame, out []*modelpb.StacktraceFrame) { for idx, eventFrame := range from { fr := out[idx] if eventFrame.AbsPath.IsSet() { fr.AbsPath = eventFrame.AbsPath.Val } if eventFrame.Classname.IsSet() { fr.Classname = eventFrame.Classname.Val } if eventFrame.ColumnNumber.IsSet() { val := uint32(eventFrame.ColumnNumber.Val) fr.Colno = &val } if eventFrame.ContextLine.IsSet() { fr.ContextLine = eventFrame.ContextLine.Val } if eventFrame.Filename.IsSet() { fr.Filename = eventFrame.Filename.Val } if eventFrame.Function.IsSet() { fr.Function = eventFrame.Function.Val } if eventFrame.LibraryFrame.IsSet() { val := eventFrame.LibraryFrame.Val fr.LibraryFrame = val } if eventFrame.LineNumber.IsSet() { val := uint32(eventFrame.LineNumber.Val) fr.Lineno = &val } if eventFrame.Module.IsSet() { fr.Module = eventFrame.Module.Val } if len(eventFrame.PostContext) > 0 { fr.PostContext = modeldecoderutil.Reslice(fr.PostContext, len(eventFrame.PostContext)) copy(fr.PostContext, eventFrame.PostContext) } if len(eventFrame.PreContext) > 0 { fr.PreContext = modeldecoderutil.Reslice(fr.PreContext, len(eventFrame.PreContext)) copy(fr.PreContext, eventFrame.PreContext) } if len(eventFrame.Vars) > 0 { fr.Vars = modeldecoderutil.ToKv(eventFrame.Vars, fr.Vars) } } } func mapToTransactionModel(from *transaction, event *modelpb.APMEvent) { out := modelpb.Transaction{} event.Transaction = &out // overwrite metadata with event specific information mapToServiceModel(from.Context.Service, &event.Service) mapToAgentModel(from.Context.Service.Agent, &event.Agent) overwriteUserInMetadataModel(from.Context.User, event) mapToUserAgentModel(from.Context.Request.Headers, &event.UserAgent) mapToClientModel(from.Context.Request, &event.Source, &event.Client) if from.FAAS.IsSet() { if event.Faas == nil { event.Faas = &modelpb.Faas{} } mapToFAASModel(from.FAAS, event.Faas) } if from.Context.Cloud.IsSet() { if event.Cloud == nil { event.Cloud = &modelpb.Cloud{} } mapToCloudModel(from.Context.Cloud, event.Cloud) } mapToDroppedSpansModel(from.DroppedSpanStats, event.Transaction) // map transaction specific data if from.Context.IsSet() { if len(from.Context.Custom) > 0 { out.Custom = modeldecoderutil.ToKv(from.Context.Custom, out.Custom) } if len(from.Context.Tags) > 0 { modeldecoderutil.MergeLabels(from.Context.Tags, event) } if from.Context.Message.IsSet() { out.Message = &modelpb.Message{} if from.Context.Message.Age.IsSet() { val := uint64(from.Context.Message.Age.Milliseconds.Val) out.Message.AgeMillis = &val } if from.Context.Message.Body.IsSet() { out.Message.Body = from.Context.Message.Body.Val } if from.Context.Message.Headers.IsSet() { out.Message.Headers = modeldecoderutil.HTTPHeadersToModelpb(from.Context.Message.Headers.Val, out.Message.Headers) } if from.Context.Message.Queue.IsSet() && from.Context.Message.Queue.Name.IsSet() { out.Message.QueueName = from.Context.Message.Queue.Name.Val } if from.Context.Message.RoutingKey.IsSet() { out.Message.RoutingKey = from.Context.Message.RoutingKey.Val } } if from.Context.Request.IsSet() { if event.Http == nil { event.Http = &modelpb.HTTP{} } event.Http.Request = &modelpb.HTTPRequest{} mapToRequestModel(from.Context.Request, event.Http.Request) if from.Context.Request.HTTPVersion.IsSet() { event.Http.Version = from.Context.Request.HTTPVersion.Val } } if from.Context.Request.URL.IsSet() { if event.Url == nil { event.Url = &modelpb.URL{} } mapToRequestURLModel(from.Context.Request.URL, event.Url) } if from.Context.Response.IsSet() { if event.Http == nil { event.Http = &modelpb.HTTP{} } event.Http.Response = &modelpb.HTTPResponse{} mapToResponseModel(from.Context.Response, event.Http.Response) } if from.Context.Page.IsSet() { if from.Context.Page.URL.IsSet() && !from.Context.Request.URL.IsSet() { event.Url = modelpb.ParseURL(from.Context.Page.URL.Val, "", "") } if from.Context.Page.Referer.IsSet() { if event.Http == nil { event.Http = &modelpb.HTTP{} } if event.Http.Request == nil { event.Http.Request = &modelpb.HTTPRequest{} } if event.Http.Request.Referrer == "" { event.Http.Request.Referrer = from.Context.Page.Referer.Val } } } } if from.Duration.IsSet() { if event.Event == nil { event.Event = &modelpb.Event{} } event.Event.Duration = uint64(from.Duration.Val * float64(time.Millisecond)) } if from.ID.IsSet() { out.Id = from.ID.Val event.Span = &modelpb.Span{} event.Span.Id = from.ID.Val } if from.Marks.IsSet() { out.Marks = make(map[string]*modelpb.TransactionMark, len(from.Marks.Events)) for event, val := range from.Marks.Events { if len(val.Measurements) > 0 { tm := modelpb.TransactionMark{} tm.Measurements = val.Measurements out.Marks[event] = &tm } } } if from.Name.IsSet() { out.Name = from.Name.Val } if event.Event == nil { event.Event = &modelpb.Event{} } if from.Outcome.IsSet() { event.Event.Outcome = from.Outcome.Val } else { if from.Context.Response.StatusCode.IsSet() { statusCode := from.Context.Response.StatusCode.Val if statusCode >= http.StatusInternalServerError { event.Event.Outcome = "failure" } else { event.Event.Outcome = "success" } } else { event.Event.Outcome = "unknown" } } if from.ParentID.IsSet() { event.ParentId = from.ParentID.Val } if from.Result.IsSet() { out.Result = from.Result.Val } sampled := true if from.Sampled.IsSet() { sampled = from.Sampled.Val } out.Sampled = sampled if from.SampleRate.IsSet() { if from.SampleRate.Val > 0 { out.RepresentativeCount = 1 / from.SampleRate.Val } } else { out.RepresentativeCount = 1 } if from.Session.ID.IsSet() { event.Session = &modelpb.Session{} event.Session.Id = from.Session.ID.Val event.Session.Sequence = uint64(from.Session.Sequence.Val) } if from.SpanCount.Dropped.IsSet() { if out.SpanCount == nil { out.SpanCount = &modelpb.SpanCount{} } dropped := uint32(from.SpanCount.Dropped.Val) out.SpanCount.Dropped = &dropped } if from.SpanCount.Started.IsSet() { if out.SpanCount == nil { out.SpanCount = &modelpb.SpanCount{} } started := uint32(from.SpanCount.Started.Val) out.SpanCount.Started = &started } if !from.Timestamp.Val.IsZero() { event.Timestamp = modelpb.FromTime(from.Timestamp.Val) } if from.TraceID.IsSet() { event.Trace = &modelpb.Trace{} event.Trace.Id = from.TraceID.Val } if from.Type.IsSet() { out.Type = from.Type.Val } if from.UserExperience.IsSet() { out.UserExperience = &modelpb.UserExperience{} out.UserExperience.CumulativeLayoutShift = -1 out.UserExperience.FirstInputDelay = -1 out.UserExperience.TotalBlockingTime = -1 out.UserExperience.LongTask = nil if from.UserExperience.CumulativeLayoutShift.IsSet() { out.UserExperience.CumulativeLayoutShift = from.UserExperience.CumulativeLayoutShift.Val } if from.UserExperience.FirstInputDelay.IsSet() { out.UserExperience.FirstInputDelay = from.UserExperience.FirstInputDelay.Val } if from.UserExperience.TotalBlockingTime.IsSet() { out.UserExperience.TotalBlockingTime = from.UserExperience.TotalBlockingTime.Val } if from.UserExperience.Longtask.IsSet() { out.UserExperience.LongTask = &modelpb.LongtaskMetrics{} out.UserExperience.LongTask.Count = uint64(from.UserExperience.Longtask.Count.Val) out.UserExperience.LongTask.Sum = from.UserExperience.Longtask.Sum.Val out.UserExperience.LongTask.Max = from.UserExperience.Longtask.Max.Val } } if from.OTel.IsSet() { if event.Span == nil { event.Span = &modelpb.Span{} } mapOTelAttributesTransaction(from.OTel, event) } if len(from.Links) > 0 { if event.Span == nil { event.Span = &modelpb.Span{} } event.Span.Links = modeldecoderutil.ResliceAndPopulateNil( event.Span.Links, len(from.Links), modeldecoderutil.NewType[modelpb.SpanLink], ) mapSpanLinks(from.Links, event.Span.Links) } if out.Type == "" { out.Type = "unknown" } } func mapToLogModel(from *log, event *modelpb.APMEvent) { if from.FAAS.IsSet() { if event.Faas == nil { event.Faas = &modelpb.Faas{} } mapToFAASModel(from.FAAS, event.Faas) } if !from.Timestamp.Val.IsZero() { event.Timestamp = modelpb.FromTime(from.Timestamp.Val) } if from.TraceID.IsSet() { event.Trace = &modelpb.Trace{} event.Trace.Id = from.TraceID.Val } if from.TransactionID.IsSet() { event.Transaction = &modelpb.Transaction{} event.Transaction.Id = from.TransactionID.Val event.Span = &modelpb.Span{} event.Span.Id = from.TransactionID.Val } if from.SpanID.IsSet() { event.Span = &modelpb.Span{} event.Span.Id = from.SpanID.Val } if from.Message.IsSet() { event.Message = from.Message.Val } if from.Level.IsSet() { if event.Log == nil { event.Log = &modelpb.Log{} } event.Log.Level = from.Level.Val } if from.Logger.IsSet() { if event.Log == nil { event.Log = &modelpb.Log{} } event.Log.Logger = from.Logger.Val } if from.OriginFunction.IsSet() { if event.Log == nil { event.Log = &modelpb.Log{} } if event.Log.Origin == nil { event.Log.Origin = &modelpb.LogOrigin{} } event.Log.Origin.FunctionName = from.OriginFunction.Val } if from.OriginFileLine.IsSet() { if event.Log == nil { event.Log = &modelpb.Log{} } if event.Log.Origin == nil { event.Log.Origin = &modelpb.LogOrigin{} } if event.Log.Origin.File == nil { event.Log.Origin.File = &modelpb.LogOriginFile{} } event.Log.Origin.File.Line = uint32(from.OriginFileLine.Val) } if from.OriginFileName.IsSet() { if event.Log == nil { event.Log = &modelpb.Log{} } if event.Log.Origin == nil { event.Log.Origin = &modelpb.LogOrigin{} } if event.Log.Origin.File == nil { event.Log.Origin.File = &modelpb.LogOriginFile{} } event.Log.Origin.File.Name = from.OriginFileName.Val } if from.ErrorType.IsSet() || from.ErrorMessage.IsSet() || from.ErrorStacktrace.IsSet() { event.Error = &modelpb.Error{} event.Error.Message = from.ErrorMessage.Val event.Error.Type = from.ErrorType.Val event.Error.StackTrace = from.ErrorStacktrace.Val } if from.ServiceName.IsSet() { if event.Service == nil { event.Service = &modelpb.Service{} } event.Service.Name = from.ServiceName.Val } if from.ServiceVersion.IsSet() { if event.Service == nil { event.Service = &modelpb.Service{} } event.Service.Version = from.ServiceVersion.Val } if from.ServiceEnvironment.IsSet() { if event.Service == nil { event.Service = &modelpb.Service{} } event.Service.Environment = from.ServiceEnvironment.Val } if from.ServiceNodeName.IsSet() { if event.Service == nil { event.Service = &modelpb.Service{} } if event.Service.Node == nil { event.Service.Node = &modelpb.ServiceNode{} } event.Service.Node.Name = from.ServiceNodeName.Val } if from.ProcessThreadName.IsSet() { if event.Process == nil { event.Process = &modelpb.Process{} } if event.Process.Thread == nil { event.Process.Thread = &modelpb.ProcessThread{} } event.Process.Thread.Name = from.ProcessThreadName.Val } if from.EventDataset.IsSet() { if event.Event == nil { event.Event = &modelpb.Event{} } event.Event.Dataset = from.EventDataset.Val } if len(from.Labels) > 0 { modeldecoderutil.MergeLabels(from.Labels, event) } if event.Error == nil { if event.Event == nil { event.Event = &modelpb.Event{} } event.Event.Kind = "event" } } func mapOTelAttributesTransaction(from otel, out *modelpb.APMEvent) { scope := pcommon.NewInstrumentationScope() m := otelAttributeMap(&from) if from.SpanKind.IsSet() { out.Span.Kind = from.SpanKind.Val } if out.Labels == nil { out.Labels = make(modelpb.Labels) } if out.NumericLabels == nil { out.NumericLabels = make(modelpb.NumericLabels) } // TODO: Does this work? Is there a way we can infer the status code, // potentially in the actual attributes map? spanStatus := ptrace.NewStatus() spanStatus.SetCode(ptrace.StatusCodeUnset) otlp.TranslateTransaction(m, spanStatus, scope, out) if out.Span.Kind == "" { switch out.Transaction.Type { case "messaging": out.Span.Kind = "CONSUMER" case "request": out.Span.Kind = "SERVER" default: out.Span.Kind = "INTERNAL" } } } const ( spanKindInternal = "INTERNAL" spanKindServer = "SERVER" spanKindClient = "CLIENT" spanKindProducer = "PRODUCER" spanKindConsumer = "CONSUMER" ) func mapOTelAttributesSpan(from otel, out *modelpb.APMEvent) { m := otelAttributeMap(&from) if out.Labels == nil { out.Labels = make(modelpb.Labels) } if out.NumericLabels == nil { out.NumericLabels = make(modelpb.NumericLabels) } var spanKind ptrace.SpanKind if from.SpanKind.IsSet() { switch from.SpanKind.Val { case spanKindInternal: spanKind = ptrace.SpanKindInternal case spanKindServer: spanKind = ptrace.SpanKindServer case spanKindClient: spanKind = ptrace.SpanKindClient case spanKindProducer: spanKind = ptrace.SpanKindProducer case spanKindConsumer: spanKind = ptrace.SpanKindConsumer default: spanKind = ptrace.SpanKindUnspecified } out.Span.Kind = from.SpanKind.Val } otlp.TranslateSpan(spanKind, m, out) if spanKind == ptrace.SpanKindUnspecified { switch out.Span.Type { case "db", "external", "storage": out.Span.Kind = "CLIENT" default: out.Span.Kind = "INTERNAL" } } } func mapToUserAgentModel(from nullable.HTTPHeader, out **modelpb.UserAgent) { // overwrite userAgent information if available if from.IsSet() { if h := from.Val.Values(textproto.CanonicalMIMEHeaderKey("User-Agent")); len(h) > 0 { if *out == nil { *out = &modelpb.UserAgent{} } (*out).Original = strings.Join(h, ", ") } } } func overwriteUserInMetadataModel(from user, out *modelpb.APMEvent) { // overwrite User specific values if set // either populate all User fields or none to avoid mixing // different user data if !from.Domain.IsSet() && !from.ID.IsSet() && !from.Email.IsSet() && !from.Name.IsSet() { return } out.User = &modelpb.User{} if from.Domain.IsSet() { out.User.Domain = from.Domain.Val } if from.ID.IsSet() { out.User.Id = fmt.Sprint(from.ID.Val) } if from.Email.IsSet() { out.User.Email = from.Email.Val } if from.Name.IsSet() { out.User.Name = from.Name.Val } } func otelAttributeMap(o *otel) pcommon.Map { m := pcommon.NewMap() for k, v := range o.Attributes { if attr, ok := otelAttributeValue(k, v); ok { attr.CopyTo(m.PutEmpty(k)) } } return m } func otelAttributeValue(k string, v interface{}) (pcommon.Value, bool) { // According to the spec, these are the allowed primitive types // Additionally, homogeneous arrays (single type) of primitive types are allowed // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/common.md#attributes switch v := v.(type) { case string: return pcommon.NewValueStr(v), true case bool: return pcommon.NewValueBool(v), true case json.Number: // Semantic conventions have specified types, and we rely on this // in processor/otel when mapping to our data model. For example, // `http.status_code` is expected to be an int. if !isOTelDoubleAttribute(k) { if v, err := v.Int64(); err == nil { return pcommon.NewValueInt(v), true } } if v, err := v.Float64(); err == nil { return pcommon.NewValueDouble(v), true } case []interface{}: array := pcommon.NewValueSlice() array.Slice().EnsureCapacity(len(v)) for i := range v { if elem, ok := otelAttributeValue(k, v[i]); ok { elem.CopyTo(array.Slice().AppendEmpty()) } } return array, true } return pcommon.Value{}, false } // isOTelDoubleAttribute indicates whether k is an OpenTelemetry semantic convention attribute // known to have type "double". As this list grows over time, we should consider generating // the mapping with OpenTelemetry's semconvgen build tool. // // For the canonical semantic convention definitions, see // https://github.com/open-telemetry/opentelemetry-specification/tree/main/semantic_conventions/trace func isOTelDoubleAttribute(k string) bool { switch k { case "aws.dynamodb.provisioned_read_capacity": return true case "aws.dynamodb.provisioned_write_capacity": return true } return false } func mapSpanLinks(from []spanLink, out []*modelpb.SpanLink) { for i, link := range from { out[i].SpanId = link.SpanID.Val out[i].TraceId = link.TraceID.Val } } func targetFromDestinationResource(res string) *modelpb.ServiceTarget { target := modelpb.ServiceTarget{} submatch := reForServiceTargetExpr.FindStringSubmatch(res) switch len(submatch) { case 3: target.Type = submatch[1] target.Name = submatch[2] default: target.Name = res } return &target }