Node.jsでストリーミングJSON出力の形式をどのように指定しますか?

Node.jsでストリーミングJSON出力の形式をどのように指定しますか?

JSONロギングを使用するNode.jsサーバーがあります。サーバーがローカルで実行されると、ログエントリがJSONオブジェクトに出力されるため、読み取りは非常に退屈です。わかりやすくするために、コマンドを介して出力をパイプしたいと思います。

私は次のようないくつかの選択肢を試しました。ジャックところで、問題は、Node.jsサーバーが起動したときに間違ったJSONが複数行出力され、間違ったJSONを無視するコマンドが見つからなかったことです。

使用できる事前に作成されたコマンドはありますか?それとも直接実装する必要がありますか?サーバー出力の各行は完全なJSONオブジェクトです(最初の数行を除く)。

答え1

jsonログを出力するには、端末で美しいjsonを確認してください。

node my.js | jq -R 'split("\n")|.[length  - 1]|fromjson'

答え2

私はjqやjsonをあまり使用しないので、環境を模倣する必要がありますが、一般的なアイデアで問題を解決できます。

ここでは、3つのヘッダー行といくつかの(偽)JSON出力を生成しました。

#!/bin/sh
echo line 1
echo line 2
echo line 3
echo real json output 1
echo real json output 2
echo real json output 3

以下は、3つのヘッダー行を読み取り、残りの入力を実際のコマンドjqに渡すスクリプトです(またはjqをエミュレートするにはsedを介して渡します)。

#!/usr/bin/env bash

for((HEADLINES=3; HEADLINES > 0; HEADLINES--))
do
  IFS= read -r header
  printf "%s\n" "$header"
done

sed 's/^/parsing: /'

ここで重要なアイデアは、必要な数のヘッダー行を取得してreadそのまま印刷してから、残りの入力をjq(sed、ここ)に渡すことです。 sed コマンドを目的のjqコマンドに置き換えます。

実行例:

$ ./json.sh  | ./jq.sh
line 1
line 2
line 3
parsing: real json output 1
parsing: real json output 2
parsing: real json output 3

答え3

JSON以外の出力行があると仮定すると、n次の短いスクリプトはこれらの行をそのまま渡し、残りの行をフォーマットするjqために使用されます。入力がスクリプトの標準n入力から来ると仮定すると、スクリプトは最初のコマンドライン引数から数字を取得します(この引数がない場合、デフォルトは5です)。

#!/bin/sh

n=${1-5}

if [ "$n" -gt 0 ]; then
    head -n "$n"
fi

jq .

スクリプトは、コマンドが正確な入力行headだけを消費すると仮定します。n一部の実装ではheadこのように動作しないため、n行よりも多くの内容を読み取って処理するための入力は残りませんjq。 GNUはheadこの点でうまく機能し、期待どおりに機能します。

テスト:

$ sh script.sh 2 <file.json
non-json text
on two lines
{
  "name": "myapp",
  "hostname": "myhost.local",
  "pid": 64662,
  "source_file_path": "/path/to/src/connector.js",
  "req_id": "2339717c-6c3b-4e51-a4b2-5c647efd9c25",
  "connector": "abc123",
  "level": "INFO",
  "req": {
    "method": "GET",
    "url": "http://backend/server/url"
  },
  "time": "2016-09-01T06:31:55.099Z",
  "v": 0,
  "message": "Outgoing request"
}

以下は上記のバリエーションで、入力のJSONコンテンツの先頭に非常に簡単な検出を追加します。 JSON文書は、最初{の文字がaの最初の行で始まるとします。

スクリプトは最初に入力を一時ファイル(スクリプトの終了時に削除されます)に保存し、ファイルを2回解析します。 JSON以外のデータを一度抽出した後、JSON文書を再抽出します。

#!/bin/sh

tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT

cat >"$tmpfile"

sed -n '/^[^{]/{p;d;}; q' <"$tmpfile"
sed -n '/^{/,$p'          <"$tmpfile" | jq .

テスト:

$ sh script.sh <file.json
non-json text
on two lines
{
  "name": "myapp",
  "hostname": "myhost.local",
  "pid": 64662,
  "source_file_path": "/path/to/src/connector.js",
  "req_id": "2339717c-6c3b-4e51-a4b2-5c647efd9c25",
  "connector": "abc123",
  "level": "INFO",
  "req": {
    "method": "GET",
    "url": "http://backend/server/url"
  },
  "time": "2016-09-01T06:31:55.099Z",
  "v": 0,
  "message": "Outgoing request"
}

一時ファイルの使用がエレガントでない場合、次のバリアントはコマンドラインから入力ファイルのパス名を取得します。

#!/bin/sh

infile=$1

sed -n '/^[^{]/{p;d;}; q' <"$infile"
sed -n '/^{/,$p'          <"$infile" | jq .

関連情報