在使用java.util.logging.Logger时如何将日志写入文本文件

173

我有一个情况需要将我创建的所有日志写入文本文件。

我们正在使用java.util.logging.Logger API生成日志。

我尝试过:

private static Logger logger = Logger.getLogger(className.class.getName());
FileHandler fh;   
fh = new FileHandler("C:/className.log");   
logger.addHandler(fh); 

但是仍然只能在控制台上获取我的日志....


可能是重复的问题:Java日志API,禁用标准输出日志记录 - Raedwald
4
一些答案建议使用FileHandler,就像您最初尝试的一样。需要注意的一点(一个痛苦学习的经验):FileHandler是同步的。这意味着在高度多线程应用程序中,只需传递要记录的对象,其toString()方法调用同步方法,就有可能出现死锁。请注意FileHandler。 - Tim Boudreau
@TimBoudreau 感谢您提供非常有价值的信息。您是否有在多线程应用程序中更安全的建议? - Tharindu Sathischandra
我不知道有什么特别好的处理程序实现适用于此,但自己编写相当容易。基本上:
  1. 最好是无锁的(例如原子引用的链接列表)有序集合/队列,多个线程可以向其中添加日志记录。
  2. 一个线程拉取队列并异步地将记录刷新到文件中。
  3. 尽早添加运行时关闭挂钩,以便它最后运行,关闭该线程并刷新任何未完成的消息。请注意,任何异步记录解决方案在JVM硬崩溃的情况下都可能丢失记录。
- Tim Boudreau
10个回答

280

试试这个样例,它对我有效。

