My Redmine Gen.2 ログダウンロード機能を改善しています


My Redmine

原田です。今回は弊社が提供するプロジェクト管理のクラウドサービス「My Redmine」の ログダウンロード機能 の改善について書きます。

My Redmineのログダウンロード機能では、過去5ヶ月間のログを年月ごとにダウンロードできます。

My Redmine Gen.2 ログダウンロード機能
ログダウンロード機能の画面

そのログダウンロード機能で、ログファイルが解凍できない事象がありました。

$ gzip -d xxxxx-YYYYMM-production.log.gz
gzip: invalid compressed data--crc error
gzip: xxxxx-YYYYMM-production.log.gz: uncompress failed

原因と改善策を検討しました

今回発生した問題を調査したところ以下のことが分かりました。

メモリ不足が原因の一つと考えて、次のように改善することにしました。

ログデータを1行単位でファイルに追記する

現状は以下のように処理しており、全てのログデータを収集後ファイルに出力していました。今回はGB単位のログデータが存在していましたのでファイルに出力する前にOut of memoryエラーになっていた可能性も考えられます。

# ログデータ収集
logs =
  src_objs.each_with_object([]) do |key, arr|
    gz_raw_data = AWS::S3::Bucket.object(key).get.body.read
    gz_raw_data.each_line do |message|

      #
      # 各種処理
      #

      arr << message
    end
  end

# ログファイル出力
obj = AWS::S3::Bucket.object(dest_key)
obj.upload_stream do |write_stream|
  Zlib::GzipWriter.wrap(write_stream) do |gzw|
    gzw << logs.join("\n")
    gzw.finish
  end
end

これを1行読み込むたびにファイルに出力(追記)するよう見直しました。

Dir.mktmpdir(nil, '/tmp') do |dir|
  src_objs.each do |key|
    gz_raw_data = AWS::S3::Bucket.object(key).get.body.read
    gz_raw_data.each_line do |message|

      #
      # 各種処理
      #

      # ログファイル出力(追記)
      File.open(tmp_log_file_path, 'a') do |f|
        f.puts(message)
      end
    end
  end

  #
  # ログファイルを圧縮
  #

  # 圧縮ファイルをアップロード
  obj = AWS::S3::Bucket.object(dest_key)
  obj.upload_file(tmp_gzip_file_path)
end

圧縮処理はコマンドで行う

現状はzlibライブラリを使用してログファイルの圧縮を実施していますが、コマンド(gzip)を実行することで同様の圧縮は可能です。コマンドは多くの方々により日々改善されています。自前で圧縮処理を作成するより速度や効率の面から見てもコマンドを使用するべきと思いました。

任意のコマンドが実行できる Open3.capture3 メソッドを使用することで、標準出力・標準エラー・終了ステータスの取得が可能です。

# gzip圧縮
o, e, s = Open3.capture3("gzip %s" % tmp_log_file_path)

ログファイルを分割する

一ヶ月分のログデータがGB単位になることを私は想定していませんでした。利用頻度が多くなると出力されるログデータも増加すると思います。

そこでログデータを一定のファイルサイズで分割し、分割したファイルをまとめて(アーカイブし)圧縮するtgz(tar.gz)形式に変更します。現状の圧縮形式(gzip)で問題なく運用できていますので、この「一定のファイルサイズ」を大きな値(例えば、500MB)にしておく必要があります。

Dir.mktmpdir(nil, '/tmp') do |dir|
  tmp_log_file_path = nil
  tmp_file_paths = []
  
  src_objs.each do |key|
    gz_raw_data = AWS::S3::Bucket.object(key).get.body.read
    gz_raw_data.each_line do |message|

      #
      # 各種処理
      #

      unless tmp_log_file_path
        tmp_log_file_path = "#{TMP_LOG_FILE_PATH}.%03d" % (tmp_file_paths.size + 1)
        tmp_file_paths << tmp_log_file_path
      end

      # ログファイル出力(追記)
      File.open(tmp_log_file_path, 'a') do |f|
        f.puts(message)
        
        if FileTest.size(f) >= SPLIT_FILE_SIZE
          tmp_log_file_path = nil
        end
      end
    end
  end

  if tmp_file_paths.size == 1
    # gzip圧縮(現状と同じ)
  else
    # tgz圧縮
    o, e, s = Open3.capture3(
      "tar -czf %s %s" % [tmp_compression_file_path, tmp_file_paths.join(' ')]
    )
  end

  #
  # 圧縮ファイルをアップロード
  #
end

リソースの制約はあるけど私はサーバーレスが好きです

AWS EC2のようにサーバーを管理することが無く、アプリケーションの実装に集中できますので私にはFargate・Lambda・Step Functionsが性に合っています。今回のことでサーバーレスコンピューティングをもっと理解しなければと痛感しました。

【スタッフ募集中】
弊社ではAWSを活用したソリューションの企画・設計・構築・運用や、Ruby on Rails・JavaScriptフレームワークなどを使用したアプリケーション開発を行うスタッフを募集しています。詳細はこちら
弊社での勤務に関心をお持ちの方は、知り合いの弊社社員・関係者を通じてご連絡ください。

My Redmine

こちらの記事もオススメです!
お客様のご指摘から「My Redmine」を改善できました
お客様のご協力により、サービスを改善できたことはとてもうれしいです。
仮想マシン+Windowsの代わりにAmazon AppStream2.0を使ってみるのはどうでしょう?
MacでWindowsアプリを動作させるアイデアの一つとしてAppStreamを使ってみました。
オープンソースカンファレンス 2022 でセミナー発表しました
OSC 2022オンラインFallで「はじめてのプロジェクト管理ツール〜Redmine超入門〜」を発表。
Redmineで構築されている国民年金基金連合会の「他年金調査 事業所回答システム」を調べてみた
Redmineを利用して構築されている国民年金基金連合会の「他年金調査 事業所回答システム」を調べてみました。
RedmineのSettingクラスのコードを読んでみる
RedmineのSettingクラスの実装があまり見ない感じで面白かったので紹介します。
ファーエンドテクノロジーからのお知らせ(2024/05/09更新)
2024年5月11日 オライリー本の全冊公開日のお知らせ(もくもく勉強会も同時開催)
ファーエンドテクノロジーが所蔵するオライリー本(全冊)公開日のご案内です。公開日には「もくもく勉強会」も同時開催します。
入門Redmine 第6版 出版記念企画セミナー「Redmineのアクセス制御」【2024/5/30開催】
入門Redmine 第6版(2024年3月23日発売)の書籍から「Redmineのアクセス制御」について解説します。
My Redmine 初回ご契約で「入門Redmine 第6版」プレゼントのお知らせ
Redmineのクラウドサービス「My Redmine」を初めてご契約いただいたお客様にRedmine解説書「入門Redmine 第6版」を進呈いたします。
2024年度ブランドパートナーに島根県在住のモデル ユイさんを継続起用
ユイさん(モデルスタジオミューズ所属)をファーエンドテクノロジーの2024年度ブランドパートナーとして継続して起用します。
Redmineの最新情報をメールでお知らせする「Redmine News」配信中
新バージョンやセキュリティ修正のリリース情報、そのほか最新情報を迅速にお届け