添加到Java Logger的FileHandler接收LogRecords两次,并且当isLoggable返回true时,并不总是将输出发送到File

时间:2014-05-29 07:26:17

标签: java logging filter bukkit

我的目标是使其发送到Level.WARNING的所有LogRecords和更高Bukkit's Logger被发送到我指定的日志文件中,以及控制台(按照惯例)。我已经看到一些网页显示了为单个Logger提供多个处理程序的示例,并且无法查看我的代码如何不同。我已经搞砸了一段时间,似乎无法弄清楚导致在控制台输出和文件输出中观察到的奇怪行为的原因。

每当玩家右键点击一个项目框架并且预期在" TEST"之后记录时,我会在NullPointerException中生成ItemFrameListener在文件输出中。过滤器似乎正在接收" TEST"两次,每次都应该返回true,但只记录一次文件(这是我原本期待的)。第一个NPE第一次返回true,然后第二次返回false,因为它的哈希值现在在集合中,但是没有任何内容添加到输出文件中。后续NPE仅根据控制台调用过滤器一次,每次都按预期返回false。

如果我添加

if (true) {
  return true;
}

在LogFilter中的初始调试输出之后,过滤器仍然声称根据控制台输出接收每个消息两次,但是对于之后的所有后续NPE都是如此,这与之前不同。 " TEST"在这种情况下,两个NPE都记录在日志文件中(以便'" TEST",NPE1和NPE2)。

如果我完全删除过滤器(首先不要添加过滤器),那么所有Level.WARNING及更高版本的LogRecords都会将其放入日志文件中(与之前的测试一样,它们只出现一次每个在文件中。)

主要插件类:

package uk.co.mysterymayhem.bukkittestplugin;

import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;

public class BukkitTestPlugin extends JavaPlugin {

  public final GiantDamageListener giantDamageListener = 
          new GiantDamageListener(this);
  public final SpawnerBreakPlaceListener spawnerBreakPlaceListener = 
          new SpawnerBreakPlaceListener(this);
  public final LogBreakListener logBreakListener = new LogBreakListener(this);
  public final ItemFrameListener playerInteractListener = 
          new ItemFrameListener(this);

  @Override
  public void onEnable() {
    PluginManager pm = this.getServer().getPluginManager();
    pm.registerEvents(giantDamageListener, this);
    pm.registerEvents(spawnerBreakPlaceListener, this);
    pm.registerEvents(logBreakListener, this);
    pm.registerEvents(playerInteractListener, this);
    try {
      if(!this.getDataFolder().exists()){
        this.getDataFolder().mkdir();
      }
      FileHandler errorHandler = new FileHandler(this.getDataFolder().getPath() + 
              System.getProperty("file.separator") + "errorlog%u.log");
      errorHandler.setLevel(Level.WARNING);
      errorHandler.setFormatter(new java.util.logging.SimpleFormatter());
      errorHandler.setFilter(new LogFilter());
      Bukkit.getLogger().addHandler(errorHandler);
    } catch (IOException | SecurityException e) {
      Bukkit.getLogger().info("Error occured adding logging handler");
      e.printStackTrace();
    }
    Bukkit.getLogger().info("Enabled testing plugin");
    Bukkit.getLogger().warning("TEST");
  }

  @Override
  public void onDisable() {
    Bukkit.getLogger().info("Disabled testing plugin");
    this.getServer().getPluginManager().disablePlugin(this);
  }
}

LogFilter类:

package uk.co.mysterymayhem.bukkittestplugin;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Filter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.bukkit.Bukkit;

public class LogFilter implements Filter {

  private final HashSet<List<Byte>> previousErrors;

  public LogFilter() {
    previousErrors = new HashSet<>();
  }

  @Override
  public boolean isLoggable(LogRecord record) {

    // Debug message
    Bukkit.getLogger().log(Level.INFO, "{0} [number {1}:{2}]",
            new Object[]{record.toString(), record.getSequenceNumber(),
              record.getLoggerName()});

    // Prepare empty string
    String currentMessage = "";
    try {
      for (StackTraceElement element :
              record.getThrown().getCause().getStackTrace()) {
        currentMessage += element.toString() + "\n";
      }
    } catch (NullPointerException e) {
      // No exception or no exception cause, unlikely to be spammed, so return
      // early
      return true;
    }

    // Prepare List
    List<Byte> currentHashList = new ArrayList<>();
    try {
      // Create MD5 hash of exception cause
      MessageDigest digest = MessageDigest.getInstance("MD5");
      byte[] currentHash = digest.digest(currentMessage.getBytes("UTF-8"));

      // Add bytes to Byte list
      for (byte element : currentHash) {
        currentHashList.add(element);
      }
    } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
      // Shh the compiler
    }
    // Try and add this list of Bytes, false if already found, true if new
    // (just return this when working)
    boolean shouldLog = previousErrors.add(currentHashList);
    // Debug output of shouldLog value
    Bukkit.getLogger().log(Level.INFO, "\n\n{0}\n\n", shouldLog);
    return shouldLog;
    //return previousErrors.add(currentHashList);
    //return currentHashList.isEmpty() || previousErrors.add(currentHashList);
  }
}

