Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,28 @@ this repository:

![](https://raw.githubusercontent.com/devlooped/WhatsApp/main/assets/img/aspire.png)

The spans/activites created by the `OpenTelemetryHandler` follow the [OpenTelemetry Semantic Conventions for Messaging Spans](https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/).
Specifically, it uses a [consumer span](https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/#consumer-spans)
named "process whatsapp" to track the processing of incoming WhatsApp messages.

| Attribute/Tag | Value | Description | OTEL Convention |
|-----------|-------|-------------|-----------------|
| `messaging.system` | `whatsapp` | Identifies the messaging system being used. | [messaging.system](https://opentelemetry.io/docs/specs/semconv/registry/attributes/messaging/) |
| `messaging.operation.name` | `process` | The name of the operation performed on the message, indicating processing of incoming messages. | [messaging.operation.name](https://opentelemetry.io/docs/specs/semconv/registry/attributes/messaging/) |
| `messaging.destination.name` | Service ID (e.g., WhatsApp Business Account phone number) | The name of the destination to which the message is sent. In this context, it's the service identifier for the WhatsApp endpoint. | [messaging.destination.name](https://opentelemetry.io/docs/specs/semconv/registry/attributes/messaging/) |
| `messaging.client.id` | User phone number | The identifier of the client that sent the message. | [messaging.client.id](https://opentelemetry.io/docs/specs/semconv/registry/attributes/messaging/) |
| `messaging.message.id` | Message ID | The unique identifier of the message being processed. | [messaging.message.id](https://opentelemetry.io/docs/specs/semconv/registry/attributes/messaging/) |
| `messaging.message.conversation_id` | Conversation ID (if available) | The identifier of the conversation the message belongs to, if applicable. | [messaging.message.conversation_id](https://opentelemetry.io/docs/specs/semconv/registry/attributes/messaging/) |

These attributes provide detailed context for tracing message processing flows in distributed systems.

In addition to spans, the handler emits metrics:
- `messaging.process.duration` (histogram): Duration of WhatsApp message processing in seconds. See [OTEL convention](https://opentelemetry.io/docs/specs/semconv/messaging/messaging-metrics/#metric-messagingprocessduration)
- `messaging.client.consumed.messages` (counter): Number of WhatsApp messages processed. See [OTEL convention](https://opentelemetry.io/docs/specs/semconv/messaging/messaging-metrics/#metric-messagingclientconsumedmessages)

These metrics also carry the same tags as the spans for correlation.

<!-- #content -->

## Scalability and Performance

Expand Down
2 changes: 1 addition & 1 deletion src/WhatsApp/OpenTelemetryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ static class OpenTelemetryExtensions
activity.AddTag("error.type", e.GetType().FullName);
activity.SetStatus(ActivityStatusCode.Error, e.Message);

activity.AddEvent(new ActivityEvent("exception", tags: new(tags)));
activity.AddEvent(new ActivityEvent("exception", tags: [.. tags]));

return tags;
}
Expand Down
13 changes: 7 additions & 6 deletions src/WhatsApp/OpenTelemetryHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ public override IAsyncEnumerable<Response> HandleAsync(IEnumerable<IMessage> mes
}
else
{
using var span = activitySource.StartActivity("whatsapp process", ActivityKind.Consumer);
using var span = activitySource.StartActivity("process whatsapp", ActivityKind.Consumer);
if (span != null)
{
span.SetTag("messaging.system", "whatsapp");
span.SetTag("messaging.destination", "whatsapp");
span.SetTag("messaging.operation", "process");
span.SetTag("messaging.operation.name", "process");
span.SetTag("messaging.destination.name", message.ServiceId);
span.SetTag("messaging.client.id", message.UserNumber);
span.SetTag("messaging.message.id", message.Id);
span.SetTag("messaging.client.id", message.ServiceId);
if (message.ConversationId is string conversationId)
span.SetTag("messaging.message.conversation_id", conversationId);
}
Expand All @@ -94,8 +94,9 @@ public override IAsyncEnumerable<Response> HandleAsync(IEnumerable<IMessage> mes
var tags = new TagList
{
{ "messaging.system", "whatsapp" },
{ "messaging.operation", "process" },
{ "messaging.client.id", message.ServiceId },
{ "messaging.operation.name", "process" },
{ "messaging.destination.name", message.ServiceId },
{ "messaging.client.id", message.UserNumber },
};

return base.HandleAsync(messages, cancellation).WithErrorHandlingAsync(
Expand Down