Hatena::Grouprubyist

Going My Ruby Way このページをアンテナに追加 RSSフィード

Ruby ロゴ (C) Ruby Association LLC

2011年08月05日(金)

SSHサーバを作る (その3) ログ出力 (2)

| 23:22 | SSHサーバを作る (その3) ログ出力 (2) - Going My Ruby Way を含むブックマーク はてなブックマーク - SSHサーバを作る (その3) ログ出力 (2) - Going My Ruby Way SSHサーバを作る (その3) ログ出力 (2) - Going My Ruby Way のブックマークコメント

「挑戦:SSHサーバを作る」です。

logger を作ろうと思います。

ログのレベル

ログのメッセージは重要度のレベル(=severity)付けをします。

(2011/08/10 [注] warn は warning に変更しました。開発中なので今後も仕様は変更される可能性があります)

重要度レベル
最高 fatal
error
warn
info
最低 debug

logger には閾値(threshold)を状態として持たせます。

threshold より severity のレベルが低い場合はメッセージの出力を抑止します。例えば、threshold が「warn」の場合、severity が「fatal」「error」「warn」のメッセージは出力しますが、それ以外は出力しません。

(例)

重要度レベル thresholdseverity
最高 fatal ログ出力
error ログ出力
warn <--ここ ログ出力
info ---
最低 debug ---

fatal」より高いレベルの「any」を設けます。これは threshold への設定を想定したもので severity で使うべきではありません。

「debug」より高いレベルの「none」を設けます。これは severity への設定を想定したもので threshold で使うべきではありません。

重要度レベル thresholdseverity想定する使用例
any 使わない メッセージをすべて抑止するためにthreshold に設定する
最高 fatal 致命的なエラー
error 回復可能なエラー
warn 警告
info 情報
最低 debug (デバッグ用の)詳細情報
none 使わない log メソッドでのメッセージ出力を(一時的など)無効にする場合に severity に設定する

logger の仕様

こんな感じで使用します。

   logger = GMRW::SSH2::Server::Logger.new(out)

   logger.log(:info) { "This is a information." }

out は実際にメッセージを書き出すオブジェクトです。Logger や IO などです。

log メソッドでは severity とメッセージを指定します。

    logger.log(:info)
    logger.log { "This is the second message." } # severity は「info」

メッセージは log メソッドに与えたブロックを評価した値です。評価値が偽値(falsy)な場合やブロックを省略した場合は、メッセージを出力しません。

severity を省略した場合は、直前の severity と同じものが設定されます。

デフォルトの severity を debug にしたい場合、以下のようにします。

    logger.log(:debug) { nil }
       または
    logger.log(:debug) { false }
       または
    logger.log(:debug) {}
       または
    logger.log(:debug)
       または
    logger.log:debug

severity アクセサで明示的に設定することもできます。

    logger.severity = :debug
       または
    logger.severity(:debug)
       または
    logger.severity:debug

同様に threshold の設定もできます。

    logger.threshold = :error
       または
    logger.threshold(:error)
       または
    logger.threshold:error

threshold と severity の初期値は「info」です。

log メソッドで与えたブロックの評価値は format に格納した proc で変換してから出力します。

format は設定可能です。メッセージを全て大文字にする場合は以下のようにします。

     logger.format {|s| s.upcase }

logger 実装

では、GMRW::SSH2::Server::Logger のコードです。

gmrw/ssh2/server/logger.rb (前回の続き)

class GMRW::SSH2::Server::Logger < Array
  def initialize(out)
    @out = out

    replace([:any, :fatal, :error, :warn, :info, :debug, :none])
  end

  property :threshold, ':info'
  property :severity, :threshold

  def log(sev=severity)
    active = slice(0..(index(threshold)||-1)).include?(severity(sev))
    msg    = yield if block_given?

    write(threshold, severity, format[msg]) if msg && active
  end

  property :format, 'proc {|s| s.to_s }'

  private
  attr_reader :out

  def write(thr, sev, msg)
    out.puts "#{sev}: #{msg}"
  end
end

# vim:set ts=2 sw=2 et fenc=UTF-8:

GMRW::SSH2::Server::Logger は out (実際にログ出力するオブジェクト) を $stdout などの IO と想定しています。

サブクラスで write をオーバーライドして out に適した出力を行ないます。

gserver 用 logger の GMRW::SSH2::Server::Logger::GServerLogger は以下のように実装しました。

gmrw/ssh2/server/logger/gserver_logger.rb

#!/usr/bin/env ruby
# -*- coding: UTF-8 -*-

require 'gmrw/ssh2/server/logger'

module GMRW::SSH2::Server
  class Logger::GServerLogger < Logger
    private
    def write(thr, sev, msg)
      out.debug = (thr == :debug)
      out.send(:log, "#{sev}: #{msg}")
    end
  end
end

# vim:set ts=2 sw=2 et fenc=UTF-8:

----

次回は Server::Service で logger を使うコードを追加していきます。