# Loggen mit log4j



## irfl (1. Juli 2008)

Hallo,
ich bin leider eine blutige Anfängerin was Programmierung betrifft. Im Moment bin ich gerade dabei etwas zum Loggen meines Programmablauf zu erstellen. Beim Suchen zu diesem Thema bin ich auf log4j gestoßen und habe mich auch schon mit Dokus und Tutorials auseinandergesetzt, allerdings noch nicht das gefunden was ich verwenden möchte 

Und das wäre meine Frage: Wie erhalte ich die Logdaten (keine selbsterstellten Messages)? Also z.B. Socket connected (ich habe zwar keine Socket, aber ihr wisst sicher was ich meine).

Vielen Dank schon einmal im Voraus.

Lg irfl


----------



## irfl (2. Juli 2008)

Hallo,
ich bins noch mal. Vielleicht könnt ihr mir ja anhand meines Codes eine Auskunft geben. (Ich bin wirklich schon sehr verzweifelt:´()


```
public void logging(){
        try{
	String name = "testtest.log";
	PatternLayout layout = new PatternLayout("%r [%t] %-5p %c -%m %n");
	FileAppender fa	= new FileAppender(layout, name );
	logger.addAppender(fa);
	logger.setLevel(Level.ALL);
        }
      catch (Exception ex){
	System.out.println(ex);
        }
}
```

Also die Datei testtest.log wird bereits erstellt, allerdings wird hierbei nur etwas eingefügt, wenn ich es direkt anweise, also z.B. logger.debug(message). Allerdings möchte ich doch gern, dass das Programm selbst die Angaben liefert, die in die Datei geschrieben werden sollen. Stell ich mir das ganze zu einfach vor Kann man das irgendwie umsetzen?

Vielen Dank schon mal für die Rettung!
mfg irfl


----------



## zerix (2. Juli 2008)

Hallo,

na solche Ausgaben musst du selbst erzeugen. 

```
logger.info("Socket connected")
```

http://www.laliluna.de/log4j-tutorial_de.html

MFG

Sascha


----------



## irfl (2. Juli 2008)

Naja aber wenn ich die Logdatei selbst schreiben muss, macht das doch gar keinen Sinn oder? Woher will ich denn dann wissen, wann da ein Fehler auftritt usw
(Steh ich da jetzt irgendwie auf dem Schlauch oder so? )


----------



## zerix (2. Juli 2008)

Dann frag ich mal so. Woher soll denn die Logging-API das wissen? Du hast doch das Programm geschrieben und weißt, wo Fehler auftreten können. 

Wo du zum Beispiel loggen kannst, ist in einem catch-Block. Wenn eine Exception an dieser Stelle auftreten sollte, kannst du die Exception mitloggen. So hast du einen Nachweis über diese Exception. Du hast ja bei vielen Programmen keine Console in denen du die Excpetion siehst. Damit diese dann festgehalten werden können, kann man sie halt mit loggen.

MFG

Sascha


----------



## irfl (2. Juli 2008)

Ok, da muss ich mal schauen, wie ich das umsetzen kann. Danke erst mal für die Infos. Zwei Probleme hab ich aber schon. Beim Programmablauf wird die Ausgabe auf der Konsole und in die Datei umgesetzt. Eigentlich möchte ich die Ausage nur in die Datei. Zusätzlich wird in die Datei statt des Datums und der Uhrzeit nur eine 0 ausgegeben und beim Neustart wird das geloggte einfach an die Datei angehangen. Gibt es da einen Befehl, mit der die Datei überschrieben wird?


----------



## zerix (2. Juli 2008)

Dann nutz mal für die Zeit %d.

Warum möchtest du denn die Datei immer wieder überschreiben?

```
FileAppender fa	= new FileAppender(layout, name, false );
```

Poste mal bitte deinen Code.

MFG

Sascha


----------



## irfl (2. Juli 2008)

```
public class LogFileWrite {
	
      private static Logger logger = Logger.getRootLogger();
	
      public void logging(){
	try{
	         String name = "testtest.log";
	         PatternLayout layout = new PatternLayout("%d [%t] %-5p %c - %m %n");
	         FileAppender fa	= new FileAppender(layout, name, false );
	         logger.addAppender(fa);
	         logger.setLevel(Level.ALL);
	         logger.debug("Programm gestartet");
	}
	catch (Exception ex){
	          System.out.println(ex);
	}
	}
}
```

Also so siehts bis jetzt aus (ich hab deine Vorschläge schon eingearbeitet..Meldung wird allerdings immer noch auf Konsole ausgegeben ). Eigentlich wollte ich es so machen, dass der Name der log-Datei aus einem Textfeld ausgelesen wird, um sie dann zu erstellen, aber das hatte bis jetzt noch nicht funktioniert

mfg irfl


----------



## zerix (2. Juli 2008)

Könntest du mal die Konfigurationsdatei mal posten?

MFG

Sascha


----------



## zeja (2. Juli 2008)

Folgendes funktioniert bei mir:

```
package de.tutorials;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;

public class LogFileWrite {

	private static final Logger classLogger = Logger
			.getLogger(LogFileWrite.class);

	public static void main(String[] args) {
		try {
			final String name = "testtest.log";
			final Logger rootLogger = Logger.getRootLogger();
			final PatternLayout layout = new PatternLayout(
					"%d [%t] %-5p %c - %m %n");
			final FileAppender fa = new FileAppender(layout, name, false);
			rootLogger.addAppender(fa);
			rootLogger.setLevel(Level.ALL);
			classLogger.debug("Programm gestartet");
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}
```

@Sascha: Wenn man das so macht braucht man keine Konfigurationsdatei.


----------



## zerix (2. Juli 2008)

Bei ihr funktioniert es ja auch. Die Ausgaben werden aber zusätzlich auf der Konsole ausgegeben, wenn ich es richtig verstanden hab. Jetzt wollte ich mal schauen, ob vielleicht ein ConsoleAppender in der Config-File deklariert ist.

MFG

Sascha


----------



## zeja (2. Juli 2008)

Kann natürlich sein wenn man noch zusätzlich ein Configfile lädt, ja.

rootLogger.removeAllAppenders()


----------



## Oliver Gierke (2. Juli 2008)

Darf ich fragen, warum du das Setup des Loggers programmatisch machen willst? Im Normalfall trägt man seine Applikation durch verschiedene Phase (Test, Integration, Produktivbetrieb). Da werden häufig extrem unterschiedliche Loggingkonfigurationen verwendet - daher gehört sowas meiner meinung nach in ein Konfigurationsfile: log4j.properties bzw. log4j.xml im Classpath. Wie das genau funktioniert steht in oben verlinktem Tutorial.

Zu deiner Frage, wie du an die Fehlermeldung kommst: im Normalfall wird eine Logausgabe notwendig, wenn ein bestimmtes Ereignis auftritt. Du hattest oben den Fall einer Socket Connection angesprochen. Hier hast du eigentlich zwei Fälle: wenn die Connection nicht aufgebaut werden kann fliegt im Allgemeinen eine Exception. Dann kannst du im catch-Block deinem Logger die Exception mitgeben:


```
...
} catch (IOException e) {
  logger.warn("Could not open socket connection: ", e);
}
```

Im Erfolgsfall schreibst du halt einfach "Created socket connection!" oder so in dein log.

Vielleicht noch ein paar Best Practices bzgl. Logging:

1. Benutz im Code nie die API eines konkreten Loggingproviders. Günstiger ist es, ein allgemeines Loggingframework wie commons-logging zu verwenden und dessen API zum Erzeugen von loggern zu nutzen. Commons-logging findet zur Laufzeit herraus, welche Logging API wirklich im Classpath liegt und benutzt diese.

Nochmal kurz: du brauchst Commons-Logging + den Logger deiner Wahl (hier wohl Log4j) im Classpath und verwendest im Code aber NUR commons logging.

2. Aufpassen bei teueren String Konkatenationen beim Bauen der Message
Grundsätzlich hat es sich als Best Practice etabliert Logausgaben so im Code zu schreiben:


```
if (log.isDebugEnabled()) {
  log.debug("Foo" + /*... ganz viele andere Strings */ + "Bar");
}
```

Der Grund ist, das dieses Zusammensetzen der String unter umständen recht teuer werden kann (z.B. wenn man auf vielen Objekten toString ruft). Da diese String unter umständen umsonst zusammengebaut werden (wenn z.B. das Level DEBUG gar nicht aktiv ist) baut man diese if-clause ein. Und da Softwareentwickler nichts mehr schätzen als Konsistenz mancht man das im Allgemeinen immer so .

Gruß
Ollie


----------



## zerix (2. Juli 2008)

So gehts auch, aber wäre natürlich sinnvoller, wenn man diesen aus der Config-File löscht, anstatt ihn erst hinzuzufügen und dann wieder zu entfernen. ;-)

