Java Logger Konfiguration zur Laufzeit

takidoso

Erfahrenes Mitglied
Hallo und Halli,
soweit ich mich erinnnere hatte der log4j die Möglichkeit von sich aus seine Konfiguration zu überwachen und entsprechend aufzufrischen.
Wie sieht das eigentlich mit dem "hauseigenen" Java-Logger aus?
ch habe leider beim googeln nicht wirklich was gefunden, entweder weil ich nach dem Falschen suchte oder weil es da nichts gibt.

Gehe ich richtig davon aus, dass man für ein Konfigurationsupdate zur Laufzeit selbst was bauen müsste?

mit neugierigen Grüßen

Takidoso

Nachtrag: ich habe gerade noch ein LogManager.readProperties() gesehen.
Ich nehme an man müsste diese Routine z.B. in einem java.util.Timer laufen lassen.
Oder gibt es da noch elegantere Mittel?
 
Zuletzt bearbeitet:
Hallo,

hier mal ein Beispiel für das dynamische aktualisieren der Logger Konfiguration bei Log4j:
Java:
package de.tutorials;

import java.io.File;
import java.net.URISyntaxException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class DynamicLoggingConfigurationExample implements Runnable {

    static Logger log = Logger
            .getLogger(DynamicLoggingConfigurationExample.class);

    /**
     * @param args
     */
    public static void main(String[] args) {
        Executors.newSingleThreadExecutor().execute(new Runnable() {
            public void run() {
                try {
                    File log4jProperties = new File(getClass().getClassLoader().getResource("log4j.properties").toURI());
                    
                    
                    long lastModified = 0;
                    long currentLastModified = 0;
                    
                    while(true){
                        currentLastModified = log4jProperties.lastModified();
                        if(currentLastModified !=  lastModified){
                            
                            //TODO synchronization!
                            System.out.println("config file change");
                            PropertyConfigurator.configure(log4jProperties.toURI().toURL());
                            lastModified = currentLastModified;
                        }
                        
                        TimeUnit.SECONDS.sleep(1);
                    }
                    
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        Executors.newSingleThreadExecutor().execute(
                new DynamicLoggingConfigurationExample());
    }

    public void run() {
        while (true) {
            if (log.isInfoEnabled()) {
                long time = System.currentTimeMillis();
                log.info("time");
                if (log.isDebugEnabled()) {
                    log.debug("time");
                }
            }
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

log4j.properties im Classpath:
Code:
log4j.rootLogger=DEBUG, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n

Hier ein ähnliches Beispiel für java.util.logging:
Java:
package de.tutorials;

import java.io.File;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

public class DynamicLoggingConfigurationExample implements Runnable {
    static String logConfigFileName = "c:/temp/logging.properties";

    static {
        System.setProperty("java.util.logging.config.file", logConfigFileName);
    }

    Logger log = Logger.getLogger(DynamicLoggingConfigurationExample.class
            .getName());

    /**
     * @param args
     */
    public static void main(String[] args) {
        Executors.newSingleThreadExecutor().execute(new Runnable() {
            public void run() {
                try {
                    File log4jProperties = new File(logConfigFileName);

                    long lastModified = 0;
                    long currentLastModified = 0;

                    while (true) {
                        currentLastModified = log4jProperties.lastModified();
                        if (currentLastModified != lastModified) {

                            // TODO synchronization!
                            System.out.println("config file change");
                            LogManager.getLogManager().readConfiguration();

                            lastModified = currentLastModified;
                        }

                        TimeUnit.SECONDS.sleep(1);
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        Executors.newSingleThreadExecutor().execute(
                new DynamicLoggingConfigurationExample());
    }

    public void run() {
        while (true) {
            if (log.isLoggable(Level.INFO)) {
                long time = System.currentTimeMillis();
                log.info("time " + time);
                if (log.isLoggable(Level.FINEST)) {
                    log.finest("time " + time);
                }
            }

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

logging.properties
Code:
handlers = java.util.logging.ConsoleHandler
.level = INFO #HIER mal auf FINEST umstellen
java.util.logging.ConsoleHandler.level = FINEST
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

Gruß Tom
 
Hi Tom
herzlichen Dank für die modernen Beispiele.
Ich habe bei mir nun den goten alten java.util.Timer + TimerTask verwendet, da ich noch in einer 1.4 Umgebung laufe (jo bei uns ist das noch alles alt).
hat prima geklappt.

tausend Dank

Takidoso
 
Hallo nochmal :-/
habe nun das Phänomen das der java logger meint bei jedem Wechsel des Konfigs eine neue log-Datei zu erstellen. dass ist dann natürlich irgendwie dumm.
kann man den Logger irgendwie darauf anspitzen das nicht zu tun?
gut man könnte die Datei selbst lesen und schauen ob da was geändert wurde. aber vielleicht geht das ja auch eleganter.

schon wieder fragende Grüße

Takidoso
 
Da ich leider keine andere Idee gefunden habe, habe hier mal eine Lösung mit dem Vergleich der alten mit der neuen Konfiguration. Nur wenn Sie ungleich sind bzw. und die Konfiguration aber schon ein mal auf genommen wurde wird der Java-Logger initialisiert.
Java:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;


public class DynamicLoggerConfigurator implements PropertyChangeListener
{
	final static public String PN_DYN_LOG_CONFIG_DELAY  = "logger_reconfig_delay";
	final static public String PN_DYN_LOG_CONFIG_PERIOD = "logger_reconfig_period";
	
	private Logger       m_logger        = null; 
	private byte[]       m_configContent = null;
	
	public void initLoggingConfiguration(Properties props, final Logger logger)
	{
		String numStr = props.getProperty(PN_DYN_LOG_CONFIG_DELAY, "180");
        long delay = 180;
        try
        {
        	delay = Long.parseLong(numStr.trim());
        }
        catch (NumberFormatException e)
        {
        	logger.warning("logger_reconfig_period needs to have just digits but is:"+numStr);
        }
        
        numStr = props.getProperty(PN_DYN_LOG_CONFIG_PERIOD, "120");
        long period = 120;
        try
        {
        	delay = Long.parseLong(numStr.trim());
        }
        catch (NumberFormatException e)
        {
        	logger.warning("logger_reconfig_period needs to have just digits but is:"+numStr);
        }
        
        Timer logConfigTimer = new Timer(true);
    	logConfigTimer.schedule(new TimerTask()
    	                        {
    								public void run()
    								{
    									try
    									{
    										String fname = System.getProperty("java.util.logging.config.file");
    										if (fname == null) 
    										{
    										    fname = System.getProperty("java.home");
    										    if (fname == null) 
    										    {
    										    	throw new Error("Can't find java.home ");
    										    }
    										    File f = new File(fname, "lib");
    										    f = new File(f, "logging.properties");
    										    fname = f.getCanonicalPath();
    										}
    										InputStream in = new FileInputStream(fname);
    										BufferedInputStream bin = new BufferedInputStream(in);
    										int initSize = m_configContent==null ? 4069 : Math.max(4069, m_configContent.length);
    										ByteArrayOutputStream baos = new ByteArrayOutputStream(initSize);
    										BufferedOutputStream  bos  = new BufferedOutputStream(baos,initSize);
    										
    										int c;
    										while ((c = bin.read()) != -1)
    										{
    											bos.write(c);
    										}
    										bos.flush();
    										byte[] configContent = baos.toByteArray();
    										
    										boolean isContentEqual = true;
    										
    										
    										if (m_configContent == null)
    										{
    											// everything is fine since it is the first look on the file
    											// logically the content is identical
    										}
    										else if (m_configContent.length==configContent.length)
    										{
    											for (int i=0; i<m_configContent.length && isContentEqual; i++)
        										{
        											if(configContent[i]!=m_configContent[i])
        											{
        												isContentEqual = false;
        											}
        										}
    										}
    										else
    										{
    											isContentEqual = false;
    										}
    										
    										if (!isContentEqual)
    										{
    											m_configContent = configContent;
        										LogManager.getLogManager().readConfiguration();
    										}
    										
    									}
    									catch (IOException ioEx)
    									{
    										logger.log(Level.SEVERE, "Logger could not reread Configuration", ioEx);
    									}
    								}
    	                        },
    	                        delay*1000,
    	                        period*1000);
    	LogManager.getLogManager().addPropertyChangeListener(this);
    	m_logger = logger;
	}
	public void propertyChange(PropertyChangeEvent evt)
	{
		m_logger.info("Logging-Properties have changed");
	}
}
In der Hoffnung, dass es auch so zuverlässig läuft, wie ich das mir erhoffe (also ohne Gewehr)

Takidoso
 
ich hatte da offenbar noch ein paar Bugs :eek:
jetzt eine Version die es besser macht, merkwürdig bleibt nur, warum der logger, der in der Routine propertyChanged(...) nicht angesprochen wird, zumindest sehe ich da keinerlei Eintrag. :confused:
Java:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;


public class DynamicLoggerConfigurator implements PropertyChangeListener
{
	final static public String PN_DYN_LOG_CONFIG_DELAY  = "logger_reconfig_delay";
	final static public String PN_DYN_LOG_CONFIG_PERIOD = "logger_reconfig_period";
	
	private Logger       m_logger        = null; 
	private byte[]       m_configContent = null;
	
	private static final Logger    logger                 = Logger.getLogger("TEST-LOGGER");
	
	public void initLoggingConfiguration(Properties props, final Logger logger)
	{
		String numStr = props.getProperty(PN_DYN_LOG_CONFIG_DELAY, "180");
        long delay = 180;
        try
        {
        	delay = Long.parseLong(numStr.trim());
        }
        catch (NumberFormatException e)
        {
        	logger.warning("logger_reconfig_period needs to have just digits but is:"+numStr);
        }
        
        numStr = props.getProperty(PN_DYN_LOG_CONFIG_PERIOD, "120");
        long period = 120;
        try
        {
        	period = Long.parseLong(numStr.trim());
        }
        catch (NumberFormatException e)
        {
        	logger.warning("logger_reconfig_period needs to have just digits but is:"+numStr);
        }
        
        Timer logConfigTimer = new Timer(true);
    	logConfigTimer.schedule(new TimerTask()
    	                        {
    								public void run()
    								{
    									try
    									{
    										String fname = System.getProperty("java.util.logging.config.file");
    										if (fname == null) 
    										{
    										    fname = System.getProperty("java.home");
    										    if (fname == null) 
    										    {
    										    	throw new Error("Can't find java.home ");
    										    }
    										    File f = new File(fname, "lib");
    										    f = new File(f, "logging.properties");
    										    fname = f.getCanonicalPath();
    										}
    										System.out.println("logging config file='"+fname+"'");
    										InputStream in = new FileInputStream(fname);
    										BufferedInputStream bin = new BufferedInputStream(in);
    										int initSize = m_configContent==null ? 4069 : Math.max(4069, m_configContent.length);
    										ByteArrayOutputStream baos = new ByteArrayOutputStream(initSize);
    										BufferedOutputStream  bos  = new BufferedOutputStream(baos,initSize);
    										
    										int c;
    										while ((c = bin.read()) != -1)
    										{
    											bos.write(c);
    										}
    										bos.flush();
    										byte[] configContent = baos.toByteArray();
    										
    										boolean isContentEqual = true;
    										
    										
    										if (m_configContent == null)
    										{
    											// everything is fine since it is the first look on the file
    											// logically the content is identical
    											m_configContent = configContent;
    										}
    										else if (m_configContent.length==configContent.length)
    										{
    											for (int i=0; i<m_configContent.length && isContentEqual; i++)
        										{
        											if(configContent[i]!=m_configContent[i])
        											{
        												isContentEqual = false;
        											}
        										}
    										}
    										else
    										{
    											isContentEqual = false;
    										}
    										
    										if (!isContentEqual)
    										{
    											m_configContent = configContent;
        										LogManager.getLogManager().readConfiguration();
        										System.out.println("new Config should have been read");
    										}
    										else
    										{
    											System.out.println("Config has not been changed");
    										}
    										
    									}
    									catch (IOException ioEx)
    									{
    										logger.log(Level.SEVERE, "Logger could not reread Configuration", ioEx);
    									}
    									catch (Throwable th)
    									{
    										logger.log(Level.SEVERE, "generell Problem occured:", th);
    									}
    								}
    	                        },
    	                        delay*1000,
    	                        period*1000);
    	LogManager.getLogManager().addPropertyChangeListener(this);
    	m_logger = logger;
    	System.out.println("Logger-Object="+m_logger);
	}
	public void propertyChange(PropertyChangeEvent evt)
	{
		System.out.println("Logger-Object="+m_logger);
		m_logger.info("Logging-Properties have changed");
	}
	
	static public void main(String args[])
	{
		
		String propsFileName = args.length>0 ? args[0] : null;
		Properties props = new Properties();
		
		
		try
		{
			if (propsFileName!=null)
			{
				props.load(new BufferedInputStream(new FileInputStream(propsFileName)));
			}
			else
			{
				props.setProperty(PN_DYN_LOG_CONFIG_DELAY, "5");
				props.setProperty(PN_DYN_LOG_CONFIG_PERIOD, "5");
			}
		
			DynamicLoggerConfigurator configurator = new DynamicLoggerConfigurator();
			configurator.initLoggingConfiguration(props, logger);
			
			while (true)
			{
				logger.severe("test1");
				logger.warning("test2");
				logger.info("test3");
				logger.fine("test4");
				logger.finer("test5");
				logger.finest("test6");
				Thread.sleep(3000);
			}
			
		}
		catch (Throwable th)
		{
			th.printStackTrace();
		}
	}
}

Nachtrag: Es ist der LogManager, der ganz offenbar seinen PropertyChangeSupport nicht so ganz sauber verwendet. Das hat zur Folge, dass obwohl als PropertyChangeListener gemeldet der Event nicht ankommt :(.
(Oder ich habe irgendwas nicht verstanden)
 
Zuletzt bearbeitet:

Neue Beiträge

Zurück