2019/05/06

JSON in JSON なログファイルビューア

プログラミング処理の中で、JSON の中に JSON を入れるのは比較的簡単です。エスケープはライブラリがよしなにやってくれますから。でもその多段に JSON を突っ込まれた JSON 文字列をCLIでハンドリングするのはちょっと面倒くさいです。

例えばこんなん。

{
 "message": "{\"level\":\"info\",\"ts\":1557004280.5372975,\"caller\":\"zap/server_interceptors.go:40\",\"msg\":\"finished unary call with code OK\",\"grpc.start_time\":\"2019-05-04T21:11:20Z\",\"system\":\"grpc\",\"span.kind\":\"server\",\"grpc.service\":\"FooService\",\"grpc.method\":\"GetBar\",\"grpc.code\":\"OK\",\"grpc.time_ms\":248.45199584960938}\n",
 "namespace": "foo-service",
 "podName": "foo-86495899d8-m2vfl",
 "containerName": "foo-service"
}

JSON の message の value に JSON が入ってる。

昨今、石を投げればこのような多段 JSON in JSON なログに遭遇します。

この JSON in JSON 状態のエスケープされてる中身を目parseするか、なんとかCLI上で parse するか、という話。

それ、 jq の fromjson でできるよ!というのが今のところのベターアプローチだと思います。

$ stern | jq '. | {message: (.message|fromjson), namespace,podName,containerName}'

{
  "containerName" : "foo-service",
  "podName" : "foo-86495899d8-m2vfl",
  "namespace" : "foo-service",
  "message" : {
     "grpc.service" : "FooService",
     "grpc.method" : "GetBar",
     "grpc.code" : "OK",
     "span.kind" : "server",
     "caller" : "zap/server_interceptors.go:40",
     "system" : "grpc",
     "msg" : "finished unary call with code OK",
     "grpc.start_time" : "2019-05-04T21:11:20Z",
     "grpc.time_ms" : 248.451995849609,
     "level" : "info",
     "ts" : 1557004280.5373
  }
}

でもこれ、面倒くさくないです?

というわけで、JSON in JSON を再帰的にさっといい感じにしてくれるやつ書きました。

JSON in JSON Log Viewer ということで jl じぇいえる コマンドです。

App::jl モジュールに同梱されてます。

$ stern | jl
{
   "podName" : "foo-86495899d8-m2vfl",
   "message" : {
      "system" : "grpc",
      "grpc.time_ms" : 248.451995849609,
      "msg" : "finished unary call with code OK",
      "grpc.method" : "GetBar",
      "span.kind" : "server",
      "level" : "info",
      "grpc.start_time" : "2019-05-04T21:11:20Z",
      "ts" : 1557004280.5373,
      "grpc.code" : "OK",
      "caller" : "zap/server_interceptors.go:40",
      "grpc.service" : "FooService"
   },
   "containerName" : "foo-service",
   "namespace" : "foo-service"
}

超絶簡単ですね! JSON in JSON を再帰的にやっつけます。デフォルトだと最大10段ネストしててもなんとかしてくれます。

--no-pretty オプションをつけて再度 jq に渡すとかすると、便利かもしれません。

$ stern | jl | jq .message.msg
"finished unary call with code OK"

圧倒的に覚えることが減る。

満足した。

サイト内検索