MFG

Sascha


----------



## irfl (2. Juli 2008)

Ja an sich funktioniert es, bis auf das die Ausgabe auch in der Console erfolgt. Kann man das noch irgendwie unterdrücken? Und es kommt eine java.lang.NullPointerException wenn ich auf das Textfeld für den Namen zugreifen will.

```
String name = GUI.logdatei.getText();
```


----------



## zerix (2. Juli 2008)

Bitte meine Posts lesen. 
Poste bitte mal die Config-Datei, wenn du eine hast. 

Alleine an der geposteten Zeile, kann man nicht sagen, warum eine NullpointerException auftritt. Du solltest schon die Exception posten und den Quelltext der Klasse GUI. 

MFG

Sascha


----------



## irfl (2. Juli 2008)

Ich hatte es vorher mit der der Config-Datei probiert, allerdings hat es nicht so recht funktioniert. (Habe mich ja wie gesagt schon mit Dokus auseinandergesetzt) Welche Zeile ist denn da jetzt überflüssig.

Das waren jetzt ganz schön viele Infos auf einmal*information overload*


----------



## zerix (2. Juli 2008)

Dann mach es einfach wie zeja gesagt hat. Entferne alle Appander.
Oder schau dir nochmal das mit den Configfiles an.

Du sagtest vorhin, dass eine NullPointerException auftritt. Dann solltest du die Exception  und die Klasse GUI mal posten.

