package jp.ehobby.logging; import java.io.IOException; import java.net.SocketException; import java.net.UnknownHostException; import java.util.HashMap; import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; import jp.ehobby.info.SystemInfo; import jp.ehobby.util.number.NumberUtilities; import jp.ehobby.util.syslog.Syslog; import jp.ehobby.util.syslog.SyslogFacility; import jp.ehobby.util.syslog.SyslogSeverity; /** * Syslog に書き出す Handler. * SyslogHandler として以下の設定が可能です. * <pre> * jp.ehobby.logging.SyslogHandler.server = Syslogサーバのアドレス (デフォルト:localhost) * jp.ehobby.logging.SyslogHandler.port = Syslogサーバのポート (デフォルト:514) * jp.ehobby.logging.SyslogHandler.tag = Syslogのタグ (デフォルト:指定なし) * jp.ehobby.logging.SyslogHandler.facility = Syslog分類 (デフォルト:USER_LEVEL) * jp.ehobby.logging.SyslogHandler.FINEST = Syslog重要度 (デフォルト:DEBUG) * jp.ehobby.logging.SyslogHandler.FINER = Syslog重要度 (デフォルト:DEBUG) * jp.ehobby.logging.SyslogHandler.FINE = Syslog重要度 (デフォルト:DEBUG) * jp.ehobby.logging.SyslogHandler.CONFIG = Syslog重要度 (デフォルト:INFORMATIONAL) * jp.ehobby.logging.SyslogHandler.INFO = Syslog重要度 (デフォルト:INFORMATIONAL) * jp.ehobby.logging.SyslogHandler.WARNING = Syslog重要度 (デフォルト:WARNING) * jp.ehobby.logging.SyslogHandler.SEVERE = Syslog重要度 (デフォルト:CRITICAL) * * Syslogのタグのキーが存在しない場合、タグが出力されません. * Syslogのタグのキーが存在する場合、「[PID]」がログに書き出されます. * Syslogのタグが指定されている場合、「指定されたタグ[PID]」がログに書き出されます. * Syslog分類には {@link jp.ehobby.util.syslog.SyslogFacility} の定数名を指定可能です. * Syslog重要度には {@link jp.ehobby.util.syslog.SyslogSeverity} の定数名を指定可能です. * </pre> * * @author kei-n */ public class SyslogHandler extends Handler { /** Level リスト. */ private static final Level[] LEVEL_LIST = { Level.FINEST, Level.FINER, Level.FINE, Level.CONFIG, Level.INFO, Level.WARNING, Level.SEVERE }; /** デフォルトの Severity. */ private static final SyslogSeverity[] DEFAULT_SEVERITYS = { SyslogSeverity.DEBUG, SyslogSeverity.DEBUG, SyslogSeverity.DEBUG, SyslogSeverity.INFORMATIONAL, SyslogSeverity.INFORMATIONAL, SyslogSeverity.WARNING, SyslogSeverity.CRITICAL }; /** Syslog 出力用. */ private final Syslog syslog; /** Syslog レベル. */ private final HashMap<Level, SyslogSeverity> levelMap; /** Syslog 分類. */ private final SyslogFacility facility; /** ローカルホスト名. */ private final String hostName; /** Tag名. */ private final String tag; /** syslog が close されたか否かを表す. */ private boolean closed; /** * SyslogHandler を構築します. * * @throws SocketException Socketエラー * @throws UnknownHostException 不明なホスト */ public SyslogHandler() throws SocketException, UnknownHostException { LogManager logMgr = LogManager.getLogManager(); String cName = getClass().getName(); this.syslog = createSyslog(logMgr, cName); this.facility = createFacility(logMgr, cName); this.levelMap = createLevelMap(logMgr, cName); this.hostName = SystemInfo.getLocalHostName(); this.tag = logMgr.getProperty(cName + ".tag"); //$NON-NLS-1$ this.closed = false; // その他設定 configure(logMgr, cName); } @Override public void publish(final LogRecord record) { if (this.closed) { return; } if (!isLoggable(record)) { return; } Level level = record.getLevel(); SyslogSeverity severity = this.levelMap.get(level); Formatter fmt = getFormatter(); String message = record.getMessage(); if (fmt != null) { message = fmt.format(record); } try { this.syslog.logger( this.facility, severity, this.hostName, this.tag, message); } catch (IOException e) { // Nothing to do. } } @Override public void flush() { // Nothing to do. } @Override public synchronized void close() throws SecurityException { if (this.closed) { return; } this.closed = true; try { this.syslog.close(); } catch (IOException e) { // NOP } } //////////////////////////////////////////////////////////////////////////// // // Syslog の設定 // /** * Syslogの設定を実施します. * * @param mgr LogManager * @param cName クラス名 */ private void configure(final LogManager mgr, final String cName) { Formatter newFormatter = createFormatter(mgr, cName); setFormatter(newFormatter); } /** * 設定ファイルに指定されたサーバ、ポートにログを書き込む Syslog を構築します. * * @param mgr LogManager * @param cName クラス名 * @return Syslog * @throws SocketException ソケットエラー * @throws UnknownHostException 不明なホスト */ private static Syslog createSyslog(final LogManager mgr, final String cName) throws SocketException, UnknownHostException { // Syslog サーバ String server = mgr.getProperty(cName + ".server"); //$NON-NLS-1$ if (server == null) { server = "localhost"; //$NON-NLS-1$ } // Syslog サーバポート String portStr = mgr.getProperty(cName + ".port"); //$NON-NLS-1$ int port = Syslog.SERVER_PORT; if (portStr != null) { port = (int) NumberUtilities.longValue(portStr); } return new Syslog(server, port); } /** * Syslog に出力する分類を生成します. * * @param mgr LogManager * @param cName クラス名 * @return SyslogFacility */ private static SyslogFacility createFacility(final LogManager mgr, final String cName) { // Syslog 分類設定 String facilityName = mgr.getProperty(cName + ".facility"); //$NON-NLS-1$ if (facilityName == null) { facilityName = SyslogFacility.USER_LEVEL.name(); } return SyslogFacility.valueOf(facilityName); } /** * LevelとSyslogの重要度対応MAPを生成します. * * @param mgr LogManager * @param cName クラス名 * @return LevelとSyslogの重要度対応MAP */ private static HashMap<Level, SyslogSeverity> createLevelMap(final LogManager mgr, final String cName) { HashMap<Level, SyslogSeverity> map = new HashMap<>(); for (int i = 0; i < LEVEL_LIST.length; i++) { String key = LEVEL_LIST[i].getName(); String val = mgr.getProperty(cName + '.' + key); SyslogSeverity severity = DEFAULT_SEVERITYS[i]; if (val != null) { try { severity = SyslogSeverity.valueOf(val); } catch (IllegalArgumentException e) { // NOP } } map.put(LEVEL_LIST[i], severity); } return map; } /** * Formatter を返します. * Formatter が指定されていない場合、そのままのメッセージを出力する Formatter を返します. * * @param mgr LogManager * @param cName クラス名 * @return Formatter */ private static Formatter createFormatter(final LogManager mgr, final String cName) { String val = mgr.getProperty(cName + ".formatter"); //$NON-NLS-1$ if (val != null) { try { Class<Formatter> clz = (Class<Formatter>) ClassLoader.getSystemClassLoader().loadClass(val); return clz.newInstance(); } catch (Exception e) { // Nothing to do. } } return new SyslogDefaultFormatter(); } } /** * SyslogのデフォルトFormatter. * 特にフォーマットを実施せず、そのままメッセージを出力する. */ class SyslogDefaultFormatter extends Formatter { @Override public String format(LogRecord record) { return record.getMessage(); } }