errorlog0.log内容:

May 29, 2014 7:47:16 AM uk.co.mysterymayhem.bukkittestplugin.BukkitTestPlugin onEnable
WARNING: TEST

服务器日志内容:

[07:47:13] [Server thread/INFO]: Starting minecraft server version 1.7.9
[07:47:13] [Server thread/INFO]: Loading properties
[07:47:14] [Server thread/INFO]: Default game type: SURVIVAL
[07:47:14] [Server thread/INFO]: Generating keypair
[07:47:14] [Server thread/INFO]: Starting Minecraft server on *:25565
[07:47:14] [Server thread/INFO]: This server is running CraftBukkit version git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks (MC: 1.7.9) (Implementing API version 1.7.9-R0.1-SNAPSHOT)
[07:47:14] [Server thread/INFO]: [WorldEdit] Loading WorldEdit v5.6.2
[07:47:14] [Server thread/INFO]: [BukkitTestPlugin] Loading BukkitTestPlugin v1
[07:47:14] [Server thread/INFO]: Preparing level "world"
[07:47:14] [Server thread/INFO]: Preparing start region for level 0 (Seed: -3851174208534506511)
[07:47:14] [Thread-7/INFO]: ----- Bukkit Auto Updater -----
[07:47:14] [Thread-7/INFO]: It appears that you're running a Development Build, when you've specified in bukkit.yml that you prefer to run Recommended Builds.
[07:47:14] [Thread-7/INFO]: If you would like to be kept informed about new Development Build releases, it is recommended that you change 'preferred-channel' in your bukkit.yml to 'dev'.
[07:47:14] [Thread-7/INFO]: With that set, you will be told whenever a new version is available for download, so that you can always keep up to date and secure with the latest fixes.
[07:47:14] [Thread-7/INFO]: If you would like to disable this warning, simply set 'suggest-channels' to false in bukkit.yml.
[07:47:14] [Thread-7/INFO]: ----- ------------------- -----
[07:47:15] [Server thread/INFO]: Preparing start region for level 1 (Seed: 6490719194628450682)
[07:47:15] [Server thread/INFO]: Preparing start region for level 2 (Seed: 6490719194628450682)
[07:47:16] [Server thread/INFO]: [WorldEdit] Enabling WorldEdit v5.6.2
[07:47:16] [Server thread/INFO]: WEPIF: Using the Bukkit Permissions API.
[07:47:16] [Server thread/INFO]: [BukkitTestPlugin] Enabling BukkitTestPlugin v1
[07:47:16] [Server thread/INFO]: Enabled testing plugin
[07:47:16] [Server thread/INFO]: java.util.logging.LogRecord@42f0b157 [number 13:Minecraft]
[07:47:16] [Server thread/INFO]: java.util.logging.LogRecord@42f0b157 [number 13:Minecraft]
[07:47:16] [Server thread/WARN]: TEST
[07:47:16] [Server thread/INFO]: Server permissions file permissions.yml is empty, ignoring it
[07:47:16] [Server thread/INFO]: Done (1.818s)! For help, type "help" or "?"
[07:47:21] [User Authenticator #1/INFO]: UUID of player Mysteryem is 0e0f6b65-7a4c-4a22-a2cb-3f1437c0a1ae
[07:47:21] [Server thread/INFO]: Mysteryem[/127.0.0.1:63217] logged in with entity id 280 at ([world] 69.6618433079422, 65.51186923562442, 262.1120780933005)
[07:47:23] [Server thread/INFO]: Mysteryem right clicked an item frame
[07:47:23] [Server thread/INFO]: java.util.logging.LogRecord@5fe6b30 [number 18:Minecraft]
[07:47:23] [Server thread/INFO]: 

true


[07:47:23] [Server thread/INFO]: java.util.logging.LogRecord@5fe6b30 [number 18:Minecraft]
[07:47:23] [Server thread/INFO]: 

false


[07:47:23] [Server thread/ERROR]: Could not pass event PlayerInteractEntityEvent to BukkitTestPlugin v1
org.bukkit.event.EventException
    at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:294) ~[craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62) ~[craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:501) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:486) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.PlayerConnection.a(PlayerConnection.java:1071) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.PacketPlayInUseEntity.a(SourceFile:55) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.PacketPlayInUseEntity.handle(SourceFile:10) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.NetworkManager.a(NetworkManager.java:157) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.ServerConnection.c(SourceFile:134) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.MinecraftServer.v(MinecraftServer.java:667) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.DedicatedServer.v(DedicatedServer.java:260) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.MinecraftServer.u(MinecraftServer.java:558) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.MinecraftServer.run(MinecraftServer.java:469) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.ThreadServerApplication.run(SourceFile:628) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