MFG

Sascha


----------



## irfl (2. Juli 2008)

```
package kernfunktion;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;


public class GUI extends JFrame implements ActionListener {
	
	private static final long serialVersionUID = -7157848825008430852L;
	
	private static final String konvertPath = "";
	
	private JPanel srcPanel;
	
	private JPanel srcPanel2;
		
	public static JTextField konvertierungsdatei;
	
	public static JTextField logdatei;
	
	private JButton konvert;
	
	
	public GUI(){
		this.setTitle("Konvertierungsplattform");		
		this.init();
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE );
		this.setSize(600,200);
		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
		setLocation((dim.width - getWidth())/2, (dim.height - getHeight())/2);
		this.setVisible(true);
	}
	
	public void init(){
		
		this.getContentPane().setLayout(new BorderLayout());
		//Menu
		JMenuBar menuBar = new JMenuBar(); 
		JMenu file = new JMenu( "Datei" ); 
		menuBar.add( file ); 
		this.setJMenuBar(menuBar);
		
		Action exitAction = new AbstractAction( "Beenden" ) { 
			public void actionPerformed( ActionEvent e ) { 
				LogFileWrite.logger.debug("Anweisung: " + e.getActionCommand());
				System.exit( 0 ); 
			}
		} ;
		file.add( exitAction );
		
		JPanel center = new JPanel(new GridLayout(2, 1));

		this.srcPanel = new JPanel();
		this.srcPanel.setBorder(new TitledBorder("Quell-Datei"));
		this.konvertierungsdatei = new JTextField("", 30);
		JButton srcBrowse = new JButton("Durchsuchen");
		srcBrowse.setActionCommand("browseSrc");
		srcBrowse.addActionListener(this);

		this.srcPanel.add(this.konvertierungsdatei);
		this.srcPanel.add(srcBrowse);
		
		this.srcPanel2 = new JPanel();
		this.srcPanel2.setBorder(new TitledBorder("Log-Datei"));
		this.logdatei = new JTextField("", 30);
		JButton destBrowse = new JButton("Durchsuchen");
		destBrowse.setActionCommand("browseDest");
		destBrowse.addActionListener(this);

		this.srcPanel2.add(this.logdatei);
		this.srcPanel2.add(destBrowse);

		center.add(this.srcPanel);
		center.add(this.srcPanel2);
		this.getContentPane().add(center, BorderLayout.CENTER);
		
		JPanel south = new JPanel(new FlowLayout());

		this.konvert = new JButton("konvertieren");
		this.konvert.setContentAreaFilled(false);
		this.konvert.setFocusPainted(false);
		this.konvert.setActionCommand("konvert");
		this.konvert.addActionListener(this);
		
		south.add(this.konvert);
		this.getContentPane().add(south, BorderLayout.SOUTH);
			
	}

	public void actionPerformed(ActionEvent e){
		LogFileWrite.logger.debug("Anweisung: " + e.getActionCommand());
		if (e.getActionCommand().equals("browseSrc")){
			JFileChooser fc = new JFileChooser(konvertPath); 
			MyFileFiltertxt myfilefilter = new MyFileFiltertxt();
			fc.setFileFilter( myfilefilter); 
			int state = fc.showOpenDialog( null ); 
			if ( state == JFileChooser.APPROVE_OPTION )
			{ 
			File file = fc.getSelectedFile(); 
			konvertierungsdatei.setText( file.getName() ); 
			}
		}
		else if (e.getActionCommand().equals("browseDest")){
			JFileChooser fc = new JFileChooser(konvertPath); 
			MyFileFilterlog myfilefilter = new MyFileFilterlog();
			fc.setFileFilter( myfilefilter); 
			int state = fc.showOpenDialog( null ); 
			if ( state == JFileChooser.APPROVE_OPTION )
			{ 
			File file = fc.getSelectedFile(); 
			logdatei.setText(file.getName());
			}
		}
		else if (e.getActionCommand().equals("konvert")){
			
			TxtToIni test = new TxtToIni();
			
			try {
				test.dateiInArray(konvertierungsdatei.getText());
			} catch (FileNotFoundException e1) {
				LogFileWrite.logger.error("Datei nicht gefunden");
				JOptionPane.showMessageDialog( null, "Die Datei konnte nicht gefunden werden!" );
			} catch (IOException e1) {
				JOptionPane.showMessageDialog( null, e1.getMessage() );
			}
			
		}
	}

}
```

