@@ -10,6 +10,9 @@ import datadog.trace.api.Config
1010import datadog.trace.api.Pair
1111import datadog.trace.api.ProcessTags
1212import datadog.trace.api.WellKnownTags
13+ import datadog.trace.api.git.CommitInfo
14+ import datadog.trace.api.git.GitInfo
15+ import datadog.trace.api.git.GitInfoProvider
1316import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString
1417import datadog.trace.test.util.DDSpecification
1518import java.nio.ByteBuffer
@@ -25,8 +28,8 @@ class SerializingMetricWriterTest extends DDSpecification {
2528
2629 def " should produce correct message #iterationIndex with process tags enabled #withProcessTags" () {
2730 setup :
28- if (withProcessTags) {
29- injectSysConfig(EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED , " true " )
31+ if (! withProcessTags) {
32+ injectSysConfig(EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED , " false " )
3033 }
3134 ProcessTags . reset()
3235 long startTime = MILLISECONDS . toNanos(System . currentTimeMillis())
@@ -45,6 +48,9 @@ class SerializingMetricWriterTest extends DDSpecification {
4548 then :
4649 sink. validatedInput()
4750
51+ cleanup :
52+ removeSysConfig(EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED )
53+ ProcessTags . reset()
4854
4955 where :
5056 content << [
@@ -136,8 +142,105 @@ class SerializingMetricWriterTest extends DDSpecification {
136142 withProcessTags << [true , false ]
137143 }
138144
145+ def " ServiceSource optional in the payload" () {
146+ setup :
147+ long startTime = MILLISECONDS . toNanos(System . currentTimeMillis())
148+ long duration = SECONDS . toNanos(10 )
149+ WellKnownTags wellKnownTags = new WellKnownTags (" runtimeid" , " hostname" , " env" , " service" , " version" , " language" )
150+
151+ // Create keys with different combinations of HTTP fields
152+ def keyWithNoSource = new MetricKey (" resource" , " service" , " operation" , null , " type" , 200 , false , false , " server" , [], " GET" , " /api/users" , null )
153+ def keyWithSource = new MetricKey (" resource" , " service" , " operation" , " source" , " type" , 200 , false , false , " server" , [], " POST" , null , null )
154+
155+ def content = [
156+ Pair . of(keyWithNoSource, new AggregateMetric (). recordDurations(1 , new AtomicLongArray (1L ))),
157+ Pair . of(keyWithSource, new AggregateMetric (). recordDurations(1 , new AtomicLongArray (1L ))),
158+ ]
159+
160+ ValidatingSink sink = new ValidatingSink (wellKnownTags, startTime, duration, content)
161+ SerializingMetricWriter writer = new SerializingMetricWriter (wellKnownTags, sink, 128 )
162+
163+ when :
164+ writer. startBucket(content. size(), startTime, duration)
165+ for (Pair<MetricKey , AggregateMetric > pair : content) {
166+ writer. add(pair. getLeft(), pair. getRight())
167+ }
168+ writer. finishBucket()
169+
170+ then :
171+ sink. validatedInput()
172+ }
173+
174+ def " HTTPMethod and HTTPEndpoint fields are optional in payload" () {
175+ setup :
176+ long startTime = MILLISECONDS . toNanos(System . currentTimeMillis())
177+ long duration = SECONDS . toNanos(10 )
178+ WellKnownTags wellKnownTags = new WellKnownTags (" runtimeid" , " hostname" , " env" , " service" , " version" , " language" )
179+
180+ // Create keys with different combinations of HTTP fields
181+ def keyWithBoth = new MetricKey (" resource" , " service" , " operation" , null , " type" , 200 , false , false , " server" , [], " GET" , " /api/users" , null )
182+ def keyWithMethodOnly = new MetricKey (" resource" , " service" , " operation" , null , " type" , 200 , false , false , " server" , [], " POST" , null ,null )
183+ def keyWithEndpointOnly = new MetricKey (" resource" , " service" , " operation" , null , " type" , 200 , false , false , " server" , [], null , " /api/orders" ,null )
184+ def keyWithNeither = new MetricKey (" resource" , " service" , " operation" , null , " type" , 200 , false , false , " client" , [], null , null , null )
185+
186+ def content = [
187+ Pair . of(keyWithBoth, new AggregateMetric (). recordDurations(1 , new AtomicLongArray (1L ))),
188+ Pair . of(keyWithMethodOnly, new AggregateMetric (). recordDurations(1 , new AtomicLongArray (1L ))),
189+ Pair . of(keyWithEndpointOnly, new AggregateMetric (). recordDurations(1 , new AtomicLongArray (1L ))),
190+ Pair . of(keyWithNeither, new AggregateMetric (). recordDurations(1 , new AtomicLongArray (1L )))
191+ ]
192+
193+ ValidatingSink sink = new ValidatingSink (wellKnownTags, startTime, duration, content)
194+ SerializingMetricWriter writer = new SerializingMetricWriter (wellKnownTags, sink, 128 )
195+
196+ when :
197+ writer. startBucket(content. size(), startTime, duration)
198+ for (Pair<MetricKey , AggregateMetric > pair : content) {
199+ writer. add(pair. getLeft(), pair. getRight())
200+ }
201+ writer. finishBucket()
202+
203+ then :
204+ sink. validatedInput()
205+ // Test passes if validation in ValidatingSink succeeds
206+ // ValidatingSink verifies that map size matches actual number of fields
207+ // and that HTTPMethod/HTTPEndpoint are only present when non-empty
208+ }
209+
210+ def " add git sha commit info when sha commit is #shaCommit" () {
211+ setup :
212+ GitInfoProvider gitInfoProvider = Mock (GitInfoProvider )
213+ gitInfoProvider. getGitInfo() >> new GitInfo (null , null , null , new CommitInfo (shaCommit))
139214
140- class ValidatingSink implements Sink {
215+ long startTime = MILLISECONDS . toNanos(System . currentTimeMillis())
216+ long duration = SECONDS . toNanos(10 )
217+ WellKnownTags wellKnownTags = new WellKnownTags (" runtimeid" , " hostname" , " env" , " service" , " version" , " language" )
218+
219+ // Create keys with different combinations of HTTP fields
220+ def key = new MetricKey (" resource" , " service" , " operation" , null , " type" , 200 , false , false , " server" , [], " GET" , " /api/users" , null )
221+
222+ def content = [Pair . of(key, new AggregateMetric (). recordDurations(1 , new AtomicLongArray (1L ))),]
223+
224+ ValidatingSink sink = new ValidatingSink (wellKnownTags, startTime, duration, content)
225+ SerializingMetricWriter writer = new SerializingMetricWriter (wellKnownTags, sink, 128 , gitInfoProvider)
226+
227+ when :
228+
229+ writer. startBucket(content. size(), startTime, duration)
230+ for (Pair<MetricKey , AggregateMetric > pair : content) {
231+ writer. add(pair. getLeft(), pair. getRight())
232+ }
233+ writer. finishBucket()
234+
235+ then :
236+
237+ sink. validatedInput()
238+
239+ where :
240+ shaCommit << [null , " 123456" ]
241+ }
242+
243+ static class ValidatingSink implements Sink {
141244
142245 private final WellKnownTags wellKnownTags
143246 private final long startTimeNanos
@@ -161,7 +264,9 @@ class SerializingMetricWriterTest extends DDSpecification {
161264 void accept (int messageCount , ByteBuffer buffer ) {
162265 MessageUnpacker unpacker = MessagePack . newDefaultUnpacker(buffer)
163266 int mapSize = unpacker. unpackMapHeader()
164- assert mapSize == (7 + (Config . get(). isExperimentalPropagateProcessTagsEnabled() ? 1 : 0 ))
267+ String gitCommitSha = GitInfoProvider . INSTANCE . getGitInfo()?. getCommit()?. getSha()
268+ assert mapSize == (7 + (Config . get(). isExperimentalPropagateProcessTagsEnabled() ? 1 : 0 )
269+ + (gitCommitSha != null ? 1 : 0 ))
165270 assert unpacker. unpackString() == " RuntimeID"
166271 assert unpacker. unpackString() == wellKnownTags. getRuntimeId() as String
167272 assert unpacker. unpackString() == " Sequence"
@@ -178,6 +283,10 @@ class SerializingMetricWriterTest extends DDSpecification {
178283 assert unpacker. unpackString() == " ProcessTags"
179284 assert unpacker. unpackString() == ProcessTags . tagsForSerialization as String
180285 }
286+ if (gitCommitSha != null ) {
287+ assert unpacker. unpackString() == " GitCommitSha"
288+ assert unpacker. unpackString() == gitCommitSha
289+ }
181290 assert unpacker. unpackString() == " Stats"
182291 int outerLength = unpacker. unpackArrayHeader()
183292 assert outerLength == 1
0 commit comments