Caused by: java.lang.NullPointerException
    at uk.co.mysterymayhem.bukkittestplugin.ItemFrameListener.onPlayerrightClick(ItemFrameListener.java:61) ~[?:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_51]
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.7.0_51]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.7.0_51]
    at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.7.0_51]
    at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:292) ~[craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    ... 13 more
[07:47:24] [Server thread/INFO]: Mysteryem right clicked an item frame
[07:47:24] [Server thread/INFO]: java.util.logging.LogRecord@286863e6 [number 24:Minecraft]
[07:47:24] [Server thread/INFO]: 

false


[07:47:24] [Server thread/ERROR]: Could not pass event PlayerInteractEntityEvent to BukkitTestPlugin v1
org.bukkit.event.EventException
    at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:294) ~[craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62) ~[craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:501) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:486) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.PlayerConnection.a(PlayerConnection.java:1071) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.PacketPlayInUseEntity.a(SourceFile:55) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.PacketPlayInUseEntity.handle(SourceFile:10) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.NetworkManager.a(NetworkManager.java:157) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.ServerConnection.c(SourceFile:134) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.MinecraftServer.v(MinecraftServer.java:667) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.DedicatedServer.v(DedicatedServer.java:260) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.MinecraftServer.u(MinecraftServer.java:558) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.MinecraftServer.run(MinecraftServer.java:469) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    at net.minecraft.server.v1_7_R3.ThreadServerApplication.run(SourceFile:628) [craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
Caused by: java.lang.NullPointerException
    at uk.co.mysterymayhem.bukkittestplugin.ItemFrameListener.onPlayerrightClick(ItemFrameListener.java:61) ~[?:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_51]
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.7.0_51]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.7.0_51]
    at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.7.0_51]
    at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:292) ~[craftbukkit-1.7.9-R0.1-20140503.225437-20.jar:git-Bukkit-1.7.2-R0.3-66-g43d8943-b3078jnks]
    ... 13 more
[07:47:27] [Server thread/INFO]: Mysteryem issued server command: /stop
[07:47:27] [Server thread/INFO]: Mysteryem: Stopping the server..[m
[07:47:27] [Server thread/INFO]: Stopping server
[07:47:27] [Server thread/INFO]: [BukkitTestPlugin] Disabling BukkitTestPlugin v1
[07:47:27] [Server thread/INFO]: Disabled testing plugin
[07:47:27] [Server thread/INFO]: [WorldEdit] Disabling WorldEdit v5.6.2
[07:47:27] [Server thread/INFO]: Saving players
[07:47:27] [Server thread/INFO]: Mysteryem lost connection: Server closed
[07:47:27] [Server thread/INFO]: Mysteryem left the game.
[07:47:27] [Server thread/INFO]: Saving worlds
[07:47:27] [Server thread/INFO]: Saving chunks for level 'world'/Overworld
[07:47:28] [Server thread/INFO]: Saving chunks for level 'world_nether'/Nether
[07:47:28] [Server thread/INFO]: Saving chunks for level 'world_the_end'/The End
[07:47:28] [Thread-4/INFO]: Stopping server

1 个答案:

答案 0 :(得分:0)

如果要将输出从一个日志记录框架发送到另一个日志框架,则应创建自定义Handler

在LogFilter中使用记录器可以创建logging loop。日志框架旨在处理此问题。您必须删除Filter类中所有使用记录器的操作。如果要跟踪输出,则应创建自定义Formatter。然后将其安装在FileHandler上。

public class TraceFormatter extends SimpleFormatter {

@Override
public String format(LogRecord record) {
    StringBuilder buf = new StringBuilder();
    LogRecord trace = new LogRecord(Level.INFO, "{0} [number {1}:{2}]");
    trace.setSourceClassName(record.getSourceClassName());
    trace.setSourceMethodName(record.getSourceMethodName());
    trace.setLoggerName(record.getLoggerName());
    trace.setParameters(new Object[]{record.toString(), 
        record.getSequenceNumber(),
          record.getLoggerName()});

    buf.append(super.format(trace)).append('\n');
    //Insert more code.
    buf.append(super.format(record));

    return buf.toString();
}    

}

要记住的另一件事是任何逃脱&#39; Filter.isLoggable(LogRecord)&#39;被ErrorManager 抓取。您可能希望捕获错误,以免它们泄漏到您的程序中。

public class LogFilter implements Filter {
private final ErrorManager error = new ErrorManager();

@Override
public boolean isLoggable(LogRecord record) {
    try {
        //Insert code.
        return false;
    } catch (RuntimeException re) {
        error.error(new SimpleFormatter().format(record), 
                re, ErrorManager.GENERIC_FAILURE);
        return true;
    }
}

}