(die Pfade hab ich mal entfernt)


----------



## zerix (2. Juli 2008)

Na die Exception solltest du auch posten.

MFG

Sascha


----------



## irfl (2. Juli 2008)

hier noch die Datei, die war beim letzten Eintrag irgendwie nicht dabei...


----------



## irfl (2. Juli 2008)

Welche Exception? Das ist bis jetzt meine gesamte GUI-Klasse. (Das ist mein erstes größeres Programm... )


----------



## zerix (2. Juli 2008)

irfl hat gesagt.:


> Ja an sich funktioniert es, bis auf das die Ausgabe auch in der Console erfolgt. Kann man das noch irgendwie unterdrücken? Und es kommt eine java.lang.NullPointerException wenn ich auf das Textfeld für den Namen zugreifen will.
> 
> ```
> String name = GUI.logdatei.getText();
> ```



Na die Exception die du hier erwähnt hast.

MFG

Sascha


----------



## irfl (2. Juli 2008)

Das ist meine momentane Konsolenausgabe:
2008-07-02 10:43:42,893 [main] DEBUG root - Programm gestartet
java.lang.NullPointerException
2008-07-02 10:43:44,534 [AWT-EventQueue-0] DEBUG root - Anweisung: browseSrc
2008-07-02 10:43:47,455 [AWT-EventQueue-0] DEBUG root - Anweisung: konvert
2008-07-02 10:43:51,579 [AWT-EventQueue-0] INFO  root - Die Konvertierung war erfolgreich

Es wird halt keine log-Datei angelegt wegen der Exception.


----------



## shutdown (2. Juli 2008)

> Allerdings möchte ich doch gern, dass das Programm selbst die Angaben liefert, die in die Datei geschrieben werden sollen



Wenn es dir wirklich nur darum geht, die Fehler zu loggen, die dein Programm von alleine erkennt (Exceptions) und nicht noch zusätzlich Log-Informationen mitgeben willst, wäre vielleicht auch das eine Möglichkeit:


