nginxはライブダッシュボードに記録する


私の以前のブログの記事の上に構築し、私はNGinxのログ表示を構築することを決めた表示を作成します.
起こるかもしれない問題はログファイルが巨大であるかもしれません、そして、我々はそれをすべてロードして、解析したくありません.私は私たちが十分なデータを得た場合は、ファイルの最後にロードし、チェックしていない場合、我々は十分な負荷まで繰り返してください.

  def fetch_logs(params, node) do
    data = :rpc.call(node, __MODULE__, :logs_callback, [params])

    {data, length(data)}
  end

この関数呼び出しはRPCでラップされます.ので、ライブビューの複数のノードとの互換性を呼び出します.また、ログファイルを使用してノード上でフィルタリングを行うことができますので、並べ替えと検索のparamsを渡す-データに関数を送信します.
コールバックはファイルを開き、ファイルサイズを取得し、ファイル読み込みを処理する再帰関数に渡します.その後、データを取得した後にソートし、必要な行だけを返します.
  def logs_callback(params) do
    %{limit: limit, sort_by: sort_by, sort_dir: sort_dir} = params

    with {:ok, pid} <- :file.open(@path, [:binary]),
         {:ok, info} <- :file.read_file_info(pid),
         {:file_info, file_size, _, _, _, _, _, _, _, _, _, _, _, _} <- info,
         {:ok, content} <- get_data([], pid, file_size, 0, params),
         :ok <- :file.close(pid) do
           content
           |> Enum.sort_by(&sort_value(&1, sort_by), sort_dir)
           |> Enum.take(limit)
    else
      error -> IO.puts(error)
    end
  end
再帰的な関数は、ファイルの末尾からいくつかの量のデータを読み込み、別の行に分割すると、ほとんどの時間が完全な行ではない最初の1つを覚えているので、今のところそれを覚えています.残りの部分をフィルタリングして解析します.それから、我々は十分なデータを得たかどうかチェックします、はい、我々がちょうどそれを返すならば、フロントエンドに表示されます.さもなければ、私たちは現在解析された行と以前に解析されていなかった部分を再帰的な呼び出しに渡します.ファイルの終わりからもう一つのチャンクのデータを取得します.それから、我々はちょうどステップを繰り返します.そのとき、我々は我々が十分なデータを得たかどうかもう一度チェックします.これは、制限に達するまで、またはファイルの内容のすべてを読むまで繰り返されます.
あなたのWebアプリに応じて@ avgchin lineountの長さが異なることがあります-それは私たちがファイルの末尾からたびに取得したいどのように多くのバイトを計算するために使用されます.
@avg_line_lenght 200
  def get_data(data, _pid, 0 = _load_from, _head_size, _params), do: {:ok, data}

  def get_data(data, pid, load_from, last_line_offset, params) do
    %{limit: limit, search: search} = params
    chunk_size = limit * @avg_line_lenght

    {load_from, buffer_size} =
      if load_from - chunk_size < 0 do
        {0, load_from + last_line_offset}
      else
        {load_from - chunk_size, chunk_size + last_line_offset}
      end

    [first_line | full_lines_chunk] = get_data_chunk(pid, load_from, buffer_size)

    updated_data = parse_chunk(full_lines_chunk, search) ++ data

    newlines = Enum.count(updated_data)

    if newlines < limit do
      get_data(updated_data, pid, load_from, byte_size(first_line), params)
    else
      {:ok, updated_data}
    end
  end

  defp get_data_chunk(pid, load_from, buffer_size) do
    case :file.pread(pid, [{load_from, buffer_size}]) do
      {:ok, [:eof]} -> ""
      {:ok, [content]} -> content
      _ -> ""
    end
    |> :binary.split(<<"\n">>, [:global])
  end

  defp parse_chunk(data, search) do
    data
    |> filter_rows(search)
    |> Stream.map(&String.trim/1)
    |> Stream.map(&parse/1)
    |> Stream.filter(fn parsed -> parsed |> elem(0) == :ok end)
    |> Enum.map(&parsed_line_to_map/1)
  end

そして、それは基本的な考えです.
作業ファイルはgithub
< div >