ひょんなことからエンコードを指定して Syslog にログ出力をしたいと思い立ったものの、
どうも log4j では対応していないらしく、OS デフォルトエンコードでしか出力できないらしい。
変更個所は少ないのでちょこっと書き換えてみた。
rsyslog の設定
CentOS 6.0 を使って検証してみようとしたところ、デフォでインストールされているのは rsyslog だった。
rsyslog を扱うのは初めてだったので以下設定メモ。
まずは設定ファイル /etc/rsyslog.conf を編集。
デフォルトでは外部からのログを受け付けていないので、以下のコメントアウトされている箇所を外す。
# provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 514
適当にファシリティとテンプレートを追加。
$template testtemplate,"%timegenerated%,%msg%\n"
local0.* /var/log/local0.log;testtemplate
設定ファイルを編集したのでservice rsyslog reloadでリロードする。
logger -p local0.info "test"のようにテストしてみる。
/var/log/local0.log に「test」と出力されていることを確認。設定はできているようだ。
現状の log4j の挙動を確認
Layout layout = new PatternLayout("%d|%p|%m");
String syslogHost = "xxx.xxx.xxx.xxx";
int syslogFacility = SyslogAppender.getFacility("LOCAL0");
SyslogAppender appender = new SyslogAppender(layout, syslogHost, syslogFacility);
Logger logger = Logger.getLogger(Main.class);
logger.addAppender(appender);
logger.setLevel(Level.WARN);
logger.warn("警告ですよ!!");
SyslogAppender にエンコード指定箇所は無いのでこんな感じ。
ソースファイルのエンコードを MS932 で Windows から実行してログ出力された内容を見ると、
菱形はてなマークに表示されている。
(ソースファイルのエンコードを UTF-8 にすると正常に出力される)
log4j のソースを一部書き換える
org.apache.log4j.net.SyslogAppender はフィールドに
org.apache.log4j.helpers.SyslogQuietWriter を持っており、
さらに SyslogQuietWriter が持っている org.apache.log4j.helpers.SyslogWriter が
string.getBytes() したバイト列を送っている。
SyslogAppenderにエンコードを指定したコンストラクタを追加する。
private String encoding;
  public
  SyslogAppender(Layout layout, String syslogHost, int syslogFacility, String encode) {
    this(layout, syslogHost, syslogFacility);
    setEncoding(encode);
  }
  public String getEncoding() {
    return encoding;
  }

  public void setEncoding(String encoding) {
    if(encoding == null)
        return;

    if (sqw != null)
        sqw.setEncoding(encoding);

        this.encoding = encoding;
  }
SyslogAppender から値を受け取るエンコードを SyslogWriter に渡すようにSyslogQuietWriterを変更する。
  public
  void setEncoding(String encoding) {
    if (out instanceof SyslogWriter) {
      ((SyslogWriter) out).setEncoding(encoding);
    }
  }
SyslogWriterにgetter/setter を追加し、write() メソッドを変更。
  public String getEncoding() {
    return encoding;
  }

  public void setEncoding(String encoding) {
    this.encoding = encoding;
  }

  public
  void write(final String string) throws IOException {

    if(this.ds != null && this.address != null) {
        byte[] bytes;
        if (encoding == null) {
            bytes = string.getBytes();
        } else {
            bytes = string.getBytes(encoding);
        }
        //
        //  syslog packets must be less than 1024 bytes
        //
        int bytesLength = bytes.length;
        if (bytesLength >= 1024) {
            bytesLength = 1024;
        }
        DatagramPacket packet = new DatagramPacket(bytes, bytesLength,
                               address, port);
        ds.send(packet);
    }

  }
修正点は以上。
(あとは SyslogAppenderTest にエンコード指定用のテストケースを追加した。)
エンコード指定してログ出力してみる
コンストラクタから指定して、後は同じ。
SyslogAppender appender = new SyslogAppender(layout, syslogHost, syslogFacility, "UTF-8");
文字化けせずログ出力できたことを確認する。
Nov 27 03:00:31,00:53,991|WARN|警告ですよ!!
できたできた!

参考:
rsyslog の設定をしてみる - いますぐ実践! Linuxシステム管理 / Vol.207
rsyslogdの初期設定 - ほげおメモ

Copyright© 2011-2021 Shunsuke Otani All Right Reserved .