0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

時間のかかるコマンドの標準出力と標準エラー出力を一定時間ごとに Slack に送信する

Last updated at Posted at 2025-04-10

やりたいこと

実行に時間のかかるコマンドがある。その進捗を確認するために、出力を n 秒ごとに Slack に送信したい。

  • 要件
    • 標準出力と標準エラー出力をそれぞれ n 秒ごとに Slack に送信する。
    • 差分だけ送信する。つまり、一度 Slack に送信した内容は再度送信しないようにする。

前提

任意の Slack チャンネルにメッセージを送信するための Incoming Webhook URL を持っていること。もし持っていない場合は以下を参考に URL を生成してください。

方法

実行に 30 秒程度かかる show_numbers.rb という Ruby スクリプトがある。

show_numbers.rb
require 'prime'

# 同期モードを有効にしないと、後で使用する Open3.popen3 で出力を読み込めないので注意。
$stdout.sync = true
$stderr.sync = true

(1..30).each_with_index do |n, i|
  sleep(1) unless i == 0

  if Prime.prime?(n)
    $stderr.puts("[WARN] #{n} is a prime number. It's a lonely number.")
    next
  end

  puts("[INFO] #{n}")
end
$ ruby show_numbers.rb
[INFO] 1
[WARN] 2 is a prime number. It's a lonely number.
[WARN] 3 is a prime number. It's a lonely number.
[INFO] 4
[WARN] 5 is a prime number. It's a lonely number.
[INFO] 6
(略)

このスクリプトを実行するために ruby show_numbers.rb というコマンドを外部プログラムのコマンドとして Open3.popen3 を使って実行する。その出力を 10 秒ごとに Slack に送信する。

notify_to_stack.rb
require 'json'
require 'net/http'
require 'open3'

# ここに Slack の Incoming Webhook URL を設定する。
SLACK_WEBHOOK_URL = 'https://hooks.slack.com/services/xxx'

# メッセージを Slack に送信する。
def send_to_slack(message)
  Net::HTTP.post_form(URI(SLACK_WEBHOOK_URL), payload: { text: message }.to_json)
end

# バッファ (このメソッドの下で定義) の内容を Slack に送信し、その後バッファをクリアする。
def send_and_clear_buffers(stdout_buffer:, stderr_buffer:, now: Time.now)
  messages = []

  messages << now
  messages << <<~TEXT.chomp
    stdout
    ```
    #{stdout_buffer.any? ? stdout_buffer.join.chomp : '(none)'}
    ```
  TEXT
  messages << <<~TEXT.chomp
    stderr
    ```
    #{stderr_buffer.any? ? stderr_buffer.join.chomp : '(none)'}
    ```
  TEXT

  send_to_slack(messages.join("\n"))

  stdout_buffer.clear
  stderr_buffer.clear
end

# 標準出力と標準エラー出力のためのバッファ (文字列の一時的な保存場所) を用意する。
stdout_buffer = []
stderr_buffer = []

command = 'ruby show_numbers.rb'
tick = 10

Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
  # 標準出力をバッファに書き込むスレッド。
  stdout_thread = Thread.new(stdout, stdout_buffer) do |out, buffer|
    out.each_line { |line| buffer << line }
  end
  # 標準エラー出力をバッファに書き込むスレッド。
  stderr_thread = Thread.new(stderr, stderr_buffer) do |out, buffer|
    out.each_line { |line| buffer << line }
  end
  # 10 秒ごとにバッファの内容を Slack に送信するスレッド。
  slack_thread = Thread.new(stdout_buffer, stderr_buffer) do |stdout_b, stderr_b|
    loop do
      sleep(tick)
      send_and_clear_buffers(stdout_buffer: stdout_b, stderr_buffer: stderr_b)
    end
  end

  send_to_slack("#{Time.now}: Start")
  # 実行したコマンドが終了するのを待つ。
  wait_thr.join

  # コマンドの実行が終了したら各スレッドを終了し、バッファに残った内容を Slack に送信する。
  [stdout_thread, stderr_thread, slack_thread].each(&:exit)
  send_and_clear_buffers(stdout_buffer: stdout_buffer, stderr_buffer: stderr_buffer)

  send_to_slack("#{Time.now}: Finish")
end

この Ruby スクリプトを実行すると、以下のように Slack にメッセージが送信される。

image.png

参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?