Skip to content

Commit

Permalink
chore: ChatApp.Telemetry docker refresh
Browse files Browse the repository at this point in the history
  • Loading branch information
guitarrapc committed Jun 2, 2021
1 parent 3ed1ee3 commit 4a1f6e2
Show file tree
Hide file tree
Showing 23 changed files with 97 additions and 55 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1580,7 +1580,7 @@ I believe that this can be easily and effectively applied to sending a large num

## Experimentals
### OpenTelemetry
MagicOnion.OpenTelemetry is implementation of [open\-telemetry/opentelemetry\-dotnet: OpenTelemetry \.NET SDK](https://github.com/open-telemetry/opentelemetry-dotnet), so you can use any OpenTelemetry exporter, like [Prometheus](https://prometheus.io/), [StackDriver](https://cloud.google.com/stackdriver/pricing), [Zipkin](https://zipkin.io/) and others.
MagicOnion.OpenTelemetry is implementation of [open\-telemetry/opentelemetry\-dotnet: OpenTelemetry \.NET SDK](https://github.com/open-telemetry/opentelemetry-dotnet), so you can use any OpenTelemetry exporter, like [Jaeger](https://www.jaegertracing.io/), [Zipkin](https://zipkin.io/), [StackDriver](https://cloud.google.com/stackdriver) and others.
See details at [MagicOnion.Server.OpenTelemetry](src/MagicOnion.Server.OpenTelemetry)

Expand Down
30 changes: 18 additions & 12 deletions samples/ChatApp.Telemetry/ChatApp.Server/ChatHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,27 @@ public class ChatHub : StreamingHubBase<IChatHub, IChatHubReceiver>, IChatHub
private string myName;
private readonly ActivitySource mysqlActivity;
private readonly ActivitySource redisActivity;
private readonly MagicOnionOpenTelemetryOptions options;

public ChatHub(BackendActivitySources backendActivity)
public ChatHub(BackendActivitySources backendActivity, MagicOnionOpenTelemetryOptions options)
{
this.mysqlActivity = backendActivity.Get("mysql");
this.redisActivity = backendActivity.Get("redis");
this.options = options;
mysqlActivity = backendActivity.Get("mysql");
redisActivity = backendActivity.Get("redis");
}

public async Task JoinAsync(JoinRequest request)
{
var random = new Random();
this.room = await this.Group.AddAsync(request.RoomName);
this.myName = request.UserName;

this.Broadcast(this.room).OnJoin(request.UserName);
room = await this.Group.AddAsync(request.RoomName);
myName = request.UserName;
Broadcast(this.room).OnJoin(request.UserName);

// dummy external operation db.
var random = new Random();
using (var activity = mysqlActivity.StartActivity("room/insert", ActivityKind.Internal))
{
// this is sample. use orm or any safe way.
activity.SetTag("service.name", options.ServiceName);
activity.SetTag("table", "rooms");
activity.SetTag("query", $"INSERT INTO rooms VALUES (0, '@room', '@username', '1');");
activity.SetTag("parameter.room", request.RoomName);
Expand All @@ -46,6 +48,7 @@ public async Task JoinAsync(JoinRequest request)
}
using (var activity = redisActivity.StartActivity($"member/status", ActivityKind.Internal))
{
activity.SetTag("service.name", options.ServiceName);
activity.SetTag("command", "set");
activity.SetTag("parameter.key", this.myName);
activity.SetTag("parameter.value", "1");
Expand All @@ -59,15 +62,15 @@ public async Task JoinAsync(JoinRequest request)

public async Task LeaveAsync()
{
var random = new Random();
await this.room.RemoveAsync(this.Context);

this.Broadcast(this.room).OnLeave(this.myName);

// dummy external operation.
var random = new Random();
using (var activity = mysqlActivity.StartActivity("room/update", ActivityKind.Internal))
{
// this is sample. use orm or any safe way.
activity.SetTag("service.name", options.ServiceName);
activity.SetTag("table", "rooms");
activity.SetTag("query", $"UPDATE rooms SET status=0 WHERE id='room' AND name='@username';");
activity.SetTag("parameter.room", this.room.GroupName);
Expand All @@ -77,6 +80,7 @@ public async Task LeaveAsync()

using (var activity = redisActivity.StartActivity($"member/status", ActivityKind.Internal))
{
activity.SetTag("service.name", options.ServiceName);
activity.SetTag("command", "set");
activity.SetTag("parameter.key", this.myName);
activity.SetTag("parameter.value", "0");
Expand All @@ -86,13 +90,14 @@ public async Task LeaveAsync()

public async Task SendMessageAsync(string message)
{
var random = new Random();
var response = new MessageResponse { UserName = this.myName, Message = message };
this.Broadcast(this.room).OnSendMessage(response);

// dummy external operation.
var random = new Random();
using (var activity = redisActivity.StartActivity($"chat_latest_message", ActivityKind.Internal))
{
activity.SetTag("service.name", options.ServiceName);
activity.SetTag("command", "set");
activity.SetTag("parameter.key", room.GroupName);
activity.SetTag("parameter.value", $"{myName}={message}");
Expand All @@ -104,13 +109,14 @@ public async Task SendMessageAsync(string message)

public async Task GenerateException(string message)
{
var random = new Random();
var ex = new Exception(message);

// dummy external operation.
var random = new Random();
using (var activity = mysqlActivity.StartActivity("errors/insert", ActivityKind.Internal))
{
// this is sample. use orm or any safe way.
activity.SetTag("service.name", options.ServiceName);
activity.SetTag("table", "errors");
activity.SetTag("query", $"INSERT INTO rooms VALUES ('{ex.Message}', '{ex.StackTrace}');");
await Task.Delay(TimeSpan.FromMilliseconds(random.Next(2, 20)));
Expand Down
7 changes: 5 additions & 2 deletions samples/ChatApp.Telemetry/ChatApp.Server/ChatService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public ChatService(BackendActivitySources backendActivity, MagicOnionOpenTelemet
{
this.options = options;
this.mysqlSource = backendActivity.Get("mysql");
this.s2sSource = backendActivity.Get("chatapp.s2s");
this.s2sSource = backendActivity.Get("chatapp.server.s2s");
this.logger = logger;
}

Expand All @@ -35,6 +35,7 @@ public async UnaryResult<Nil> GenerateException(string message)
using (var activity = this.mysqlSource.StartActivity("errors/insert", ActivityKind.Internal))
{
// this is sample. use orm or any safe way.
activity.SetTag("service.name", options.ServiceName);
activity.SetTag("table", "errors");
activity.SetTag("query", $"INSERT INTO rooms VALUES ('{ex.Message}', '{ex.StackTrace}');");
await Task.Delay(TimeSpan.FromMilliseconds(2));
Expand All @@ -50,13 +51,14 @@ public async UnaryResult<Nil> SendReportAsync(string message)
using (var activity = this.mysqlSource.StartActivity("report/insert", ActivityKind.Internal))
{
// this is sample. use orm or any safe way.
activity.SetTag("service.name", options.ServiceName);
activity.SetTag("table", "report");
activity.SetTag("query", $"INSERT INTO report VALUES ('foo', 'bar');");
await Task.Delay(TimeSpan.FromMilliseconds(2));
}

// Server to Server operation
var channel = GrpcChannel.ForAddress("http://localhost:4999");
var channel = GrpcChannel.ForAddress(Environment.GetEnvironmentVariable("Server2ServerEndpoint", EnvironmentVariableTarget.Process) ?? "http://localhost:4999");
var client = MagicOnionClient.Create<IMessageService>(channel, new[]
{
// propagate trace context from ChatApp.Server to MicroServer
Expand All @@ -68,6 +70,7 @@ public async UnaryResult<Nil> SendReportAsync(string message)
using (var activity = this.mysqlSource.StartActivity("report/get", ActivityKind.Internal))
{
// this is sample. use orm or any safe way.
activity.SetTag("service.name", options.ServiceName);
activity.SetTag("table", "report");
activity.SetTag("query", $"INSERT INTO report VALUES ('foo', 'bar');");
await Task.Delay(TimeSpan.FromMilliseconds(1));
Expand Down
13 changes: 9 additions & 4 deletions samples/ChatApp.Telemetry/ChatApp.Server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["ChatApp.Server/ChatApp.Server.csproj", "ChatApp.Server/"]
COPY ["ChatApp.Shared/ChatApp.Shared.csproj", "ChatApp.Shared/"]
RUN dotnet restore "ChatApp.Server/ChatApp.Server.csproj"
COPY ["samples/ChatApp.Telemetry/ChatApp.Server/ChatApp.Server.csproj", "samples/ChatApp.Telemetry/ChatApp.Server/"]
COPY ["samples/ChatApp.Telemetry/ChatApp.Shared/ChatApp.Shared.csproj", "samples/ChatApp.Telemetry/ChatApp.Shared/"]
COPY ["src/MagicOnion/MagicOnion.csproj", "src/MagicOnion/"]
COPY ["src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj", "src/MagicOnion.Abstractions/"]
COPY ["src/MagicOnion.Server/MagicOnion.Server.csproj", "src/MagicOnion.Server/"]
COPY ["src/MagicOnion.Server.OpenTelemetry/MagicOnion.Server.OpenTelemetry.csproj", "src/MagicOnion.Server.OpenTelemetry/"]
COPY ["src/MagicOnion.Shared/MagicOnion.Shared.csproj", "src/MagicOnion.Shared/"]
RUN dotnet restore "samples/ChatApp.Telemetry/ChatApp.Server/ChatApp.Server.csproj"
COPY . .
WORKDIR "/src/ChatApp.Server"
WORKDIR "/src/samples/ChatApp.Telemetry/ChatApp.Server"
RUN dotnet build "ChatApp.Server.csproj" -c Debug -o /app

FROM build AS publish
Expand Down
6 changes: 4 additions & 2 deletions samples/ChatApp.Telemetry/ChatApp.Server/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void ConfigureServices(IServiceCollection services)
});

// additional Tracer for user's own service.
services.AddAdditionalTracer(Configuration, new[] { "chatapp.s2s", "mysql", "redis" });
services.AddAdditionalTracer(Configuration);
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand Down Expand Up @@ -108,8 +108,9 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

public static class TelemetryExtensions
{
public static void AddAdditionalTracer(this IServiceCollection services, IConfiguration configuration, string[] serviceNames)
public static void AddAdditionalTracer(this IServiceCollection services, IConfiguration configuration)
{
var serviceNames = new[] { "chatapp.server.s2s", "mysql", "redis" };
var exporter = configuration.GetValue<string>("UseExporter").ToLowerInvariant();
foreach (var service in serviceNames)
{
Expand All @@ -125,6 +126,7 @@ public static void AddAdditionalTracer(this IServiceCollection services, IConfig
case "zipkin":
OpenTelemetry.Sdk.CreateTracerProviderBuilder()
.AddSource(service)
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(service))
.AddZipkinExporter()
.Build();
break;
Expand Down
1 change: 1 addition & 0 deletions samples/ChatApp.Telemetry/ChatApp.Server/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"MagicOnion": {
"OpenTelemetry": {
"ServiceName": "ChatApp.Server",
// "ExposeRpcScope": false,
"TracingTags": {
"foo": "bar"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["samples/ChatApp.Telemetry/ChatApp.Server/ChatApp.Server.csproj", "samples/ChatApp.Telemetry/ChatApp.Server/"]
COPY ["samples/ChatApp.Telemetry/MicroServer/MicroServer.csproj", "samples/ChatApp.Telemetry/MicroServer/"]
COPY ["samples/ChatApp.Telemetry/ChatApp.Shared/ChatApp.Shared.csproj", "samples/ChatApp.Telemetry/ChatApp.Shared/"]
COPY ["src/MagicOnion/MagicOnion.csproj", "src/MagicOnion/"]
COPY ["src/MagicOnion.Abstractions/MagicOnion.Abstractions.csproj", "src/MagicOnion.Abstractions/"]
COPY ["src/MagicOnion.Server/MagicOnion.Server.csproj", "src/MagicOnion.Server/"]
COPY ["src/MagicOnion.Server.OpenTelemetry/MagicOnion.Server.OpenTelemetry.csproj", "src/MagicOnion.Server.OpenTelemetry/"]
COPY ["src/MagicOnion.Shared/MagicOnion.Shared.csproj", "src/MagicOnion.Shared/"]
RUN dotnet restore "samples/ChatApp.Telemetry/ChatApp.Server/ChatApp.Server.csproj"
RUN dotnet restore "samples/ChatApp.Telemetry/MicroServer/MicroServer.csproj"
COPY . .
WORKDIR "/src/samples/ChatApp.Telemetry/ChatApp.Server"
RUN dotnet build "ChatApp.Server.csproj" -c Debug -o /app
WORKDIR "/src/samples/ChatApp.Telemetry/MicroServer"
RUN dotnet build "MicroServer.csproj" -c Debug -o /app

FROM build AS publish
RUN dotnet publish "ChatApp.Server.csproj" -c Debug -o /app
RUN dotnet publish "MicroServer.csproj" -c Debug -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "ChatApp.Server.dll"]
ENTRYPOINT ["dotnet", "MicroServer.dll"]
1 change: 0 additions & 1 deletion samples/ChatApp.Telemetry/MicroServer/MessageService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using MagicOnion;
using MagicOnion.Server;
using MessagePack;
using MicroServer.Shared;
using System;
using System.Threading.Tasks;
Expand Down
2 changes: 2 additions & 0 deletions samples/ChatApp.Telemetry/MicroServer/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
},
"MagicOnion": {
"OpenTelemetry": {
"ServiceName": "MicroServer",
// "ExposeRpcScope": false,
"TracingTags": {
"foo": "bar"
}
Expand Down
17 changes: 8 additions & 9 deletions samples/ChatApp.Telemetry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,24 @@ This is Sample to run MagicOnion with OpenTelemetry implementation.

## Getting started

To run simple ChatApp.Server,
Option1. Run ChatApp.Server on VisualStudio, and run zipkin, jeager in docker.

1. Run `docker-compose -f docker-compose.telemetry.yaml up`.
1. Launch `ChatApp.Server.Telemetry` from VisualStudio.
1. Run `ChatScene` from UnityEditor.
1. Launch UnityEditor for `sample/ChatApp/ChatApp.Unity`, open `ChatScene` then do any operations.
1. Access Telemery's Web UI.

Server and Telemetries are fully containernized.
Option2. Run all ChatApp.Server, zipkin and jeager in container

1. Run `docker-compose -f docker-compose.yaml -f docker-compose.telemetry.yaml up`.
1. Run `ChatScene` from UnityEditor.
1. Launch UnityEditor for `sample/ChatApp/ChatApp.Unity`, open `ChatScene` then do any operations.
1. Access Telemery's Web UI.

Telemetry's Web UI address.
Telemetry's Web UI address. (default send to jeager)

* [jaeger](http://localhost:16686/)
* [zipkin](http://localhost:9411/)
* no data on default. you can switch with jaeger.
* [prometheus](http://localhost:9090/)
* [grafana](http://localhost:3000/)
* user/password is `admin/admin`.
* no data on default. you can switch by appsettings.json

## Projects

Expand All @@ -34,6 +31,8 @@ There are 3 projects for this sample.
1. ChatApp.Shared (samples/ChatApp.Telemetry/)
1. ChatApp.Unity (samples/ChatApp/)

and optional Server to Server

**ChatApp.Server** is Serverside MagicOnion implementation with OpenTelemetry.

**ChatApp.Shared** is class library shared with both ChatApp.Server and ChatApp.Unity.
Expand Down
11 changes: 11 additions & 0 deletions samples/ChatApp.Telemetry/docker-compose.telemetry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,14 @@ services:
ports:
- 6831:6831/udp # client post
- 16686:16686 # web

# Server to Server
microserver:
image: cysharp/magiconion_sample_chatapp_microserver:4.3.1-1.0.0.rc4
environment:
- DOTNET_ENVIRONMENT=Development
- UseExporter=jaeger
- Jaeger__AgentHost=jaeger
- Zipkin__Endpoint=http://zipkin:9411/api/v2/spans
ports:
- 4999:80
6 changes: 2 additions & 4 deletions samples/ChatApp.Telemetry/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ services:
image: cysharp/magiconion_sample_chatapp_telemetry:4.3.1-1.0.0.rc4
ports:
- 5000:80
- 9184:9184
environment:
- DOTNET_ENVIRONMENT=Development
- MagicOnion__OpenTelemetry__MetricsExporterEndpoint=http://127.0.0.1:9184/metrics/
- MagicOnion__OpenTelemetry__MetricsExporterHostingEndpoint=http://+:9184/metrics/
- UseExporter=jaeger
- Jaeger__Host=jaeger
- Jaeger__AgentHost=jaeger
- Zipkin__Endpoint=http://zipkin:9411/api/v2/spans
- Server2ServerEndpoint=http://microserver:80
4 changes: 3 additions & 1 deletion samples/ChatApp.Telemetry/docker_build.bat
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
:: run from Repository Root
:: cysharp/magiconion_sample_chatapp_telemetry
docker build -t chatapp_magiconion:latest -f samples/ChatApp.Telemetry/ChatApp.Server/Dockerfile.full .
:: cysharp/magiconion_sample_microserver
docker build -t chatapp_magiconion:latest -f samples/ChatApp.Telemetry/ChatApp.Server/Dockerfile .
docker build -t chatapp_microserver:latest -f samples/ChatApp.Telemetry/MicroServer/Dockerfile .
5 changes: 5 additions & 0 deletions samples/ChatApp.Telemetry/docker_push.bat
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ docker tag chatapp_magiconion:latest cysharp/magiconion_sample_chatapp_telemetry
docker tag chatapp_magiconion:latest cysharp/magiconion_sample_chatapp_telemetry:4.3.1-1.0.0.rc4
docker push cysharp/magiconion_sample_chatapp_telemetry:latest
docker push cysharp/magiconion_sample_chatapp_telemetry:4.3.1-1.0.0.rc4

docker tag chatapp_microserver:latest cysharp/magiconion_sample_chatapp_microserver:latest
docker tag chatapp_microserver:latest cysharp/magiconion_sample_chatapp_microserver:4.3.1-1.0.0.rc4
docker push cysharp/magiconion_sample_chatapp_microserver:latest
docker push cysharp/magiconion_sample_chatapp_microserver:4.3.1-1.0.0.rc4
2 changes: 1 addition & 1 deletion samples/ChatApp.Telemetry/docker_run.bat
Original file line number Diff line number Diff line change
@@ -1 +1 @@
docker-compose -f docker-compose.yaml -f docker-compose.telemetry.yaml up
docker-compose -f docker-compose.yaml -f docker-compose.telemetry.yaml up
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace MagicOnion.Server.OpenTelemetry.Internal
{
public static class OpenTelemetryHelper
internal static class OpenTelemetryHelper
{
/// <summary>
/// Convert gRPC StatusCode to OpenTelemetry Status.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace MagicOnion.Server.OpenTelemetry.Internal
internal static class SemanticConventions
{
// tag spec: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md#grpc
public const string AttributeServiceName = "service.name";
public const string AttributeException = "exception";

public const string AttributeHttpHost = "http.host";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public MagicOnionOpenTelemetryClientFilter(ActivitySource activitySource, MagicO
public async ValueTask<ResponseContext> SendAsync(RequestContext context, Func<RequestContext, ValueTask<ResponseContext>> next)
{
var rpcService = context.MethodPath.Split('/')[0];
using var rpcScope = new ClientRpcScope(rpcService, context.MethodPath, context, source);
using var rpcScope = new ClientRpcScope(rpcService, context.MethodPath, context, source, options);
rpcScope.SetTags(options.TracingTags);

try
Expand All @@ -51,7 +51,8 @@ public async ValueTask<ResponseContext> SendAsync(RequestContext context, Func<R

internal class ClientRpcScope : RpcScope
{
public ClientRpcScope(string rpcService, string rpcMethod, RequestContext context, ActivitySource source) : base(rpcService, rpcMethod)
public ClientRpcScope(string rpcService, string rpcMethod, RequestContext context, ActivitySource source, MagicOnionOpenTelemetryOptions options)
: base(rpcService, rpcMethod, options.ServiceName)
{
// capture the current activity
this.ParentActivity = Activity.Current;
Expand Down
Loading

0 comments on commit 4a1f6e2

Please sign in to comment.