```
try {
           System.setErr(new PrintStream("./fehler.txt"));
} catch (FileNotFoundException e) { e.printStackTrace(); }
```

Ist von der Api her vielleicht nicht ganz so mächtig, aber für einfaches Logging durchaus brauchbar. Leitet im Grunde die FehlerInformationen von der Konsole in eine Textdatei um. Die könntest du dann noch mit Datum und Uhrzeit versehen und du hast eine Logdatei pro Programmstart.


----------



## irfl (21. Juli 2008)

Hallo,
nach einigen anderen Aufgaben, bin ich wieder bei meiner Programmieraufgabe gelandet. Bis auf das Schreiben der Logdatei funktioniert jetzt alles. Habe jetzt so viel hin und her probiert, dass folgende Fehler kommen:

log4j:WARN File option not set for appender [stdout].
log4j:WARN Are you using FileAppender instead of ConsoleAppender?
log4j:ERROR No output stream or file set for the appender named [stdout].

Warum versteh ich leider nicht so richtig

Das ist meine Klasse:

```
public class LogFileWrite {
	
	public static Logger logger = Logger.getLogger(LogFileWrite.class);
	
	public void logging(String dateiName){
		try{
			PatternLayout layout = new PatternLayout("%d [%t] %-5p %c - %m %n");
			FileAppender fa	= new FileAppender(layout, dateiName, false);
			logger.addAppender(fa);
			logger.setLevel(Level.ALL);
		}
		catch (Exception ex){
			System.out.println(ex);
		}
	}
	
}
```

Anbei noch die Propertiesdatei. Hier habe ich zum Test einiges auskommentiert. Allerdings haben die ganzen Versuche keinen Erfolg gebracht
Hoffe ihr könnt mir helfen, damit ich das endlich fertig bekomme...
Danke euch schon mal wie verrückt!

LG irfl


----------



## Oliver Gierke (21. Juli 2008)

irfl hat gesagt.:


> log4j:WARN File option not set for appender [stdout].
> log4j:WARN Are you using FileAppender instead of ConsoleAppender?
> log4j:ERROR No output stream or file set for the appender named [stdout].
> 
> Warum versteh ich leider nicht so richtig


Nicht ernsthaft, oder? Lies doch mal die Ausgabe!


> Are you using FileAppender instead of ConsoleAppender?


Und nun schau in deinen Code 

REINHAUN!


----------



## irfl (21. Juli 2008)

Also ich hab das schon übersetzen können. Ich möchte ja auch den FileAppender benutzen, da ich eine Ausgabe in eine Datei möchte (die wird auch aktualisiert - also Bearbeitungsdatum/-zeit, aber es steht nichts drin). Auf der Console möchte ich nichts. Deswegen versteh ich auch nicht, warum ich die Meldung bekomme!


----------



## irfl (22. Juli 2008)

*Langsam der Verzweiflung nahe*

Durch endloses rumprobieren hab ich scheinbar die Fehler erweitert

log4j:WARN File option not set for appender [stdout].
log4j:WARN Are you using FileAppender instead of ConsoleAppender?
log4j:WARN No appenders could be found for logger (kernfunktion.LogFileWrite).
log4j:WARN Please initialize the log4j system properly.

Verstehe einfach nicht woran es liegt.  Habe auch schon alle möglichen Tutorials durchprobiert, aber das Ganze will einfach nicht laufen...


----------



## takidoso (23. Juli 2008)

ich denke mal Log4J will damit sagen, dass er properties erwartet, damit er weiß was er machen soll.
suche doch mal im netz nach log4j.properties vielleicht erhälst Du da eine Idee, was fehlt.


----------



## irfl (23. Juli 2008)

Ich hab jetzt noch mal alles neu eingebunden usw. Jetzt funktioniert es fast! Ich hab es einerseits probiert die Datei durch Vorgabe in den Properties schreiben zu lassen (da ist das Problem, dass ich den Namen nicht variabel gestalten kann ) und wenn ich es über den Code mache, schreibt er nichts rein (außer ab und an 2 Meldungen, wenn Programm beenden über das Menü gewählt wird - nach der erfolgreichen Konvertierung).


----------

