-
Notifications
You must be signed in to change notification settings - Fork 785
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
End-to-end example applications #936
Merged
cijothomas
merged 37 commits into
open-telemetry:master
from
alanwest:alanwest/end-to-end-example
Aug 10, 2020
Merged
Changes from 30 commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
41b1921
Suite of example applications demonstrating context propagation with …
alanwest d899d2a
Put System.* namespaces first
alanwest d3b2e51
Newline at EOF
alanwest 203cad3
Fix markdownlint errors
alanwest 38a2a5b
markdownlint fix
alanwest 5fc6e6f
Merge branch 'master' into alanwest/end-to-end-example
cijothomas c75f113
Merge branch 'master' into alanwest/end-to-end-example
cijothomas bbbdb09
Merge branch 'master' into alanwest/end-to-end-example
cijothomas 0ab8939
Refactor WorkerService separating OpenTelemetry related logic from Ra…
alanwest b58a421
Refactor WebApi separating OpenTelemetry related logic from RabbitMQ …
alanwest 5248c04
Use environment variables for RabbitMQ user/pass
alanwest 834503e
Add some comments
alanwest 6191d9d
Merge remote-tracking branch 'upstream/master' into master
alanwest c38f060
Merge branch 'master' into alanwest/end-to-end-example
alanwest 225fadf
Fix RabbitMQ default user/pass
alanwest ba436f0
Use correct ActivityKind
alanwest 403faae
Add attributes following messaging specification
alanwest 59da6cd
Clearing a path through the RabbitMQ jungle
alanwest b1e5de9
Merge branch 'master' into alanwest/end-to-end-example
alanwest 9682076
Merge branch 'master' into alanwest/end-to-end-example
alanwest 4d51283
Merge branch 'master' into alanwest/end-to-end-example
alanwest c925fa2
Merge branch 'master' into alanwest/end-to-end-example
alanwest 807d0ea
Log something when sending/receiving a message
alanwest c2d0484
Code style clean up
alanwest 413a56b
Default logging to info level
alanwest de660c6
Create queue if it does not exist
alanwest a647188
Rename controller to SendMessageController
alanwest c38945d
Refine the readme
alanwest 7f0de78
MarkdownCop
alanwest bbdb926
Merge branch 'master' into alanwest/end-to-end-example
CodeBlanch 0d1a90a
Merge branch 'master' into alanwest/end-to-end-example
alanwest ccc0847
Fix merge snafu
alanwest 51784cf
Merge branch 'alanwest/end-to-end-example' of github.com:alanwest/ope…
alanwest 437a990
Add null checks on activity
alanwest 2d8bade
Add a missed null check
alanwest dcf093a
Use AddOpenTelemetry in ConfigureServices
alanwest dc654ad
Merge branch 'master' into alanwest/end-to-end-example
cijothomas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
**/.classpath | ||
**/.dockerignore | ||
**/.env | ||
**/.git | ||
**/.gitignore | ||
**/.project | ||
**/.settings | ||
**/.toolstarget | ||
**/.vs | ||
**/.vscode | ||
**/*.*proj.user | ||
**/*.dbmdl | ||
**/*.jfm | ||
**/docker-compose* | ||
**/Dockerfile* | ||
**/bin | ||
**/obj | ||
**/*.yaml | ||
**/*.yml | ||
**/*.md | ||
**/*.ps1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# OpenTelemetry Example Application | ||
|
||
This set of projects is an example distributed application comprised of two | ||
components: | ||
|
||
1. An ASP.NET Core Web API | ||
2. A background Worker Service | ||
|
||
The application demonstrates a number of OpenTelemetry concepts: | ||
|
||
* OpenTelemetry APIs for distributed context propagation. | ||
* Basic conventions of how messaging systems are handled in OpenTelemetry. | ||
|
||
The Web API publishes messages to RabbitMQ which the Worker Service consumes. | ||
Distributed context propagation is achieved using OpenTelemetry APIs to inject | ||
and extract trace context in the headers of the published messages. | ||
|
||
The Zipkin exporter is configured for viewing the distributed traces. | ||
|
||
## Running the example | ||
|
||
A running instance of RabbitMQ and Zipkin are required. These can easily be | ||
spun up in docker containers. | ||
|
||
The `WebApi` and `WorkerService` projects can be run from this directory as | ||
follows: | ||
|
||
```shell | ||
dotnet run --project WebApi | ||
dotnet run --project WorkerService | ||
``` | ||
|
||
Instead of running the projects individually, if you are using Docker Desktop, | ||
a `docker-compose` file is provided. This makes standing up the Zipkin and | ||
RabbitMQ dependencies easy, as well as starting both applications. | ||
|
||
To run the example using `docker-compose`, run the following from this | ||
directory: | ||
|
||
```shell | ||
docker-compose up --build | ||
``` | ||
|
||
With everything running: | ||
|
||
* [Invoke the Web API](http://localhost:5000/SendMessage) to send a message. | ||
* If you have run RabbitMQ and Zipkin with default settings: | ||
* View your traces with Zipkin [here](http://localhost:9411/zipkin) | ||
* Manage RabbitMQ [here](http://localhost:15672/) | ||
* user = guest | ||
* password = guest | ||
|
||
## References | ||
|
||
* [Docker Desktop](https://www.docker.com/products/docker-desktop) | ||
* [OpenTelemetry Project](https://opentelemetry.io/) | ||
* [RabbitMQ](https://www.rabbitmq.com/) | ||
* [Worker Service](https://docs.microsoft.com/en-us/azure/azure-monitor/app/worker-service) | ||
* [Zipkin](https://zipkin.io) |
107 changes: 107 additions & 0 deletions
107
examples/MicroserviceExample/Utils/Messaging/MessageReceiver.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// <copyright file="MessageReceiver.cs" company="OpenTelemetry Authors"> | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// 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. | ||
// </copyright> | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading; | ||
using Microsoft.Extensions.Logging; | ||
using OpenTelemetry.Context.Propagation; | ||
using RabbitMQ.Client; | ||
using RabbitMQ.Client.Events; | ||
|
||
namespace Utils.Messaging | ||
{ | ||
public class MessageReceiver : IDisposable | ||
{ | ||
private static readonly ActivitySource ActivitySource = new ActivitySource(nameof(MessageReceiver)); | ||
private static readonly ITextFormat TextFormat = new TraceContextFormat(); | ||
|
||
private readonly ILogger<MessageReceiver> logger; | ||
private readonly IConnection connection; | ||
private readonly IModel channel; | ||
|
||
public MessageReceiver(ILogger<MessageReceiver> logger) | ||
{ | ||
this.logger = logger; | ||
this.connection = RabbitMqHelper.CreateConnection(); | ||
this.channel = RabbitMqHelper.CreateModelAndDeclareTestQueue(this.connection); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
this.channel.Dispose(); | ||
this.connection.Dispose(); | ||
} | ||
|
||
public void StartConsumer() | ||
{ | ||
RabbitMqHelper.StartConsumer(this.channel, this.ReceiveMessage); | ||
} | ||
|
||
public void ReceiveMessage(BasicDeliverEventArgs ea) | ||
{ | ||
// Extract the ActivityContext of the upstream parent from the message headers. | ||
var parentContext = TextFormat.Extract(ea.BasicProperties, this.ExtractTraceContextFromBasicProperties); | ||
|
||
// Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. | ||
// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md#span-name | ||
var activityName = $"{ea.RoutingKey} receive"; | ||
|
||
using (var activity = ActivitySource.StartActivity(activityName, ActivityKind.Consumer, parentContext)) | ||
{ | ||
try | ||
{ | ||
var message = Encoding.UTF8.GetString(ea.Body.Span.ToArray()); | ||
|
||
this.logger.LogInformation($"Message received: [{message}]"); | ||
|
||
activity.AddTag("message", message); | ||
alanwest marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// The OpenTelemetry messaging specification defines a number of attributes. These attributes are added here. | ||
RabbitMqHelper.AddMessagingTags(activity); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like what you did here 👍 |
||
|
||
// Simulate some work | ||
Thread.Sleep(1000); | ||
} | ||
catch (Exception ex) | ||
{ | ||
this.logger.LogError(ex, "Message processing failed."); | ||
} | ||
} | ||
} | ||
|
||
private IEnumerable<string> ExtractTraceContextFromBasicProperties(IBasicProperties props, string key) | ||
{ | ||
try | ||
{ | ||
if (props.Headers.TryGetValue(key, out var value)) | ||
{ | ||
var bytes = value as byte[]; | ||
return new[] { Encoding.UTF8.GetString(bytes) }; | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
this.logger.LogError(ex, "Failed to extract trace context: {ex}"); | ||
} | ||
|
||
return Enumerable.Empty<string>(); | ||
} | ||
} | ||
} |
104 changes: 104 additions & 0 deletions
104
examples/MicroserviceExample/Utils/Messaging/MessageSender.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// <copyright file="MessageSender.cs" company="OpenTelemetry Authors"> | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// 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. | ||
// </copyright> | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Text; | ||
using Microsoft.Extensions.Logging; | ||
using OpenTelemetry.Context.Propagation; | ||
using RabbitMQ.Client; | ||
|
||
namespace Utils.Messaging | ||
{ | ||
public class MessageSender : IDisposable | ||
{ | ||
private static readonly ActivitySource ActivitySource = new ActivitySource(nameof(MessageSender)); | ||
private static readonly ITextFormat TextFormat = new TraceContextFormat(); | ||
|
||
private readonly ILogger<MessageSender> logger; | ||
private readonly IConnection connection; | ||
private readonly IModel channel; | ||
|
||
public MessageSender(ILogger<MessageSender> logger) | ||
{ | ||
this.logger = logger; | ||
this.connection = RabbitMqHelper.CreateConnection(); | ||
this.channel = RabbitMqHelper.CreateModelAndDeclareTestQueue(this.connection); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
this.channel.Dispose(); | ||
this.connection.Dispose(); | ||
} | ||
|
||
public string SendMessage() | ||
{ | ||
try | ||
{ | ||
// Start an activity with a name following the semantic convention of the OpenTelemetry messaging specification. | ||
// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md#span-name | ||
var activityName = $"{RabbitMqHelper.TestQueueName} send"; | ||
|
||
using (var activity = ActivitySource.StartActivity(activityName, ActivityKind.Producer)) | ||
{ | ||
var props = this.channel.CreateBasicProperties(); | ||
|
||
// Inject the ActivityContext into the message headers to propagate trace context to the receiving service. | ||
TextFormat.Inject(activity.Context, props, this.InjectTraceContextIntoBasicProperties); | ||
alanwest marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// The OpenTelemetry messaging specification defines a number of attributes. These attributes are added here. | ||
RabbitMqHelper.AddMessagingTags(activity); | ||
alanwest marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
var body = $"Published message: DateTime.Now = {DateTime.Now}."; | ||
|
||
this.channel.BasicPublish( | ||
exchange: RabbitMqHelper.DefaultExchangeName, | ||
routingKey: RabbitMqHelper.TestQueueName, | ||
basicProperties: props, | ||
body: Encoding.UTF8.GetBytes(body)); | ||
|
||
this.logger.LogInformation($"Message sent: [{body}]"); | ||
|
||
return body; | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
this.logger.LogError(ex, "Message publishing failed."); | ||
throw; | ||
} | ||
} | ||
|
||
private void InjectTraceContextIntoBasicProperties(IBasicProperties props, string key, string value) | ||
{ | ||
try | ||
{ | ||
if (props.Headers == null) | ||
{ | ||
props.Headers = new Dictionary<string, object>(); | ||
} | ||
|
||
props.Headers[key] = value; | ||
} | ||
catch (Exception ex) | ||
{ | ||
this.logger.LogError(ex, "Failed to inject trace context."); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like some conflict resolve header?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh nice catch! Apparently, Visual Studio was like
this is fine
.