Skip to content

Commit 544eb98

Browse files
g7ed6eLiudmila Molkova
andauthored
Add a process span to ConfluentKafka instrumentation (#1937)
Co-authored-by: Liudmila Molkova <[email protected]>
1 parent bc27d71 commit 544eb98

File tree

11 files changed

+289
-47
lines changed

11 files changed

+289
-47
lines changed

build/Common.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<OpenTelemetryCoreLatestVersion>[1.9.0,2.0)</OpenTelemetryCoreLatestVersion>
4444
<OpenTelemetryCoreLatestPrereleaseVersion>[1.9.0-rc.1]</OpenTelemetryCoreLatestPrereleaseVersion>
4545
<StackExchangeRedisPkgVer>[2.6.122,3.0)</StackExchangeRedisPkgVer>
46-
<ConfluentKafkaPkgVer>[2.3.0,3.0)</ConfluentKafkaPkgVer>
46+
<ConfluentKafkaPkgVer>[2.4.0,3.0)</ConfluentKafkaPkgVer>
4747
<CassandraCSharpDriverPkgVer>[3.16.0,4.0)</CassandraCSharpDriverPkgVer>
4848
<StyleCopAnalyzersPkgVer>[1.2.0-beta.507,2.0)</StyleCopAnalyzersPkgVer>
4949
<SystemNetHttp>[4.3.4,)</SystemNetHttp>

src/OpenTelemetry.Instrumentation.ConfluentKafka/.publicApi/PublicAPI.Unshipped.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
Confluent.Kafka.OpenTelemetryConsumeAndProcessMessageHandler<TKey, TValue>
2+
Confluent.Kafka.OpenTelemetryConsumeResultExtensions
13
OpenTelemetry.Instrumentation.ConfluentKafka.InstrumentedConsumerBuilder<TKey, TValue>
24
OpenTelemetry.Instrumentation.ConfluentKafka.InstrumentedConsumerBuilder<TKey, TValue>.InstrumentedConsumerBuilder(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string!, string!>>! config) -> void
35
OpenTelemetry.Instrumentation.ConfluentKafka.InstrumentedProducerBuilder<TKey, TValue>
@@ -6,6 +8,9 @@ OpenTelemetry.Metrics.MeterProviderBuilderExtensions
68
OpenTelemetry.Trace.TracerProviderBuilderExtensions
79
override OpenTelemetry.Instrumentation.ConfluentKafka.InstrumentedConsumerBuilder<TKey, TValue>.Build() -> Confluent.Kafka.IConsumer<TKey, TValue>!
810
override OpenTelemetry.Instrumentation.ConfluentKafka.InstrumentedProducerBuilder<TKey, TValue>.Build() -> Confluent.Kafka.IProducer<TKey, TValue>!
11+
static Confluent.Kafka.OpenTelemetryConsumeResultExtensions.ConsumeAndProcessMessageAsync<TKey, TValue>(this Confluent.Kafka.IConsumer<TKey, TValue>! consumer, Confluent.Kafka.OpenTelemetryConsumeAndProcessMessageHandler<TKey, TValue>! handler) -> System.Threading.Tasks.ValueTask<Confluent.Kafka.ConsumeResult<TKey, TValue>?>
12+
static Confluent.Kafka.OpenTelemetryConsumeResultExtensions.ConsumeAndProcessMessageAsync<TKey, TValue>(this Confluent.Kafka.IConsumer<TKey, TValue>! consumer, Confluent.Kafka.OpenTelemetryConsumeAndProcessMessageHandler<TKey, TValue>! handler, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask<Confluent.Kafka.ConsumeResult<TKey, TValue>?>
13+
static Confluent.Kafka.OpenTelemetryConsumeResultExtensions.TryExtractPropagationContext<TKey, TValue>(this Confluent.Kafka.ConsumeResult<TKey, TValue>! consumeResult, out OpenTelemetry.Context.Propagation.PropagationContext propagationContext) -> bool
914
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddKafkaConsumerInstrumentation<TKey, TValue>(this OpenTelemetry.Metrics.MeterProviderBuilder! builder) -> OpenTelemetry.Metrics.MeterProviderBuilder!
1015
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddKafkaConsumerInstrumentation<TKey, TValue>(this OpenTelemetry.Metrics.MeterProviderBuilder! builder, OpenTelemetry.Instrumentation.ConfluentKafka.InstrumentedConsumerBuilder<TKey, TValue>! consumerBuilder) -> OpenTelemetry.Metrics.MeterProviderBuilder!
1116
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddKafkaConsumerInstrumentation<TKey, TValue>(this OpenTelemetry.Metrics.MeterProviderBuilder! builder, string? name, OpenTelemetry.Instrumentation.ConfluentKafka.InstrumentedConsumerBuilder<TKey, TValue>? consumerBuilder) -> OpenTelemetry.Metrics.MeterProviderBuilder!
@@ -18,3 +23,4 @@ static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddKafkaConsumerInstr
1823
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddKafkaProducerInstrumentation<TKey, TValue>(this OpenTelemetry.Trace.TracerProviderBuilder! builder) -> OpenTelemetry.Trace.TracerProviderBuilder!
1924
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddKafkaProducerInstrumentation<TKey, TValue>(this OpenTelemetry.Trace.TracerProviderBuilder! builder, OpenTelemetry.Instrumentation.ConfluentKafka.InstrumentedProducerBuilder<TKey, TValue>! producerBuilder) -> OpenTelemetry.Trace.TracerProviderBuilder!
2025
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddKafkaProducerInstrumentation<TKey, TValue>(this OpenTelemetry.Trace.TracerProviderBuilder! builder, string? name, OpenTelemetry.Instrumentation.ConfluentKafka.InstrumentedProducerBuilder<TKey, TValue>? producerBuilder) -> OpenTelemetry.Trace.TracerProviderBuilder!
26+
virtual Confluent.Kafka.OpenTelemetryConsumeAndProcessMessageHandler<TKey, TValue>.Invoke(Confluent.Kafka.ConsumeResult<TKey, TValue>! consumeResult, System.Diagnostics.Activity? activity, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask

src/OpenTelemetry.Instrumentation.ConfluentKafka/ConfluentKafkaCommon.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ namespace OpenTelemetry.Instrumentation.ConfluentKafka;
1010

1111
internal static class ConfluentKafkaCommon
1212
{
13+
internal const string ReceiveOperationName = "receive";
14+
internal const string ProcessOperationName = "process";
15+
internal const string KafkaMessagingSystem = "kafka";
16+
internal const string PublishOperationName = "publish";
17+
1318
internal static readonly string InstrumentationName = typeof(ConfluentKafkaCommon).Assembly.GetName().Name!;
1419
internal static readonly string InstrumentationVersion = typeof(ConfluentKafkaCommon).Assembly.GetPackageVersion();
1520
internal static readonly ActivitySource ActivitySource = new(InstrumentationName, InstrumentationVersion);

src/OpenTelemetry.Instrumentation.ConfluentKafka/InstrumentedConsumer.cs

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
using System.Diagnostics;
5-
using System.Text;
65
using Confluent.Kafka;
76
using OpenTelemetry.Context.Propagation;
87
using OpenTelemetry.Trace;
@@ -11,8 +10,6 @@ namespace OpenTelemetry.Instrumentation.ConfluentKafka;
1110

1211
internal class InstrumentedConsumer<TKey, TValue> : IConsumer<TKey, TValue>
1312
{
14-
private const string ReceiveOperationName = "receive";
15-
private const string KafkaMessagingSystem = "kafka";
1613
private readonly IConsumer<TKey, TValue> consumer;
1714
private readonly ConfluentKafkaConsumerInstrumentationOptions<TKey, TValue> options;
1815

@@ -265,17 +262,6 @@ public void Close()
265262
private static string FormatConsumeException(ConsumeException consumeException) =>
266263
$"ConsumeException: {consumeException.Error}";
267264

268-
private static PropagationContext ExtractPropagationContext(Headers? headers)
269-
=> Propagators.DefaultTextMapPropagator.Extract(default, headers, ExtractTraceContext);
270-
271-
private static IEnumerable<string> ExtractTraceContext(Headers? headers, string value)
272-
{
273-
if (headers?.TryGetLastBytes(value, out var bytes) == true)
274-
{
275-
yield return Encoding.UTF8.GetString(bytes);
276-
}
277-
}
278-
279265
private static ConsumeResult ExtractConsumeResult(ConsumeResult<TKey, TValue> result) => result switch
280266
{
281267
null => new ConsumeResult(null, null),
@@ -296,10 +282,10 @@ private static void GetTags(string topic, out TagList tags, int? partition = nul
296282
{
297283
new KeyValuePair<string, object?>(
298284
SemanticConventions.AttributeMessagingOperation,
299-
ReceiveOperationName),
285+
ConfluentKafkaCommon.ReceiveOperationName),
300286
new KeyValuePair<string, object?>(
301287
SemanticConventions.AttributeMessagingSystem,
302-
KafkaMessagingSystem),
288+
ConfluentKafkaCommon.KafkaMessagingSystem),
303289
new KeyValuePair<string, object?>(
304290
SemanticConventions.AttributeMessagingDestinationName,
305291
topic),
@@ -335,7 +321,7 @@ private void InstrumentConsumption(DateTimeOffset startTime, DateTimeOffset endT
335321
if (this.options.Traces)
336322
{
337323
PropagationContext propagationContext = consumeResult.Headers != null
338-
? ExtractPropagationContext(consumeResult.Headers)
324+
? OpenTelemetryConsumeResultExtensions.ExtractPropagationContext(consumeResult.Headers)
339325
: default;
340326

341327
using Activity? activity = this.StartReceiveActivity(propagationContext, startTime, consumeResult.TopicPartitionOffset, consumeResult.Key);
@@ -364,8 +350,8 @@ private void InstrumentConsumption(DateTimeOffset startTime, DateTimeOffset endT
364350
private Activity? StartReceiveActivity(PropagationContext propagationContext, DateTimeOffset start, TopicPartitionOffset? topicPartitionOffset, object? key)
365351
{
366352
var spanName = string.IsNullOrEmpty(topicPartitionOffset?.Topic)
367-
? ReceiveOperationName
368-
: string.Concat(topicPartitionOffset!.Topic, " ", ReceiveOperationName);
353+
? ConfluentKafkaCommon.ReceiveOperationName
354+
: string.Concat(topicPartitionOffset!.Topic, " ", ConfluentKafkaCommon.ReceiveOperationName);
369355

370356
ActivityLink[] activityLinks = propagationContext.ActivityContext.IsValid()
371357
? new[] { new ActivityLink(propagationContext.ActivityContext) }
@@ -374,13 +360,13 @@ private void InstrumentConsumption(DateTimeOffset startTime, DateTimeOffset endT
374360
Activity? activity = ConfluentKafkaCommon.ActivitySource.StartActivity(spanName, kind: ActivityKind.Consumer, links: activityLinks, startTime: start, parentContext: default);
375361
if (activity?.IsAllDataRequested == true)
376362
{
377-
activity.SetTag(SemanticConventions.AttributeMessagingSystem, KafkaMessagingSystem);
363+
activity.SetTag(SemanticConventions.AttributeMessagingSystem, ConfluentKafkaCommon.KafkaMessagingSystem);
378364
activity.SetTag(SemanticConventions.AttributeMessagingClientId, this.Name);
379365
activity.SetTag(SemanticConventions.AttributeMessagingDestinationName, topicPartitionOffset?.Topic);
380366
activity.SetTag(SemanticConventions.AttributeMessagingKafkaDestinationPartition, topicPartitionOffset?.Partition.Value);
381367
activity.SetTag(SemanticConventions.AttributeMessagingKafkaMessageOffset, topicPartitionOffset?.Offset.Value);
382368
activity.SetTag(SemanticConventions.AttributeMessagingKafkaConsumerGroup, this.GroupId);
383-
activity.SetTag(SemanticConventions.AttributeMessagingOperation, ReceiveOperationName);
369+
activity.SetTag(SemanticConventions.AttributeMessagingOperation, ConfluentKafkaCommon.ReceiveOperationName);
384370
if (key != null)
385371
{
386372
activity.SetTag(SemanticConventions.AttributeMessagingKafkaMessageKey, key);

src/OpenTelemetry.Instrumentation.ConfluentKafka/InstrumentedConsumerBuilder.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,8 @@ public override IConsumer<TKey, TValue> Build()
3333
Debug.Assert(this.Options != null, "Options should not be null.");
3434

3535
ConsumerConfig config = (ConsumerConfig)this.Config;
36-
if (this.Options!.Metrics)
37-
{
38-
config.StatisticsIntervalMs ??= 1000;
39-
}
4036

41-
var consumer = new InstrumentedConsumer<TKey, TValue>(base.Build(), this.Options);
37+
var consumer = new InstrumentedConsumer<TKey, TValue>(base.Build(), this.Options!);
4238
consumer.GroupId = config.GroupId;
4339

4440
return consumer;

src/OpenTelemetry.Instrumentation.ConfluentKafka/InstrumentedProducer.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ namespace OpenTelemetry.Instrumentation.ConfluentKafka;
1111

1212
internal sealed class InstrumentedProducer<TKey, TValue> : IProducer<TKey, TValue>
1313
{
14-
private const string PublishOperationName = "publish";
15-
private const string KafkaMessagingSystem = "kafka";
16-
1714
private readonly TextMapPropagator propagator = Propagators.DefaultTextMapPropagator;
1815
private readonly IProducer<TKey, TValue> producer;
1916
private readonly ConfluentKafkaProducerInstrumentationOptions<TKey, TValue> options;
@@ -285,10 +282,10 @@ private static void GetTags(string topic, out TagList tags, int? partition = nul
285282
{
286283
new KeyValuePair<string, object?>(
287284
SemanticConventions.AttributeMessagingOperation,
288-
PublishOperationName),
285+
ConfluentKafkaCommon.PublishOperationName),
289286
new KeyValuePair<string, object?>(
290287
SemanticConventions.AttributeMessagingSystem,
291-
KafkaMessagingSystem),
288+
ConfluentKafkaCommon.KafkaMessagingSystem),
292289
new KeyValuePair<string, object?>(
293290
SemanticConventions.AttributeMessagingDestinationName,
294291
topic),
@@ -329,7 +326,7 @@ private static void RecordPublish(TopicPartition topicPartition, TimeSpan durati
329326

330327
private Activity? StartPublishActivity(DateTimeOffset start, string topic, Message<TKey, TValue> message, int? partition = null)
331328
{
332-
var spanName = string.Concat(topic, " ", PublishOperationName);
329+
var spanName = string.Concat(topic, " ", ConfluentKafkaCommon.PublishOperationName);
333330
var activity = ConfluentKafkaCommon.ActivitySource.StartActivity(name: spanName, kind: ActivityKind.Producer, startTime: start);
334331
if (activity == null)
335332
{
@@ -338,10 +335,10 @@ private static void RecordPublish(TopicPartition topicPartition, TimeSpan durati
338335

339336
if (activity.IsAllDataRequested)
340337
{
341-
activity.SetTag(SemanticConventions.AttributeMessagingSystem, KafkaMessagingSystem);
338+
activity.SetTag(SemanticConventions.AttributeMessagingSystem, ConfluentKafkaCommon.KafkaMessagingSystem);
342339
activity.SetTag(SemanticConventions.AttributeMessagingClientId, this.Name);
343340
activity.SetTag(SemanticConventions.AttributeMessagingDestinationName, topic);
344-
activity.SetTag(SemanticConventions.AttributeMessagingOperation, PublishOperationName);
341+
activity.SetTag(SemanticConventions.AttributeMessagingOperation, ConfluentKafkaCommon.PublishOperationName);
345342

346343
if (message.Key != null)
347344
{

src/OpenTelemetry.Instrumentation.ConfluentKafka/InstrumentedProducerBuilder.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,6 @@ public override IProducer<TKey, TValue> Build()
3232
{
3333
Debug.Assert(this.Options != null, "Options should not be null.");
3434

35-
ProducerConfig config = (ProducerConfig)this.Config;
36-
if (this.Options!.Metrics)
37-
{
38-
config.StatisticsIntervalMs ??= 1000;
39-
}
40-
41-
return new InstrumentedProducer<TKey, TValue>(base.Build(), this.Options);
35+
return new InstrumentedProducer<TKey, TValue>(base.Build(), this.Options!);
4236
}
4337
}

src/OpenTelemetry.Instrumentation.ConfluentKafka/OpenTelemetry.Instrumentation.ConfluentKafka.csproj

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,4 @@
2727
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPkgVer)" />
2828
</ItemGroup>
2929

30-
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
31-
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />
32-
<PackageReference Include="System.Text.Json" Version="8.0.4" />
33-
</ItemGroup>
34-
3530
</Project>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using System.Diagnostics;
5+
6+
namespace Confluent.Kafka;
7+
8+
/// <summary>
9+
/// An asynchronous action to process the <see cref="ConsumeResult{TKey,TValue}"/>.
10+
/// </summary>
11+
/// <param name="consumeResult">The <see cref="ConsumeResult{TKey,TValue}"/>.</param>
12+
/// <param name="activity">The <see cref="Activity"/>.</param>
13+
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/>.</param>
14+
/// <typeparam name="TKey">The type of key of the <see cref="ConsumeResult{TKey,TValue}"/>.</typeparam>
15+
/// <typeparam name="TValue">The type of value of the <see cref="ConsumeResult{TKey,TValue}"/>.</typeparam>
16+
/// <returns>A <see cref="ValueTask"/>.</returns>
17+
public delegate ValueTask OpenTelemetryConsumeAndProcessMessageHandler<TKey, TValue>(
18+
ConsumeResult<TKey, TValue> consumeResult,
19+
Activity? activity,
20+
CancellationToken cancellationToken = default);

0 commit comments

Comments
 (0)