Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Implements OpenTelemetry Collector in DotNet

3,137 views

Published on

OpenTelemetry Meetup #2

Published in: Technology
  • Be the first to comment

Implements OpenTelemetry Collector in DotNet

  1. 1. 河合 宜文 / Kawai Yoshifumi / @neuecc Cysharp, Inc. Cygames C#大統一理論 C#
  2. 2. Unified Realtime/API Engine for .NET Core and Unity https://github.com/Cysharp/MagicOnion/ gRPCベースのC#特化ネットワークフレームワーク .NET Core(Server) - Unity(Client)における ハイパフォーマンスなAPI通信とリアルタイム通信を実現 gRPCなのにProtocol Buffersを使わない(スキーマ共有としてC# コードそのものをサーバー/クライアントでシェアする)という C#ファーストな設計(シリアライズ自体はMessagePackで行う)
  3. 3. public class TestService : ITestService { public async UnaryResult<int> Sum(int x, int y) { return x + y; } } var client = MagicOnionClient.Create<ITestService>(channel); var result = await client.Sum(100, 200); public interface ITestService { UnaryResult<int> Sum(int x, int y); } Share Service Definition(and Requst/Response Message) written in C# Server Implementation Client Implementation
  4. 4. MagicOnion Datadog 自社アプリのためだけ、なら、特定の サービス専用のコレクターを用意するだ けでいいんですが……
  5. 5. MagicOnion Datadog New Relic Application Insights Mackerel CloudWatch Stackdriver Prometheus Zipkin フレームワーク作者としてこんなにいっぱいは 用意できない(し、ユーザー数的に誰かが作っ てくれることもそう期待できない)
  6. 6. Datadog New Relic Application Insights Mackerel CloudWatch Stackdriver Prometheus Zipkin MagicOnion ここの部分(Collector)を提供する だけでOK ここの部分(Exporter)は標準/準標 準が出揃う(はず)
  7. 7. Implements Collector
  8. 8. 1リクエスト内の細かい挙動(DB通信部分/HTTP通信 部分など)が範囲(Span)ごとにタイムライン表示さ れる。分散トレーシングではなく単一アプリケー ションでも、細かいボトルネックを探りやすい有 用な可視化 数値データ(Sum/Min/Max/Avg/Count)のグラフ化。 インフラ監視だけでなくアプリケーションモニタ リングとしても傾向を見るのに有用な可視化
  9. 9. public class OpenTelemetryCollectorFilter : MagicOnionFilterAttribute { public override async ValueTask Invoke(ServiceContext context) { try { // ここに前処理 await Next(context); // ここに正常時処理 } catch (Exception ex) { // ここに例外時処理 } finally { // ここに後処理 } } } MagicOnionではFilterとしてリクエスト前後のフック ポイントを用意しているので、これに引っ掛ける
  10. 10. var tracer = context.ServiceLocator.GetService<ITracer>(); var sampler = context.ServiceLocator.GetService<ISampler>(); var spanBuilder = tracer.SpanBuilder(context.CallContext.Method, SpanKind.Server); if (sampler != null) { spanBuilder.SetSampler(sampler); } using (spanBuilder.StartScopedSpan(out var span)) { try { span.SetAttribute("component", "grpc"); span.SetAttribute("request.size", context.GetRawRequest().LongLength); await Next(context); // 次ページに続く ITracerとISamplerを取得(これは MagicOnionのDIより。取得方法は なんでもいい) 名前をつけてSpanを作る。フレー ムワークのルートなのでルートで しょという扱い(分散トレーシン グ対応する場合は親の設定などが 必要ですが今回は割愛) 名前はサービスメソッド名(gRPC なのでTestService/Sumなどになる)
  11. 11. var tracer = context.ServiceLocator.GetService<ITracer>(); var sampler = context.ServiceLocator.GetService<ISampler>(); var spanBuilder = tracer.SpanBuilder(context.CallContext.Method, SpanKind.Server); if (sampler != null) { spanBuilder.SetSampler(sampler); } using (spanBuilder.StartScopedSpan(out var span)) { try { span.SetAttribute("component", "grpc"); span.SetAttribute("request.size", context.GetRawRequest().LongLength); await Next(context); // 次ページに続く Trace処理は軽い処理ではないので、間引く 場合はSamplerを渡せば、それのルール (1/10の確率、とか)に則って処理される スコープで囲んでいる範囲が自分のSpanにぶら 下がる雰囲気になる。サービスフレームワーク なのでリクエスト開始から完了までを囲む SetAttributeでSpanに情報を付与。この場合 gRPCですよ、とかリクエストサイズは何 バイトとでしたよ、とか。命名は自由、に みえて仕様である程度は決まってる。
  12. 12. using (spanBuilder.StartScopedSpan(out var span)) { try { // 中略 await Next(context); span.SetAttribute(“response.size”, context.GetRawResponse().LongLength); span.SetAttribute(“status_code”, (long)context.CallContext.Status.StatusCode); span.Status = ConvertStatus(context.CallContext.Status.StatusCode) .WithDescription(context.CallContext.Status.Detail); } catch (Exception ex) { span.SetAttribute(“exception”, ex.ToString()); span.SetAttribute(“status_code”, (long)context.CallContext.Status.StatusCode); span.Status = ConvertStatus(context.CallContext.Status.StatusCode) .WithDescription(context.CallContext.Status.Detail); } } ステータスコードの指定。このステータスコード はgRPCと一緒(と、仕様に書いてある)で、 Ok, Unknown, NotFoundなどがある
  13. 13. MagicOnionではIMagicOnionLoggerとして構造化ログ の口を用意しているので、これを用いる。 public interface IMagicOnionLogger { void BeginBuildServiceDefinition(); void EndBuildServiceDefinition(double elapsed); void BeginInvokeMethod(ServiceContext context, byte[] request, Type type); void EndInvokeMethod(ServiceContext context, byte[] response, Type type, double elapsed, void BeginInvokeHubMethod(StreamingHubContext context, ArraySegment<byte> request, Type t void EndInvokeHubMethod(StreamingHubContext context, int responseSize, Type type, double void InvokeHubBroadcast(string groupName, int responseSize, int broadcastGroupCount); void WriteToStream(ServiceContext context, byte[] writeData, Type type); void ReadFromStream(ServiceContext context, byte[] readData, Type type, bool complete); }
  14. 14. public class OpenTelemetryCollectorLogger : IMagicOnionLogger { static readonly IMeasureDouble UnaryElapsed = MeasureDouble.Create ("MagicOnion/measure/UnaryElapsed", "Unary API elapsed time.", "ms"); static readonly IMeasureLong UnaryResponseSize = MeasureLong.Create ("MagicOnion/measure/UnaryResponseSize", "Unary API response size.", " static readonly IMeasureLong UnaryErrorCount = MeasureLong.Create ("MagicOnion/measure/UnaryErrorCount", "Unary API error Count.", "num" static readonly TagKey MethodKey = TagKey.Create("MagicOnion/keys/Method"); readonly IStatsRecorder statsRecorder; readonly ITagger tagger; readonly ITagContext defaultTags; public OpenTelemetryCollectorLogger(IStatsRecorder statsRecorder, ITagger tagger, ITagConte { this.statsRecorder = statsRecorder; this.tagger = tagger; this.defaultTags = defaultTags ?? TagContext.Empty; } // 実装は次ページ } メトリックに使うKeyの類は事前用意 使う型はIStatsRecorderとITagger
  15. 15. public class OpenTelemetryCollectorLogger : IMagicOnionLogger { ITagContext CreateTag(ServiceContext context) { return tagger.ToBuilder(defaultTags) .Put(MethodKey, TagValue.Create(context.CallContext.Method)).Build(); } public void EndInvokeMethod(ServiceContext context, byte[] response, Type type, double elapsed, bool isErrorOrInterrupted) { var map = statsRecorder.NewMeasureMap(); map.Put(UnaryElapsed, elapsed); map.Put(UnaryResponseSize, response.LongLength); if (isErrorOrInterrupted) { map.Put(UnaryErrorCount, 1); } map.Record(CreateTag(context)); } // 他の実装は省略 } MeasureMapを作って、値をPut。キーは事 前定義しているもの。 最後にTagと共にRecord。 ダッシュボード作りにはTagの設計が肝 要なのですが本題ではないので割愛
  16. 16. Conclusion
  17. 17. 流行れ!今すぐ! ちゃんとしたCollectorを一つ実装しておけば、ユーザーの使うあら ゆるダッシュボードに対応できる……。夢見てた理想だ……。 C#でも勿論使えます SDK: https://github.com/open-telemetry/opentelemetry-dotnet/ まだalphaなので正直怪しいというか実装TODOも多いので、まだ実 用的ではないですがOpenTelemetryのローンチは9月ですし!? 標準でRedis/Http/DBとASP.NET(ウェブフレームワーク)のCollector が、ExporterはPrometheus, Stackdriver, Zipkin, ApplicationInsights 向けのものが提供される模様。

×