public static void main(String[] args) {  

    Logger logger = Logger.getLogger("MyLog");  
    FileHandler fh;  

    try {  

        // This block configure the logger with handler and formatter  
        fh = new FileHandler("C:/temp/test/MyLogFile.log");  
        logger.addHandler(fh);
        SimpleFormatter formatter = new SimpleFormatter();  
        fh.setFormatter(formatter);  

        // the following statement is used to log any messages  
        logger.info("My first log");  

    } catch (SecurityException e) {  
        e.printStackTrace();  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  

    logger.info("Hi How r u?");  

}

生成输出到MyLogFile.log

Apr 2, 2013 9:57:08 AM testing.MyLogger main  
INFO: My first log  
Apr 2, 2013 9:57:08 AM testing.MyLogger main  
INFO: Hi How r u?

编辑:

要移除控制台处理程序,请使用:

logger.setUseParentHandlers(false);

由于ConsoleHandler已经注册到父记录器中,所有的记录器都是从该父记录器派生而来的。


3
如果我想要保留所有的日志,你能否给我建议?实际上,如果我第二次运行应用程序,这种方法会替换我的文本文件。 - Pankaj
1
如何做到这一点...我在谷歌上搜索,但发现有很多令人困惑的代码...你能帮忙吗? - Pankaj
默认情况下,Level.CONFIG及以上级别的日志将被设计为在控制台上记录。因此,仅在Level.FINE及更低级别的调试消息上使用(并将Level.FINER和Level.FINEST保存为循环中的调试消息)。 - Anders
13
你可以使用FileHandler(path, true)的替代构造函数,使日志追加到现有的日志文件中。 - Sri Harsha Chilakapati
1
@Line 是的。对于这种情况,我通常会保留一个创建记录器实用程序方法。 - Sri Harsha Chilakapati
显示剩余9条评论

22
首先,您在哪里定义了您的记录器,并且从哪个类/方法尝试调用它?这里有一个可以工作的示例,新鲜出炉:
public class LoggingTester {
    private final Logger logger = Logger.getLogger(LoggingTester.class
            .getName());
    private FileHandler fh = null;

    public LoggingTester() {
        //just to make our log file nicer :)
        SimpleDateFormat format = new SimpleDateFormat("M-d_HHmmss");
        try {
            fh = new FileHandler("C:/temp/test/MyLogFile_"
                + format.format(Calendar.getInstance().getTime()) + ".log");
        } catch (Exception e) {
            e.printStackTrace();
        }

        fh.setFormatter(new SimpleFormatter());
        logger.addHandler(fh);
    }

    public void doLogging() {
        logger.info("info msg");
        logger.severe("error message");
        logger.fine("fine message"); //won't show because to high level of logging
    }
}   

在您的代码中,您忘记定义格式化程序,如果您需要简单的格式化程序,您可以按照我上面提到的方式进行操作,但还有另一种选项,您可以自己编写格式化程序。以下是一个示例(只需将其插入到此行 fh.setFormatter(new SimpleFormatter()) 的后面即可):

fh.setFormatter(new Formatter() {
            @Override
            public String format(LogRecord record) {
                SimpleDateFormat logTime = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
                Calendar cal = new GregorianCalendar();
                cal.setTimeInMillis(record.getMillis());
                return record.getLevel()
                        + logTime.format(cal.getTime())
                        + " || "
                        + record.getSourceClassName().substring(
                                record.getSourceClassName().lastIndexOf(".")+1,
                                record.getSourceClassName().length())
                        + "."
                        + record.getSourceMethodName()
                        + "() : "
                        + record.getMessage() + "\n";
            }
        });

或者您可以进行任何其他修改。希望这有所帮助。


15

日志文件的位置可以通过logging.properties文件进行控制。它可以作为JVM参数传递,例如:java -Djava.util.logging.config.file=/scratch/user/config/logging.properties

详情请见:https://docs.oracle.com/cd/E23549_01/doc.1111/e14568/handler.htm

配置文件处理器

要将日志发送到文件,请在logging.properties文件中将FileHandler添加到handlers属性中。这将全局启用文件记录。

handlers= java.util.logging.FileHandler 通过设置以下属性来配置处理程序:

java.util.logging.FileHandler.pattern=<home directory>/logs/oaam.log
java.util.logging.FileHandler.limit=50000
java.util.logging.FileHandler.count=1
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter

java.util.logging.FileHandler.pattern指定输出文件的位置和模式。默认设置为您的主目录。

java.util.logging.FileHandler.limit以字节为单位指定日志记录器写入任何一个文件的最大数量。

java.util.logging.FileHandler.count指定要循环的输出文件数量。

java.util.logging.FileHandler.formatter指定文件处理程序类使用的java.util.logging格式化程序类来格式化日志消息。SimpleFormatter编写简短的“人类可读”日志记录摘要。


要指示Java使用此配置文件而不是$JDK_HOME/jre/lib/logging.properties:

java -Djava.util.logging.config.file=/scratch/user/config/logging.properties

1
非常棒的答案,从我所看到的来看,这是唯一全局的解决方案。我决定使用更改JDK中的logging.properties(尽管在Java 11中,它们位于Java安装目录下的conf目录中)。值得注意的是此类日志文件的默认位置将是user.home/javaX.log(其中user.home是系统属性,而X是按顺序的下一个数字-对于第一个数字为0)。 - Line

6
也许这个是你需要的...
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

/**
 * LogToFile class
 * This class is intended to be use with the default logging class of java
 * It save the log in an XML file  and display a friendly message to the user
 * @author Ibrabel <ibrabel@gmail.com>
 */
public class LogToFile {

    protected static final Logger logger=Logger.getLogger("MYLOG");
    /**
     * log Method 
     * enable to log all exceptions to a file and display user message on demand
     * @param ex
     * @param level
     * @param msg 
     */
    public static void log(Exception ex, String level, String msg){

        FileHandler fh = null;
        try {
            fh = new FileHandler("log.xml",true);
            logger.addHandler(fh);
            switch (level) {
                case "severe":
                    logger.log(Level.SEVERE, msg, ex);
                    if(!msg.equals(""))
                        JOptionPane.showMessageDialog(null,msg,
                            "Error", JOptionPane.ERROR_MESSAGE);
                    break;
                case "warning":
                    logger.log(Level.WARNING, msg, ex);
                    if(!msg.equals(""))
                        JOptionPane.showMessageDialog(null,msg,
                            "Warning", JOptionPane.WARNING_MESSAGE);
                    break;
                case "info":
                    logger.log(Level.INFO, msg, ex);
                    if(!msg.equals(""))
                        JOptionPane.showMessageDialog(null,msg,
                            "Info", JOptionPane.INFORMATION_MESSAGE);
                    break;
                case "config":
                    logger.log(Level.CONFIG, msg, ex);
                    break;
                case "fine":
                    logger.log(Level.FINE, msg, ex);
                    break;
                case "finer":
                    logger.log(Level.FINER, msg, ex);
                    break;
                case "finest":
                    logger.log(Level.FINEST, msg, ex);
                    break;
                default:
                    logger.log(Level.CONFIG, msg, ex);
                    break;
            }
        } catch (IOException | SecurityException ex1) {
            logger.log(Level.SEVERE, null, ex1);
        } finally{
            if(fh!=null)fh.close();
        }
    }

    public static void main(String[] args) {

        /*
            Create simple frame for the example
        */
        JFrame myFrame = new JFrame();
        myFrame.setTitle("LogToFileExample");
        myFrame.setSize(300, 100);
        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        myFrame.setLocationRelativeTo(null);
        JPanel pan = new JPanel();
        JButton severe = new JButton("severe");
        pan.add(severe);
        JButton warning = new JButton("warning");
        pan.add(warning);
        JButton info = new JButton("info");
        pan.add(info);

        /*
            Create an exception on click to use the LogToFile class
        */
        severe.addActionListener(new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent ae) {
                int j = 20, i = 0;
                try {
                    System.out.println(j/i);
                } catch (ArithmeticException ex) {
                    log(ex,"severe","You can't divide anything by zero");
                }

            }

        });

        warning.addActionListener(new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent ae) {
                int j = 20, i = 0;
                try {
                    System.out.println(j/i);
                } catch (ArithmeticException ex) {
                    log(ex,"warning","You can't divide anything by zero");
                }

            }

        });

        info.addActionListener(new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent ae) {
                int j = 20, i = 0;
                try {
                    System.out.println(j/i);
                } catch (ArithmeticException ex) {
                    log(ex,"info","You can't divide anything by zero");
                }

            }

        });

        /*
            Add the JPanel to the JFrame and set the JFrame visible
        */
        myFrame.setContentPane(pan);
        myFrame.setVisible(true);
    }
}

