xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

gRPC / MagicOnion 入門 (13) - 送受信されているデータを可視化する

gRPC に限った話ではありませんが、ネットワークを介して送受信しているデータをダンプして目視確認したいことは頻繁にあります。このようなとき、たいてい以下のような方法が採られます。

用途 実装
個別 ダンプしたい箇所にデバッグコードを仕込む
全体 全 API の入口/出口となる箇所にデバッグコードを仕込む

MagicOnion には後者 (全通信内容をダンプする) の機能が標準で用意されているので、今回はこれを利用してみます。

ダンプ用のロガーを仕込む

方法は非常に簡単で、MagicOnionEngine の生成時にロガーを仕込んだオプションを設定するだけです。

using System;
using Grpc.Core;
using Grpc.Core.Logging;
using MagicOnion.Server;

namespace MagicOnionSample.Service
{
    class Program
    {
        static void Main()
        {
            //--- gRPC のログをコンソールに出力
            GrpcEnvironment.SetLogger(new ConsoleLogger());

            //--- MagicOnion 側のログを gRPC のログに流し込む
            //--- そのとき名前付き (JSON 形式) でデータをダンプ
            var logger = new MagicOnionLogToGrpcLoggerWithNamedDataDump();
            var options = new MagicOnionOptions(true){ MagicOnionLogger = logger };
            var service = MagicOnionEngine.BuildServerServiceDefinition(options);

            //--- いつも通りの起動
            var port = new ServerPort("localhost", 12345, ServerCredentials.Insecure);
            var server = new Server(){ Services = { service }, Ports = { port } };
            server.Start();  // launch gRPC server
            Console.ReadLine();
        }
    }
}

実行してみる

これでダンプの準備は整いました。では、以下のような API で通信してみましょう。

using MagicOnion;

namespace MagicOnionSample.ServiceDefinition
{
    public interface ISampleApi : IService<ISampleApi>
    {
        UnaryResult<double> Sample(Vector2 value);
    }
}
using System;
using MagicOnion;
using MagicOnion.Server;
using MagicOnionSample.ServiceDefinition;

namespace MagicOnionSample.Service
{
    public class SampleApi : ServiceBase<ISampleApi>, ISampleApi
    {
        public async UnaryResult<double> Sample(Vector2 value)
        {
            //--- テキトーにピタゴラスでも
            var x2 = value.X * value.X;
            var y2 = value.Y * value.Y;
            return Math.Sqrt(x2 + y2);
        }
    }
}
using System;
using System.Threading.Tasks;
using Grpc.Core;
using MagicOnion.Client;
using MagicOnionSample.ServiceDefinition;

namespace MagicOnionSample.Client
{
    class Program
    {
        static void Main() => MainAsync().Wait();

        static async Task MainAsync()
        {
            var channel = new Channel("localhost", 12345, ChannelCredentials.Insecure);
            var client = MagicOnionClient.Create<ISampleApi>(channel);

            //--- こんな感じのデータを投げてみる
            var v = new Vector2(3, 4);
            var result = await client.Sample(v);

            Console.ReadLine();
        }
    }
}

実行すると以下のようなログが出力されます。

D0911 13:58:21.959994 BeginInvokeMethod type:Unary method:/ISampleApi/Sample size:11 dump:{"X":3,"Y":4}
D0911 13:58:21.979015 EndInvokeMethod type:Unary  method:/ISampleApi/Sample size:9 elapsed:86.2561  dump:5

たった 1 行 2 行の簡単なログですが、ここから以下のことが読み取れます。

  • 通信した時刻
  • API に入ったときか出て行くときか
  • 通信方法は何か (Unary / ServerStreaming / ClientStreaming / DuplexStreaming)
  • どの API にアクセスしたか
  • データサイズはどれだけか
  • API の処理時間
  • データをダンプした結果

デバッグ時に非常に役立つと思うので、ぜひ使ってみてください :)