func TranslateTransaction()

in input/otlp/traces.go [227:571]


func TranslateTransaction(
	attributes pcommon.Map,
	spanStatus ptrace.Status,
	library pcommon.InstrumentationScope,
	event *modelpb.APMEvent,
) {
	isJaeger := strings.HasPrefix(event.Agent.Name, "Jaeger")

	var (
		netHostName string
		netHostPort int
	)

	var (
		httpScheme     string
		httpURL        string
		httpServerName string
		httpHost       string
		http           modelpb.HTTP
		httpRequest    modelpb.HTTPRequest
		httpResponse   modelpb.HTTPResponse
		urlPath        string
		urlQuery       string
	)

	var isHTTP, isRPC, isMessaging bool
	var messagingQueueName string

	var samplerType, samplerParam pcommon.Value
	attributes.Range(func(kDots string, v pcommon.Value) bool {
		if isJaeger {
			switch kDots {
			case "sampler.type":
				samplerType = v
				return true
			case "sampler.param":
				samplerParam = v
				return true
			}
		}

		k := replaceDots(kDots)
		switch v.Type() {
		case pcommon.ValueTypeSlice:
			switch kDots {
			case "elastic.profiler_stack_trace_ids":
				var vSlice = v.Slice()
				event.Transaction.ProfilerStackTraceIds = slices.Grow(event.Transaction.ProfilerStackTraceIds, vSlice.Len())
				for i := 0; i < vSlice.Len(); i++ {
					var idVal = vSlice.At(i)
					if idVal.Type() == pcommon.ValueTypeStr {
						event.Transaction.ProfilerStackTraceIds = append(event.Transaction.ProfilerStackTraceIds, idVal.Str())
					}
				}
			default:
				setLabel(k, event, v)
			}
		case pcommon.ValueTypeInt:
			switch kDots {
			case semconv25.AttributeHTTPStatusCode, semconv.AttributeHTTPResponseStatusCode:
				isHTTP = true
				httpResponse.StatusCode = uint32(v.Int())
				http.Response = &httpResponse
			case semconv25.AttributeNetPeerPort, semconv.AttributeClientPort:
				if event.Source == nil {
					event.Source = &modelpb.Source{}
				}
				event.Source.Port = uint32(v.Int())
			case semconv25.AttributeNetHostPort, semconv.AttributeServerPort:
				netHostPort = int(v.Int())
			case semconv.AttributeRPCGRPCStatusCode:
				isRPC = true
				event.Transaction.Result = codes.Code(v.Int()).String()
			default:
				setLabel(k, event, v)
			}
		case pcommon.ValueTypeMap:
		case pcommon.ValueTypeStr:
			stringval := truncate(v.Str())
			switch kDots {
			// http.*
			case semconv25.AttributeHTTPMethod, semconv.AttributeHTTPRequestMethod:
				isHTTP = true
				httpRequest.Method = stringval
				http.Request = &httpRequest
			case semconv25.AttributeHTTPURL, semconv.AttributeURLFull, semconv25.AttributeHTTPTarget, attributeHTTPPath:
				isHTTP = true
				httpURL = stringval
			case semconv.AttributeURLPath:
				isHTTP = true
				urlPath = stringval
			case semconv.AttributeURLQuery:
				isHTTP = true
				urlQuery = stringval
			case semconv12.AttributeHTTPHost: //removed after 1.12 (stable)
				isHTTP = true
				httpHost = stringval
			case semconv25.AttributeHTTPScheme, semconv.AttributeURLScheme:
				isHTTP = true
				httpScheme = stringval
			case semconv25.AttributeHTTPStatusCode, semconv.AttributeHTTPResponseStatusCode:
				if intv, err := strconv.Atoi(stringval); err == nil {
					isHTTP = true
					httpResponse.StatusCode = uint32(intv)
					http.Response = &httpResponse
				}
			case "http.protocol":
				if !strings.HasPrefix(stringval, "HTTP/") {
					// Unexpected, store in labels for debugging.
					modelpb.Labels(event.Labels).Set(k, stringval)
					break
				}
				stringval = strings.TrimPrefix(stringval, "HTTP/")
				fallthrough
			case semconv25.AttributeHTTPFlavor: //removed after 1.25 (experimental)
				isHTTP = true
				http.Version = stringval
			case semconv12.AttributeHTTPServerName: //removed after 1.12 (stable)
				isHTTP = true
				httpServerName = stringval
			case semconv18.AttributeHTTPClientIP: //removed after 1.18 (stable)
				if ip, err := modelpb.ParseIP(stringval); err == nil {
					if event.Client == nil {
						event.Client = &modelpb.Client{}
					}
					event.Client.Ip = ip
				}
			case semconv25.AttributeHTTPUserAgent, semconv.AttributeUserAgentOriginal:
				if event.UserAgent == nil {
					event.UserAgent = &modelpb.UserAgent{}
				}
				event.UserAgent.Original = stringval

			// net.*
			case semconv12.AttributeNetPeerIP, semconv25.AttributeNetSockPeerAddr:
				if event.Source == nil {
					event.Source = &modelpb.Source{}
				}
				if ip, err := modelpb.ParseIP(stringval); err == nil {
					event.Source.Ip = ip
				}
			case semconv25.AttributeNetPeerName, semconv.AttributeClientAddress:
				if event.Source == nil {
					event.Source = &modelpb.Source{}
				}
				event.Source.Domain = stringval
			case semconv25.AttributeNetHostName, semconv.AttributeServerAddress:
				netHostName = stringval
			case semconv.AttributeNetworkConnectionType:
				if event.Network == nil {
					event.Network = &modelpb.Network{}
				}
				if event.Network.Connection == nil {
					event.Network.Connection = &modelpb.NetworkConnection{}
				}
				event.Network.Connection.Type = stringval
			case semconv.AttributeNetworkConnectionSubtype:
				if event.Network == nil {
					event.Network = &modelpb.Network{}
				}
				if event.Network.Connection == nil {
					event.Network.Connection = &modelpb.NetworkConnection{}
				}
				event.Network.Connection.Subtype = stringval
			case semconv.AttributeNetworkCarrierMcc:
				if event.Network == nil {
					event.Network = &modelpb.Network{}
				}
				if event.Network.Carrier == nil {
					event.Network.Carrier = &modelpb.NetworkCarrier{}
				}
				event.Network.Carrier.Mcc = stringval
			case semconv.AttributeNetworkCarrierMnc:
				if event.Network == nil {
					event.Network = &modelpb.Network{}
				}
				if event.Network.Carrier == nil {
					event.Network.Carrier = &modelpb.NetworkCarrier{}
				}
				event.Network.Carrier.Mnc = stringval
			case semconv.AttributeNetworkCarrierName:
				if event.Network == nil {
					event.Network = &modelpb.Network{}
				}
				if event.Network.Carrier == nil {
					event.Network.Carrier = &modelpb.NetworkCarrier{}
				}
				event.Network.Carrier.Name = stringval
			case semconv.AttributeNetworkCarrierIcc:
				if event.Network == nil {
					event.Network = &modelpb.Network{}
				}
				if event.Network.Carrier == nil {
					event.Network.Carrier = &modelpb.NetworkCarrier{}
				}
				event.Network.Carrier.Icc = stringval

			// messaging.*
			//
			// messaging.destination is now called messaging.destination.name in the latest semconv
			// https://opentelemetry.io/docs/specs/semconv/attributes-registry/messaging
			// keep both of them for the backward compatibility
			case semconv16.AttributeMessagingDestination, semconv.AttributeMessagingDestinationName, "message_bus.destination":
				isMessaging = true
				messagingQueueName = stringval
			case semconv.AttributeMessagingSystem:
				isMessaging = true
				modelpb.Labels(event.Labels).Set(k, stringval)
			case semconv25.AttributeMessagingOperation, semconv.AttributeMessagingOperationType, semconv.AttributeMessagingOperationName:
				isMessaging = true
				modelpb.Labels(event.Labels).Set(k, stringval)

			// rpc.*
			//
			// TODO(axw) add RPC fieldset to ECS? Currently we drop these
			// attributes, and rely on the operation name like we do with
			// Elastic APM agents.
			case semconv.AttributeRPCSystem:
				isRPC = true
			case semconv.AttributeRPCGRPCStatusCode:
				isRPC = true
			case semconv.AttributeRPCService:
			case semconv.AttributeRPCMethod:

			// miscellaneous
			case "type":
				event.Transaction.Type = stringval
			case "session.id":
				if event.Session == nil {
					event.Session = &modelpb.Session{}
				}
				event.Session.Id = stringval
			case semconv.AttributeServiceVersion:
				// NOTE support for sending service.version as a span tag
				// is deprecated, and will be removed in 8.0. Instrumentation
				// should set this as a resource attribute (OTel) or tracer
				// tag (Jaeger).
				event.Service.Version = stringval

			// data_stream.*
			case attributeDataStreamDataset:
				if event.DataStream == nil {
					event.DataStream = &modelpb.DataStream{}
				}
				event.DataStream.Dataset = sanitizeDataStreamDataset(stringval)
			case attributeDataStreamNamespace:
				if event.DataStream == nil {
					event.DataStream = &modelpb.DataStream{}
				}
				event.DataStream.Namespace = sanitizeDataStreamNamespace(stringval)
			default:
				modelpb.Labels(event.Labels).Set(k, stringval)
			}
		default:
			setLabel(k, event, v)
		}
		return true
	})

	if event.Transaction.Type == "" {
		switch {
		case isMessaging:
			event.Transaction.Type = "messaging"
		case isHTTP, isRPC:
			event.Transaction.Type = "request"
		default:
			event.Transaction.Type = "unknown"
		}
	}

	if isHTTP {
		if http.SizeVT() != 0 {
			event.Http = &http
		}

		// Set outcome nad result from status code.
		if statusCode := httpResponse.StatusCode; statusCode > 0 {
			if event.Event.Outcome == outcomeUnknown {
				event.Event.Outcome = serverHTTPStatusCodeOutcome(int(statusCode))
			}
			if event.Transaction.Result == "" {
				event.Transaction.Result = httpStatusCodeResult(int(statusCode))
			}
		}

		httpHost := httpHost
		if httpHost == "" {
			httpHost = httpServerName
			if httpHost == "" {
				httpHost = netHostName
				if httpHost == "" {
					httpHost = event.GetHost().GetHostname()
				}
			}
			if httpHost != "" && netHostPort > 0 {
				httpHost = net.JoinHostPort(httpHost, strconv.Itoa(netHostPort))
			}
		}

		// Build a relative url from the UrlPath and UrlQuery.
		httpURL := httpURL
		if httpURL == "" && urlPath != "" {
			httpURL = urlPath
			if urlQuery != "" {
				httpURL += "?" + urlQuery
			}
		}

		// Build the modelpb.URL from http{URL,Host,Scheme}.
		event.Url = modelpb.ParseURL(httpURL, httpHost, httpScheme)
	}

	if isMessaging {
		// Overwrite existing event.Transaction.Message
		event.Transaction.Message = nil
		if messagingQueueName != "" {
			event.Transaction.Message = &modelpb.Message{}
			event.Transaction.Message.QueueName = messagingQueueName
		}
	}

	if event.Client == nil && event.Source != nil {
		event.Client = &modelpb.Client{}
		event.Client.Ip = event.Source.Ip
		event.Client.Port = event.Source.Port
		event.Client.Domain = event.Source.Domain
	}

	if samplerType != (pcommon.Value{}) {
		// The client has reported its sampling rate, so we can use it to extrapolate span metrics.
		parseSamplerAttributes(samplerType, samplerParam, event)
	}

	if event.Transaction.Result == "" {
		event.Transaction.Result = spanStatusResult(spanStatus)
	}

	// if outcome and result are still not assigned, assign success
	if event.Event.Outcome == outcomeUnknown {
		event.Event.Outcome = outcomeSuccess
		if event.Transaction.Result == "" {
			event.Transaction.Result = "Success"
		}
	}
}