5
import java.io.IOException;
import org.apache.log4j.Appender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;

/**
 * @author Kiran
 * 
 */
public class MyLogger {

    public MyLogger() {
    }

    public static void main(String[] args) {
        Logger logger = Logger.getLogger("MyLog");
        Appender fh = null;
        try {
            fh = new FileAppender(new SimpleLayout(), "MyLogFile.log");
            logger.addAppender(fh);
            fh.setLayout(new SimpleLayout());
            logger.info("My first log");
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        logger.info("Hi How r u?");
    }
}

1
谢谢,这对我很有帮助,但是现在Log4j 2已经发布了,我不得不在网站上四处寻找1.2版本。 - SoluableNonagon

5
有一款名为 log4j for Java 的优秀库可供使用。
它提供了许多功能。点击链接并寻找解决方案。

已经有了log4j2,但必须在项目级别上进行配置。 - Pini Cheyni

3

希望这对人们有所帮助

public static void writeLog(String info) {
    String filename = "activity.log";
    String FILENAME = "C:\\testing\\" + filename;
    BufferedWriter bw = null;
    FileWriter fw = null;
    try {
        fw = new FileWriter(FILENAME, true);
        bw = new BufferedWriter(fw);
        bw.write(info);
        bw.write("\n");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (bw != null)
                bw.close();
            if (fw != null)
                fw.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

3
是的,我花了半个小时和头脑去尝试让log4j写入文件。大多数工具对于它们要解决的问题来说都过于复杂了。 - Mihai Raulea

3
int SIZE = "<intialize-here>"
int ROTATIONCOUNT = "<intialize-here>"

Handler handler = new FileHandler("test.log", SIZE, LOG_ROTATIONCOUNT);
logger.addHandler(handler);     // for your code.. 

// you can also set logging levels
Logger.getLogger(this.getClass().getName()).log(Level.[...]).addHandler(handler);

1

这是我的日志记录类基于被接受的答案

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.*;

public class ErrorLogger
{
    private Logger logger;

    public ErrorLogger()
    {
        logger = Logger.getAnonymousLogger();

        configure();
    }

    private void configure()
    {
        try
        {
            String logsDirectoryFolder = "logs";
            Files.createDirectories(Paths.get(logsDirectoryFolder));
            FileHandler fileHandler = new FileHandler(logsDirectoryFolder + File.separator + getCurrentTimeString() + ".log");
            logger.addHandler(fileHandler);
            SimpleFormatter formatter = new SimpleFormatter();
            fileHandler.setFormatter(formatter);
        } catch (IOException exception)
        {
            exception.printStackTrace();
        }

        addCloseHandlersShutdownHook();
    }

    private void addCloseHandlersShutdownHook()
    {
        Runtime.getRuntime().addShutdownHook(new Thread(() ->
        {
            // Close all handlers to get rid of empty .LCK files
            for (Handler handler : logger.getHandlers())
            {
                handler.close();
            }
        }));
    }

    private String getCurrentTimeString()
    {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        return dateFormat.format(new Date());
    }

    public void log(Exception exception)
    {
        logger.log(Level.SEVERE, "", exception);
    }
}

1
这里是如何从代码中覆盖Logger配置的示例。不需要外部配置文件。

FileLoggerTest.java:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

public class FileLoggerTest {

    public static void main(String[] args) {

        try {
            String h = MyLogHandler.class.getCanonicalName();
            StringBuilder sb = new StringBuilder();
            sb.append(".level=ALL\n");
            sb.append("handlers=").append(h).append('\n');
            LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(sb.toString().getBytes("UTF-8")));
        } catch (IOException | SecurityException ex) {
            // Do something about it
        }

        Logger.getGlobal().severe("Global SEVERE log entry");
        Logger.getLogger(FileLoggerTest.class.getName()).log(Level.SEVERE, "This is a SEVERE log entry");
        Logger.getLogger("SomeName").log(Level.WARNING, "This is a WARNING log entry");
        Logger.getLogger("AnotherName").log(Level.INFO, "This is an INFO log entry");
        Logger.getLogger("SameName").log(Level.CONFIG, "This is an CONFIG log entry");
        Logger.getLogger("SameName").log(Level.FINE, "This is an FINE log entry");
        Logger.getLogger("SameName").log(Level.FINEST, "This is an FINEST log entry");
        Logger.getLogger("SameName").log(Level.FINER, "This is an FINER log entry");
        Logger.getLogger("SameName").log(Level.ALL, "This is an ALL log entry");

    }
}

MyLogHandler.java

import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;

public final class MyLogHandler extends FileHandler {

    public MyLogHandler() throws IOException, SecurityException {
        super("/tmp/path-to-log.log");
        setFormatter(new SimpleFormatter());
        setLevel(Level.ALL);
    }

    @Override
    public void publish(LogRecord record) {
        System.out.println("Some additional logic");
        super.publish(record);
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接