@@ -11,6 +11,7 @@ import (
1111 "net/http"
1212 "net/http/httptest"
1313 "os"
14+ "strconv"
1415 "strings"
1516 "testing"
1617 "time"
@@ -1844,3 +1845,117 @@ func (rt *tracedRT) RoundTrip(req *http.Request) (*http.Response, error) {
18441845func ptrFromVal [T any ](v T ) * T {
18451846 return & v
18461847}
1848+
1849+ func TestDDAttributes (t * testing.T ) {
1850+ t .Run ("regular-span" , func (t * testing.T ) {
1851+ tt , ll := testTracer (t )
1852+ ctx := context .Background ()
1853+
1854+ span , _ := ll .StartSpan (ctx , llmobs .SpanKindLLM , "test-llm" , llmobs.StartSpanConfig {})
1855+ span .Finish (llmobs.FinishSpanConfig {})
1856+
1857+ apmSpans := tt .WaitForSpans (t , 1 )
1858+ llmSpans := tt .WaitForLLMObsSpans (t , 1 )
1859+
1860+ apmSpan := apmSpans [0 ]
1861+ llmSpan := llmSpans [0 ]
1862+
1863+ assert .NotEmpty (t , llmSpan .DDAttributes .SpanID , "DDAttributes.SpanID should be populated" )
1864+ assert .NotEmpty (t , llmSpan .DDAttributes .TraceID , "DDAttributes.TraceID should be populated" )
1865+ assert .NotEmpty (t , llmSpan .DDAttributes .APMTraceID , "DDAttributes.APMTraceID should be populated" )
1866+
1867+ assert .Equal (t , llmSpan .SpanID , llmSpan .DDAttributes .SpanID , "DDAttributes.SpanID should match SpanID" )
1868+ assert .Equal (t , llmSpan .TraceID , llmSpan .DDAttributes .TraceID , "DDAttributes.TraceID should match TraceID" )
1869+ assert .NotEqual (t , llmSpan .DDAttributes .TraceID , llmSpan .DDAttributes .APMTraceID , "LLMObs trace ID should differ from DDAttributes.APMTraceID" )
1870+
1871+ // compare only the lower 64 bits of the trace ID
1872+ low64Hex := llmSpan .DDAttributes .APMTraceID [len (llmSpan .DDAttributes .APMTraceID )- 16 :]
1873+ low64HexUint , err := strconv .ParseUint (low64Hex , 16 , 64 )
1874+ require .NoError (t , err )
1875+ assert .Equal (t , apmSpan .TraceID , low64HexUint , "APM trace ID should match DDAttributes.APMTraceID" )
1876+
1877+ // Verify Scope is empty for regular spans
1878+ assert .Empty (t , llmSpan .DDAttributes .Scope , "DDAttributes.Scope should be empty for regular spans" )
1879+ })
1880+ t .Run ("experiment-span" , func (t * testing.T ) {
1881+ tt , ll := testTracer (t )
1882+ ctx := context .Background ()
1883+
1884+ experimentID := "test-experiment-123"
1885+ span , _ := ll .StartExperimentSpan (ctx , "test-experiment" , experimentID , llmobs.StartSpanConfig {})
1886+ span .Finish (llmobs.FinishSpanConfig {})
1887+
1888+ apmSpans := tt .WaitForSpans (t , 1 )
1889+ llmSpans := tt .WaitForLLMObsSpans (t , 1 )
1890+
1891+ apmSpan := apmSpans [0 ]
1892+ llmSpan := llmSpans [0 ]
1893+
1894+ assert .NotEmpty (t , llmSpan .DDAttributes .SpanID , "DDAttributes.SpanID should be populated" )
1895+ assert .NotEmpty (t , llmSpan .DDAttributes .TraceID , "DDAttributes.TraceID should be populated" )
1896+ assert .NotEmpty (t , llmSpan .DDAttributes .APMTraceID , "DDAttributes.APMTraceID should be populated" )
1897+
1898+ assert .Equal (t , llmSpan .SpanID , llmSpan .DDAttributes .SpanID , "DDAttributes.SpanID should match SpanID" )
1899+ assert .Equal (t , llmSpan .TraceID , llmSpan .DDAttributes .TraceID , "DDAttributes.TraceID should match TraceID" )
1900+ assert .NotEqual (t , llmSpan .DDAttributes .TraceID , llmSpan .DDAttributes .APMTraceID , "LLMObs trace ID should differ from DDAttributes.APMTraceID" )
1901+
1902+ assertAPMTraceID (t , apmSpan , llmSpan )
1903+
1904+ // Verify Scope is set to "experiments"
1905+ assert .Equal (t , "experiments" , llmSpan .DDAttributes .Scope , "DDAttributes.Scope should be 'experiments' for experiment spans" )
1906+ })
1907+ t .Run ("child-span-trace-ids" , func (t * testing.T ) {
1908+ tt , ll := testTracer (t )
1909+ ctx := context .Background ()
1910+
1911+ parentSpan , ctx := ll .StartSpan (ctx , llmobs .SpanKindWorkflow , "parent-workflow" , llmobs.StartSpanConfig {})
1912+ childSpan , _ := ll .StartSpan (ctx , llmobs .SpanKindLLM , "child-llm" , llmobs.StartSpanConfig {})
1913+
1914+ childSpan .Finish (llmobs.FinishSpanConfig {})
1915+ parentSpan .Finish (llmobs.FinishSpanConfig {})
1916+
1917+ apmSpans := tt .WaitForSpans (t , 2 )
1918+ llmSpans := tt .WaitForLLMObsSpans (t , 2 )
1919+
1920+ var parentLLM , childLLM * llmobstransport.LLMObsSpanEvent
1921+ for i := range llmSpans {
1922+ if llmSpans [i ].Name == "parent-workflow" {
1923+ parentLLM = & llmSpans [i ]
1924+ } else if llmSpans [i ].Name == "child-llm" {
1925+ childLLM = & llmSpans [i ]
1926+ }
1927+ }
1928+
1929+ var parentAPM , childAPM * testtracer.Span
1930+ for i := range apmSpans {
1931+ if apmSpans [i ].Name == "parent-workflow" {
1932+ parentAPM = & apmSpans [i ]
1933+ } else if apmSpans [i ].Name == "child-llm" {
1934+ childAPM = & apmSpans [i ]
1935+ }
1936+ }
1937+
1938+ require .NotNil (t , parentLLM , "Parent LLM span should exist" )
1939+ require .NotNil (t , childLLM , "Child LLM span should exist" )
1940+ require .NotNil (t , parentAPM , "Parent APM span should exist" )
1941+ require .NotNil (t , childAPM , "Child APM span should exist" )
1942+
1943+ assert .Equal (t , parentLLM .DDAttributes .TraceID , childLLM .DDAttributes .TraceID ,
1944+ "Parent and child should have the same LLMObs trace ID in DDAttributes" )
1945+ assert .Equal (t , parentLLM .DDAttributes .APMTraceID , childLLM .DDAttributes .APMTraceID ,
1946+ "Parent and child should have the same APM trace ID in DDAttributes" )
1947+ assert .NotEqual (t , parentLLM .DDAttributes .TraceID , parentLLM .DDAttributes .APMTraceID ,
1948+ "LLMObs trace ID should differ from APM trace ID" )
1949+
1950+ assertAPMTraceID (t , * parentAPM , * parentLLM )
1951+ assertAPMTraceID (t , * childAPM , * childLLM )
1952+ })
1953+ }
1954+
1955+ func assertAPMTraceID (t * testing.T , apmSpan testtracer.Span , llmSpan llmobstransport.LLMObsSpanEvent ) {
1956+ // compare only the lower 64 bits of the trace ID
1957+ low64Hex := llmSpan .DDAttributes .APMTraceID [len (llmSpan .DDAttributes .APMTraceID )- 16 :]
1958+ low64HexUint , err := strconv .ParseUint (low64Hex , 16 , 64 )
1959+ require .NoError (t , err )
1960+ assert .Equal (t , apmSpan .TraceID , low64HexUint , "APM trace ID should match DDAttributes.APMTraceID" )
1961+ }
0 commit comments