DjangoでDockerコンテナのログをテキストエリアに流し込む(Server-Sent Events)

はじめに

DjangoでDockerコンテナのログをテキストエリアに出力する方法をまとめます。

前提

Dockerコンテナの操作には docker-py を使います。

github.com

django==3.0.8
docker==4.2.0

ビューの実装

ビュー tail_log を実装します。
このビューは、URLパラメタ container_name で指定したコンテナのログを docker-pyContainer.logsで取得し、 StreamingHttpResponseで返します。

import time
from datetime import datetime

import docker
from django.http import StreamingHttpResponse


def tail_log(request, container_name):
    client = docker.from_env()
    container = client.containers.get(container_name)
    return StreamingHttpResponse(
        _stream_docker_logs(container),
        content_type='text/event-stream'
    )


def _stream_docker_logs(container):
    for line in container.logs(
            stream=True, since=datetime.utcfromtimestamp(time.time())):
        yield 'data: {}\n\n'.format(line.decode('utf-8'))
        time.sleep(0.1)

urls.py にはTemplateViewで画面を描画する index と、上記の views.tail_log を呼び出す tail_log を追記します。

from django.urls import path
from django.views.generic.base import TemplateView

from .views import tail_log

urlpatterns = [
    path('<str:container_name>', TemplateView.as_view(template_name='tail_docker_logs/index.html'), name='index'),
    path('docker/logs/<str:container_name>', tail_log, name='tail_log'),
]

テンプレートの実装

テキストエリア( console )にEventSource.onmessageで取得したDockerコンテナのログを出力します。
ログにHTML要素が含まれるような場合はエスケープしないと正しく表示されません。

<html>
  <head>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  </head>
  <body>
    <p>> docker logs -f {{ request.resolver_match.kwargs.container_name }}</p>
    <textarea id="console" rows="30" cols="100" readonly></textarea>
    <script>
    $(function() {
      let ES = new EventSource(
        "{% url 'tail_log' request.resolver_match.kwargs.container_name %}"
      );
      ES.onmessage = function(e) {
        $('#console').append(`${e.data}\n`);
        $('#console').scrollTop(
          $('#console')[0].scrollHeight - $('#console').height()
        );
      };
    });
    </script>
  </body>
</html>

実行例

コンテナ ab3c3b9660b0 のログを確認しています。

f:id:hiroki-sawano:20200726004326g:plain

さいごに

実装したコードは以下のリポジトリに置きました。

github.com