前回のエントリに対して
職場の上司から「リフレクションでもできるんちゃうん?(ドヤ)」と言われたので試してみた。
import java.io.FilterWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
 
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.helpers.SyslogQuietWriter;
import org.apache.log4j.helpers.SyslogWriter;
import org.apache.log4j.net.SyslogAppender;
 
public class SyslogAppendarTest {
 
  public void execute(String syslogHost, int syslogFacility)
      throws NoSuchFieldException, IllegalAccessException {
    Layout layout = new PatternLayout("%d|%p|%m");
    SyslogAppender appender = new SyslogAppender(layout, syslogHost, syslogFacility);
 
    // Writer を入れ替える
    setEncodeSpecifiableWriter(appender, "UTF-8");
 
    Logger logger = Logger.getLogger(SyslogAppendarTest.class);
    logger.addAppender(appender);
    logger.setLevel(Level.WARN);
    logger.warn("警告ですよ!!");
  }
 
  private static void setEncodeSpecifiableWriter(
      SyslogAppender appender, String encode) throws NoSuchFieldException, IllegalAccessException {
    // SyslogAppender が持っている SyslogQuietWriter を取得
    Field sqwField = SyslogAppender.class.getDeclaredField("sqw");
    sqwField.setAccessible(true);
    SyslogQuietWriter writer = (SyslogQuietWriter) sqwField.get(appender);
 
    // SyslogQuietWriter が持っている SyslogWriter を EncodeSpecifiableSyslogWriter に入れ替える
    Field outField = FilterWriter.class.getDeclaredField("out");
    outField.setAccessible(true);
    outField.set(writer, new EncodeSpecifiableSyslogWriter(appender.getSyslogHost(), encode));
  }
 
  // SyslogWriter を継承した Writer を実装する。
  private static class EncodeSpecifiableSyslogWriter extends SyslogWriter {
 
    private final String encode;
 
    public EncodeSpecifiableSyslogWriter(String syslogHost, String encode) {
      super(syslogHost);
      this.encode = encode;
    }
 
    // write をオーバーライドする。string.getBytes(encode) 以外は親クラスと同じ。
    @Override
    public void write(String string) throws IOException {
      if (getDs() != null && getAddress() != null) {
        byte[] bytes = string.getBytes(encode);
        int bytesLength = bytes.length;
        if (bytesLength >= 1024)
          bytesLength = 1024;
        DatagramPacket packet = new DatagramPacket(
            bytes, bytesLength, getAddress(), getPort());
        getDs().send(packet);
      }
    }
 
    private DatagramSocket getDs() {
      try {
        Field field = SyslogWriter.class.getDeclaredField("ds");
        field.setAccessible(true);
        return (DatagramSocket) field.get(this);
      } catch (Exception e) {
        e.printStackTrace();
        return null;
      }
    }
 
    private InetAddress getAddress() {
      try {
        Field field = SyslogWriter.class.getDeclaredField("address");
        field.setAccessible(true);
        return (InetAddress) field.get(this);
      } catch (Exception e) {
        e.printStackTrace();
        return null;
      }
    }
 
    private int getPort() {
      try {
        Field field = SyslogWriter.class.getDeclaredField("port");
        field.setAccessible(true);
        return ((Integer) field.get(this)).intValue();
      } catch (Exception e) {
        e.printStackTrace();
        return 514;
      }
    }
  }
 
  public static void main(String[] args)
      throws NoSuchFieldException, IllegalAccessException {
    new SyslogAppendarTest().execute(
        "xxx.xxx.xxx.xxx", SyslogAppender.getFacility("LOCAL0"));
  }
}
できた。(ドヤ)
なるほど、log4j 自体には手を入れたくないけれどエンコード指定したい、というときには
強引ながらも手はある、ということのようだ。

Copyright© 2011-2021 Shunsuke Otani All Right Reserved .