From 66ecdeda2cf967bac3bf979ee6768c5d3d69ace7 Mon Sep 17 00:00:00 2001 From: Oseme Odigie Date: Fri, 6 Oct 2023 15:09:52 -0400 Subject: [PATCH] feat(SOL-103546): Implement code changes to expose context propagation methods based on ARCH --- internal/ccsmp/ccsmp_helper.h | 2 +- internal/ccsmp/ccsmp_message_tracing.go | 315 ++++++++++++++++++++++++ internal/impl/message/message_impl.go | 201 +++++++++++++++ pkg/solace/message/message.go | 32 +++ 4 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 internal/ccsmp/ccsmp_message_tracing.go diff --git a/internal/ccsmp/ccsmp_helper.h b/internal/ccsmp/ccsmp_helper.h index 0b30a0f..16a2d3e 100644 --- a/internal/ccsmp/ccsmp_helper.h +++ b/internal/ccsmp/ccsmp_helper.h @@ -49,4 +49,4 @@ typedef struct solClient_errorInfo_wrapper void * uintptr_to_void_p(solClient_uint64_t ptr); -#endif \ No newline at end of file +#endif diff --git a/internal/ccsmp/ccsmp_message_tracing.go b/internal/ccsmp/ccsmp_message_tracing.go new file mode 100644 index 0000000..612e90c --- /dev/null +++ b/internal/ccsmp/ccsmp_message_tracing.go @@ -0,0 +1,315 @@ +// pubsubplus-go-client +// +// Copyright 2021-2023 Solace Corporation. All rights reserved. +// +// Licensed 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 ccsmp + +/* +#include +#include + +#include "solclient/solClient.h" +#include "solclient/solClientMsg.h" +#include "solclient/solClientMsgTracingSupport.h" +*/ +import "C" +import ( + "fmt" + "unsafe" + + "solace.dev/go/messaging/internal/impl/logging" +) + +// Reexport of various CCSMP types + +// SolClientMessageTracingContextType is assigned a value +type SolClientMessageTracingContextType = C.solClient_msg_tracing_context_type_t + +// SolClientMessageTracingInjectionStandardType is assigned a value +type SolClientMessageTracingInjectionStandardType = C.solClient_msg_tracing_injection_standard_type_t + +// SolClientContextTypeTransportContext is assigned a value +const SolClientContextTypeTransportContext = C.TRANSPORT_CONTEXT + +// SolClientContextTypeCreationContext is assigned a value +const SolClientContextTypeCreationContext = C.CREATION_CONTEXT + +// SolClientMessageTracingInjectionStandardTypeSMF is assigned a value +const SolClientMessageTracingInjectionStandardTypeSMF = C.SOLCLIENT_INJECTION_STANDARD_SMF + +// SolClientMessageTracingInjectionStandardTypeW3C is assigned a value +const SolClientMessageTracingInjectionStandardTypeW3C = C.SOLCLIENT_INJECTION_STANDARD_W3C + +// TODO the calls to handleCcsmpError are slow since they lock the thread. +// Ideally, we wrap these calls in C such that the golang scheduler cannot +// interrupt us, and then there is no need to lock the thread. This should +// be done for all datapath functionality, ie. the contents of this file. + +// Distributed tracing properties + +// SolClientMessageGetTraceContextTraceID function +func SolClientMessageGetTraceContextTraceID(messageP SolClientMessagePt, contextType SolClientMessageTracingContextType) ([16]byte, *SolClientErrorInfoWrapper) { + // to hold the traceID property + var cChar C.solClient_uint8_t + + errorInfo := handleCcsmpError(func() SolClientReturnCode { + return C.solClient_msg_tracing_getTraceIdByte(messageP, contextType, &cChar, C.size_t(16)) + }) + if errorInfo != nil { + if errorInfo.ReturnCode == SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Encountered error fetching Creation context traceID prop: %s, subcode: %d", errorInfo.GetMessageAsString(), errorInfo.SubCode)) + } + } + + traceID := *(*[16]byte)(unsafe.Pointer(&cChar)) + return traceID, errorInfo +} + +// SolClientMessageSetTraceContextTraceID function +func SolClientMessageSetTraceContextTraceID(messageP SolClientMessagePt, traceID [16]byte, contextType SolClientMessageTracingContextType) *SolClientErrorInfoWrapper { + if len(traceID) > 0 { + cTraceID := (*C.solClient_uint8_t)(C.CBytes(traceID[:])) + + defer C.free(unsafe.Pointer(cTraceID)) // free the pointer after function executes + + errorInfo := handleCcsmpError(func() SolClientReturnCode { + return C.solClient_msg_tracing_setTraceIdByte(messageP, contextType, cTraceID, C.size_t(len(traceID))) + }) + return errorInfo + } + return nil +} + +// SolClientMessageGetTraceContextSpanID function +func SolClientMessageGetTraceContextSpanID(messageP SolClientMessagePt, contextType SolClientMessageTracingContextType) ([8]byte, *SolClientErrorInfoWrapper) { + // to hold the spanID property + var cChar C.solClient_uint8_t + + errorInfo := handleCcsmpError(func() SolClientReturnCode { + return C.solClient_msg_tracing_getSpanIdByte(messageP, contextType, &cChar, C.size_t(8)) + }) + if errorInfo != nil { + if errorInfo.ReturnCode == SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Encountered error fetching Creation context spanID prop: %s, subcode: %d", errorInfo.GetMessageAsString(), errorInfo.SubCode)) + } + } + + spanID := *(*[8]byte)(unsafe.Pointer(&cChar)) + return spanID, errorInfo +} + +// SolClientMessageSetTraceContextSpanID function +func SolClientMessageSetTraceContextSpanID(messageP SolClientMessagePt, spanID [8]byte, contextType SolClientMessageTracingContextType) *SolClientErrorInfoWrapper { + if len(spanID) > 0 { + cSpanID := (*C.solClient_uint8_t)(C.CBytes(spanID[:])) + + defer C.free(unsafe.Pointer(cSpanID)) // free the pointer after function executes + + errorInfo := handleCcsmpError(func() SolClientReturnCode { + return C.solClient_msg_tracing_setSpanIdByte(messageP, contextType, cSpanID, C.size_t(len(spanID))) + }) + return errorInfo + } + return nil +} + +// SolClientMessageGetTraceContextSampled function +func SolClientMessageGetTraceContextSampled(messageP SolClientMessagePt, contextType SolClientMessageTracingContextType) (bool, *SolClientErrorInfoWrapper) { + // to hold the Sampled property + var cSampled C.solClient_bool_t + + errorInfo := handleCcsmpError(func() SolClientReturnCode { + return C.solClient_msg_tracing_isSampled(messageP, contextType, &cSampled) + }) + if errorInfo != nil { + if errorInfo.ReturnCode == SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Encountered error fetching Creation context sampled prop: %s, subcode: %d", errorInfo.GetMessageAsString(), errorInfo.SubCode)) + } + } + + isSampled := *(*bool)(unsafe.Pointer(&cSampled)) + return isSampled, errorInfo +} + +// SolClientMessageSetTraceContextSampled function +func SolClientMessageSetTraceContextSampled(messageP SolClientMessagePt, sampled bool, contextType SolClientMessageTracingContextType) *SolClientErrorInfoWrapper { + var isSampled C.solClient_bool_t = 0 + if sampled { + isSampled = 1 + } + return handleCcsmpError(func() SolClientReturnCode { + return C.solClient_msg_tracing_setSampled(messageP, contextType, isSampled) + }) +} + +// SolClientMessageGetTraceContextTraceState function +func SolClientMessageGetTraceContextTraceState(messageP SolClientMessagePt, contextType SolClientMessageTracingContextType) (string, *SolClientErrorInfoWrapper) { + // to hold the trace state + var traceStateChar *C.char + var traceStateSize C.size_t + + errorInfo := handleCcsmpError(func() SolClientReturnCode { + return C.solClient_msg_tracing_getTraceStatePtr(messageP, contextType, &traceStateChar, &traceStateSize) + }) + if errorInfo != nil { + if errorInfo.ReturnCode == SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Encountered error fetching Creation contex traceState prop: %s, subcode: %d", errorInfo.GetMessageAsString(), errorInfo.SubCode)) + } + return "", errorInfo + } + + traceStateBytes := C.GoBytes(unsafe.Pointer(traceStateChar), C.int(traceStateSize)) + return string(traceStateBytes), errorInfo +} + +// SolClientMessageSetTraceContextTraceState function +func SolClientMessageSetTraceContextTraceState(messageP SolClientMessagePt, traceState string, contextType SolClientMessageTracingContextType) *SolClientErrorInfoWrapper { + cStr := C.CString(traceState) + defer C.free(unsafe.Pointer(cStr)) // free the pointer after function executes + errorInfo := handleCcsmpError(func() SolClientReturnCode { + return C.solClient_msg_tracing_setTraceState(messageP, contextType, cStr) + }) + return errorInfo +} + +// For the Creation Context + +// SolClientMessageGetCreationTraceContextTraceID function +func SolClientMessageGetCreationTraceContextTraceID(messageP SolClientMessagePt) ([16]byte, *SolClientErrorInfoWrapper) { + // return the traceID property for the creation trace context + return SolClientMessageGetTraceContextTraceID(messageP, SolClientContextTypeCreationContext) +} + +// SolClientMessageSetCreationTraceContextTraceID function +func SolClientMessageSetCreationTraceContextTraceID(messageP SolClientMessagePt, traceID [16]byte) *SolClientErrorInfoWrapper { + // Sets the traceID property for the creation trace context + return SolClientMessageSetTraceContextTraceID(messageP, traceID, SolClientContextTypeCreationContext) +} + +// SolClientMessageGetCreationTraceContextSpanID function +func SolClientMessageGetCreationTraceContextSpanID(messageP SolClientMessagePt) ([8]byte, *SolClientErrorInfoWrapper) { + // return the spanID property for the creation trace context + return SolClientMessageGetTraceContextSpanID(messageP, SolClientContextTypeCreationContext) +} + +// SolClientMessageSetCreationTraceContextSpanID function +func SolClientMessageSetCreationTraceContextSpanID(messageP SolClientMessagePt, spanID [8]byte) *SolClientErrorInfoWrapper { + // Sets the spanID property for the creation trace context + return SolClientMessageSetTraceContextSpanID(messageP, spanID, SolClientContextTypeCreationContext) +} + +// SolClientMessageGetCreationTraceContextSampled function +func SolClientMessageGetCreationTraceContextSampled(messageP SolClientMessagePt) (bool, *SolClientErrorInfoWrapper) { + // return the Sampled property for the creation trace context + return SolClientMessageGetTraceContextSampled(messageP, SolClientContextTypeCreationContext) +} + +// SolClientMessageSetCreationTraceContextSampled function +func SolClientMessageSetCreationTraceContextSampled(messageP SolClientMessagePt, sampled bool) *SolClientErrorInfoWrapper { + // Sets the Sampled property for the creation trace context + return SolClientMessageSetTraceContextSampled(messageP, sampled, SolClientContextTypeCreationContext) +} + +// SolClientMessageGetCreationTraceContextTraceState function +func SolClientMessageGetCreationTraceContextTraceState(messageP SolClientMessagePt) (string, *SolClientErrorInfoWrapper) { + // return the trace state property for the creation trace context + return SolClientMessageGetTraceContextTraceState(messageP, SolClientContextTypeCreationContext) +} + +// SolClientMessageSetCreationTraceContextTraceState function +func SolClientMessageSetCreationTraceContextTraceState(messageP SolClientMessagePt, traceState string) *SolClientErrorInfoWrapper { + // Sets the trace state property for the creation trace context + return SolClientMessageSetTraceContextTraceState(messageP, traceState, SolClientContextTypeCreationContext) +} + +// For the Transport Context + +// SolClientMessageGetTransportTraceContextTraceID function +func SolClientMessageGetTransportTraceContextTraceID(messageP SolClientMessagePt) ([16]byte, *SolClientErrorInfoWrapper) { + // return the traceID property for the transport trace context + return SolClientMessageGetTraceContextTraceID(messageP, SolClientContextTypeTransportContext) +} + +// SolClientMessageSetTransportTraceContextTraceID function +func SolClientMessageSetTransportTraceContextTraceID(messageP SolClientMessagePt, traceID [16]byte) *SolClientErrorInfoWrapper { + // Sets the traceID property for the transport trace context + return SolClientMessageSetTraceContextTraceID(messageP, traceID, SolClientContextTypeTransportContext) +} + +// SolClientMessageGetTransportTraceContextSpanID function +func SolClientMessageGetTransportTraceContextSpanID(messageP SolClientMessagePt) ([8]byte, *SolClientErrorInfoWrapper) { + // return the spanID property for the transport trace context + return SolClientMessageGetTraceContextSpanID(messageP, SolClientContextTypeTransportContext) +} + +// SolClientMessageSetTransportTraceContextSpanID function +func SolClientMessageSetTransportTraceContextSpanID(messageP SolClientMessagePt, spanID [8]byte) *SolClientErrorInfoWrapper { + // Sets the spanID property for the transport trace context + return SolClientMessageSetTraceContextSpanID(messageP, spanID, SolClientContextTypeTransportContext) +} + +// SolClientMessageGetTransportTraceContextSampled function +func SolClientMessageGetTransportTraceContextSampled(messageP SolClientMessagePt) (bool, *SolClientErrorInfoWrapper) { + // return the Sampled property for the transport trace context + return SolClientMessageGetTraceContextSampled(messageP, SolClientContextTypeTransportContext) +} + +// SolClientMessageSetTransportTraceContextSampled function +func SolClientMessageSetTransportTraceContextSampled(messageP SolClientMessagePt, sampled bool) *SolClientErrorInfoWrapper { + // Sets the Sampled property for the transport trace context + return SolClientMessageSetTraceContextSampled(messageP, sampled, SolClientContextTypeTransportContext) +} + +// SolClientMessageGetTransportTraceContextTraceState function +func SolClientMessageGetTransportTraceContextTraceState(messageP SolClientMessagePt) (string, *SolClientErrorInfoWrapper) { + // return the trace state property for the transport trace context + return SolClientMessageGetTraceContextTraceState(messageP, SolClientContextTypeTransportContext) +} + +// SolClientMessageSetTransportTraceContextTraceState function +func SolClientMessageSetTransportTraceContextTraceState(messageP SolClientMessagePt, traceState string) *SolClientErrorInfoWrapper { + // Sets the trace state property for the transport trace context + return SolClientMessageSetTraceContextTraceState(messageP, traceState, SolClientContextTypeTransportContext) +} + +// For the Baggage + +// SolClientMessageGetBaggage function +func SolClientMessageGetBaggage(messageP SolClientMessagePt) (string, *SolClientErrorInfoWrapper) { + var baggageChar *C.char + var baggageSize C.size_t + errorInfo := handleCcsmpError(func() SolClientReturnCode { + return C.solClient_msg_tracing_getBaggagePtr(messageP, &baggageChar, &baggageSize) + }) + if errorInfo != nil { + if errorInfo.ReturnCode == SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Encountered error fetching baggage: %s, subcode: %d", errorInfo.GetMessageAsString(), errorInfo.SubCode)) + } + return "", errorInfo + } + + baggageBytes := C.GoBytes(unsafe.Pointer(baggageChar), C.int(baggageSize)) + return string(baggageBytes), errorInfo +} + +// SolClientMessageSetBaggage function +func SolClientMessageSetBaggage(messageP SolClientMessagePt, baggage string) *SolClientErrorInfoWrapper { + cStr := C.CString(baggage) + defer C.free(unsafe.Pointer(cStr)) // free the pointer after function executes + errorInfo := handleCcsmpError(func() SolClientReturnCode { + return C.solClient_msg_tracing_setBaggage(messageP, cStr) + }) + return errorInfo +} diff --git a/internal/impl/message/message_impl.go b/internal/impl/message/message_impl.go index 2761ff8..91349ef 100644 --- a/internal/impl/message/message_impl.go +++ b/internal/impl/message/message_impl.go @@ -312,6 +312,207 @@ func (message *MessageImpl) GetClassOfService() int { return classOfService } +// GetCreationTraceContext will return the trace context metadata used for distributed message tracing message +// creation context information across service boundaries. +// It allows correlating the producer with the consumers of a message, regardless of intermediary +// instrumentation. It must not be altered by intermediaries. +// If the content is not accessible, an empty slice will be returned and the ok flag will be false. +func (message *MessageImpl) GetCreationTraceContext() (traceID [16]byte, spanID [8]byte, sampled bool, traceState string, ok bool) { + var traceIdErr, spanIdErr, sampledErr, traceStateErr core.ErrorInfo + ok = true // will remain true if we are able to retrieve all the trace context properties + // get the creation trace context properties + traceID, traceIdErr = ccsmp.SolClientMessageGetCreationTraceContextTraceID(message.messagePointer) + if traceIdErr != nil { + ok = false + if traceIdErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to retrieve Creation Context traceID property: "+traceIdErr.GetMessageAsString()+", sub code %d", traceIdErr.SubCode)) + } + } + + spanID, spanIdErr = ccsmp.SolClientMessageGetCreationTraceContextSpanID(message.messagePointer) + if spanIdErr != nil { + ok = false + if spanIdErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to retrieve Creation Context spanID property: "+spanIdErr.GetMessageAsString()+", sub code %d", spanIdErr.SubCode)) + } + } + + sampled, sampledErr = ccsmp.SolClientMessageGetCreationTraceContextSampled(message.messagePointer) + if sampledErr != nil { + ok = false + if sampledErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to retrieve Creation Context sampled property: "+sampledErr.GetMessageAsString()+", sub code %d", sampledErr.SubCode)) + } + } + + traceState, traceStateErr = ccsmp.SolClientMessageGetCreationTraceContextTraceState(message.messagePointer) + if traceStateErr != nil { + ok = false + if traceStateErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to retrieve Creation Context traceState property: "+traceStateErr.GetMessageAsString()+", sub code %d", traceStateErr.SubCode)) + } + } + + return traceID, spanID, sampled, traceState, ok +} + +// SetTraceContext will set creation trace context metadata used for distributed message tracing. +// Creation context considered to be immutable, and should not be set multiple times. +// If the content could not be set into the message, the ok flag will be false. +func (message *MessageImpl) SetCreationTraceContext(traceID [16]byte, spanID [8]byte, sampled bool, traceState string) (ok bool) { + var traceIdErr, spanIdErr, sampledErr, traceStateErr core.ErrorInfo + ok = true // will remain true if we are able to set all the trace context properties + // set the creation trace context properties + traceIdErr = ccsmp.SolClientMessageSetCreationTraceContextTraceID(message.messagePointer, traceID) + if traceIdErr != nil { + ok = false + if traceIdErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to set Creation Context traceID property: "+traceIdErr.GetMessageAsString()+", sub code %d", traceIdErr.SubCode)) + } + } + + spanIdErr = ccsmp.SolClientMessageSetCreationTraceContextSpanID(message.messagePointer, spanID) + if spanIdErr != nil { + ok = false + if spanIdErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to set Creation Context spanID property: "+spanIdErr.GetMessageAsString()+", sub code %d", spanIdErr.SubCode)) + } + } + + sampledErr = ccsmp.SolClientMessageSetCreationTraceContextSampled(message.messagePointer, sampled) + if sampledErr != nil { + ok = false + if sampledErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to set Creation Context sampled property: "+sampledErr.GetMessageAsString()+", sub code %d", sampledErr.SubCode)) + } + } + + traceStateErr = ccsmp.SolClientMessageSetCreationTraceContextTraceState(message.messagePointer, traceState) + if traceStateErr != nil { + ok = false + if traceStateErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to set Creation Context traceState property: "+traceStateErr.GetMessageAsString()+", sub code %d", traceStateErr.SubCode)) + } + } + + return ok +} + +// GetTransportTraceContext will return the trace context metadata used for distributed message tracing +// It allows correlating the producer and the consumer with an intermediary. +// It also allows correlating multiple intermediaries among each other. +// When no transport context is present it may return a creation context when available as +// an initial transport context. +// If the content is not accessible, an empty slice will be returned and the ok flag will be false. +func (message *MessageImpl) GetTransportTraceContext() (traceID [16]byte, spanID [8]byte, sampled bool, traceState string, ok bool) { + var traceIdErr, spanIdErr, sampledErr, traceStateErr core.ErrorInfo + ok = true // will remain true if we are able to retrieve all the trace context properties + // get the transport trace context properties + traceID, traceIdErr = ccsmp.SolClientMessageGetTransportTraceContextTraceID(message.messagePointer) + if traceIdErr != nil { + ok = false + if traceIdErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to retrieve Transport Context traceID property: "+traceIdErr.GetMessageAsString()+", sub code %d", traceIdErr.SubCode)) + } + } + + spanID, spanIdErr = ccsmp.SolClientMessageGetTransportTraceContextSpanID(message.messagePointer) + if spanIdErr != nil { + ok = false + if spanIdErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to retrieve Transport Context spanID property: "+spanIdErr.GetMessageAsString()+", sub code %d", spanIdErr.SubCode)) + } + } + + sampled, sampledErr = ccsmp.SolClientMessageGetTransportTraceContextSampled(message.messagePointer) + if sampledErr != nil { + ok = false + if sampledErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to retrieve Transport Context sampled property: "+sampledErr.GetMessageAsString()+", sub code %d", sampledErr.SubCode)) + } + } + + traceState, traceStateErr = ccsmp.SolClientMessageGetTransportTraceContextTraceState(message.messagePointer) + if traceStateErr != nil { + ok = false + if traceStateErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to retrieve Transport Context traceState property: "+traceStateErr.GetMessageAsString()+", sub code %d", traceStateErr.SubCode)) + } + } + + return traceID, spanID, sampled, traceState, ok +} + +// SetTraceContext will set transport trace context metadata used for distributed message tracing. +// If the content could not be set into the message, the ok flag will be false. +func (message *MessageImpl) SetTransportTraceContext(traceID [16]byte, spanID [8]byte, sampled bool, traceState string) (ok bool) { + var traceIdErr, spanIdErr, sampledErr, traceStateErr core.ErrorInfo + ok = true // will remain true if we are able to set all the trace context properties + // set the transport trace context properties + traceIdErr = ccsmp.SolClientMessageSetTransportTraceContextTraceID(message.messagePointer, traceID) + if traceIdErr != nil { + ok = false + if traceIdErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to set Transport Context traceID property: "+traceIdErr.GetMessageAsString()+", sub code %d", traceIdErr.SubCode)) + } + } + + spanIdErr = ccsmp.SolClientMessageSetTransportTraceContextSpanID(message.messagePointer, spanID) + if spanIdErr != nil { + ok = false + if spanIdErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to set Transport Context spanID property: "+spanIdErr.GetMessageAsString()+", sub code %d", spanIdErr.SubCode)) + } + } + + sampledErr = ccsmp.SolClientMessageSetTransportTraceContextSampled(message.messagePointer, sampled) + if sampledErr != nil { + ok = false + if sampledErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to set Transport Context sampled property: "+sampledErr.GetMessageAsString()+", sub code %d", sampledErr.SubCode)) + } + } + + traceStateErr = ccsmp.SolClientMessageSetTransportTraceContextTraceState(message.messagePointer, traceState) + if traceStateErr != nil { + ok = false + if traceStateErr.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to set Transport Context traceState property: "+traceStateErr.GetMessageAsString()+", sub code %d", traceStateErr.SubCode)) + } + } + + return ok +} + +// GetBaggage will return the baggage string associated with the message +// It is expected that string is UTF8 encoded. +// If the content is not accessible, an empty slice will +// be returned and the ok flag will be false. +func (message *MessageImpl) GetBaggage() (baggage string, ok bool) { + var err core.ErrorInfo + baggage, err = ccsmp.SolClientMessageGetBaggage(message.messagePointer) + if err != nil { + ok = false + if err.ReturnCode == ccsmp.SolClientReturnCodeFail { + logging.Default.Warning(fmt.Sprintf("Failed to retrieve Baggage: "+err.GetMessageAsString()+", sub code %d", err.SubCode)) + } + } else { + ok = true + } + return baggage, ok +} + +// SetBaggage will set the baggage string associated with the message +// It is expected that string is UTF8 encoded. +func (message *MessageImpl) SetBaggage(baggage string) error { + err := ccsmp.SolClientMessageSetBaggage(message.messagePointer, baggage) + if err != nil { + logging.Default.Warning(fmt.Sprintf("Failed to set the Baggage: "+err.GetMessageAsString()+", sub code %d", err.SubCode)) + return core.ToNativeError(err, "error setting baggage: ") + } + return nil +} + func (message *MessageImpl) String() string { return ccsmp.SolClientMessageDump(message.messagePointer) } diff --git a/pkg/solace/message/message.go b/pkg/solace/message/message.go index 6f1a82f..ffc47e0 100644 --- a/pkg/solace/message/message.go +++ b/pkg/solace/message/message.go @@ -114,6 +114,38 @@ type Message interface { // 2 | COS_3 GetClassOfService() (cos int) + // GetCreationTraceContext will return the trace context metadata used for distributed message tracing message + // creation context information across service boundaries. + // It allows correlating the producer with the consumers of a message, regardless of intermediary + // instrumentation. It must not be altered by intermediaries. + // If the content is not accessible, an empty slice will be returned and the ok flag will be false. + GetCreationTraceContext() (traceID [16]byte, spanID [8]byte, sampled bool, traceState string, ok bool) + + // SetTraceContext will set creation trace context metadata used for distributed message tracing. + // Creation context considered to be immutable, and should not be set multiple times. + // If the content could not be set into the message, the ok flag will be false. + SetCreationTraceContext(traceID [16]byte, spanID [8]byte, sampled bool, traceState string) (ok bool) + + // GetTransportTraceContext will return the trace context metadata used for distributed message tracing + // It allows correlating the producer and the consumer with an intermediary. + // It also allows correlating multiple intermediaries among each other. + // When no transport context is present it may return a creation context when available as + // an initial transport context. + // If the content is not accessible, an empty slice will be returned and the ok flag will be false. + GetTransportTraceContext() (traceID [16]byte, spanID [8]byte, sampled bool, traceState string, ok bool) + + // SetTraceContext will set transport trace context metadata used for distributed message tracing. + // If the content could not be set into the message, the ok flag will be false. + SetTransportTraceContext(traceID [16]byte, spanID [8]byte, sampled bool, traceState string) (ok bool) + + // GetBaggage will return the baggage string associated with the message + // If the content is not accessible, an empty slice will + // be returned and the ok flag will be false. + GetBaggage() (baggage string, ok bool) + + // SetBaggage will set the baggage string associated with the message + SetBaggage(baggage string) error + // String implements fmt.Stringer. Prints the message as a string. A truncated response // may be returned when large payloads or properties are attached. String() string