2016-03-30 11 views
1

Ich habe eine benutzerdefinierte Protokollierungsklasse geschrieben, die sich einem PrintWriter wie System.out oder System.err sehr ähnlich verhält. Der Hauptunterschied besteht darin, dass beim Aufruf von myLogger.printf("Hello World!\n"); die Daten nicht direkt in die Protokolldatei, sondern in eine interne Warteschlange geschrieben werden und diese Warteschlange nur über die Methode flush(); in die Ausgabedatei geleert wird. So Verwendung des Codes sieht wie folgt aus:Wie kann sichergestellt werden, dass die Methode aufgerufen wird, bevor das Objekt zerstört wird?

myLogger.println("Line 1."); 
myLogger.println("Line 3."); 
myLogger.println("Actually that was Line 2. THIS is Line 3!"); 
myLogger.flush(); 

, die einen Ausgang geben sollte, die (Art) wie folgt aussieht:

2016-03-30 15:44:45::389> Line 1. 
2016-03-30 15:44:45::390> Line 3. 
2016-03-30 15:44:45::395> Actually that was Line 2. THIS is Line 3! 

jedoch das Problem, das ich habe, ist, wenn Benutzer Fehler machen. Sie vergessen nämlich, flush() aufzurufen, und die Daten, die sie in den Logger geschrieben haben, werden nie in die Datei geschrieben, und das Programm wird geschlossen, ohne die Daten zu löschen.

Ich kann nicht nach jedem einzelnen Aufruf flush, weil es den Zweck des Schreibens dieser Klasse an erster Stelle besiegen würde. Und das automatische Spülen des Systems zu verwalten wäre in ähnlicher Weise selbstzerstörerisch.

Meine Idee war, einen Anruf zu flush() innerhalb der finalize() Methode des Objekts zu setzen, aber wie ich von several other articles auf dieser Seite gelesen habe, gibt es keine Garantie, dass finalize() jemals aufgerufen werden.

Nur für Klarheit willen, ist es das, was die flush() Methode wie folgt aussieht:

public void flush() { 
    open(); 
    while(!unwrittenLogs.isEmpty()) { 
     String line = unwrittenLogs.poll(); 
     log.print(line); 
    } 
    close(); 
} 

private void open() { 
    if(log == null) { 
     try { 
      log = new PrintWriter(new FileOutputStream(logFile, true)); 
     } catch (FileNotFoundException e) { 
      System.err.printf("Unable to open Log File.\n%s\n",e.getMessage()); 
      e.printStackTrace(System.err); 
     } 
    } 
} 

private void close() { 
    if(log != null) { 
     log.close(); 
     log = null; 
    } 
} 

Also, was ist meine beste Option, um sicherzustellen, dass der Logger gespült wird, bevor das Programm beendet wird?

+0

Es gibt zwei Dinge, die Sie tun können: Flush nach jedem X schreibt, wo X konfigurierbar ist. Sie können auch [shutdown hook] (https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook-java.lang.Thread-) registrieren, die 'flush () 'ein letztes Mal. Der Shutdown-Hook wird auch nicht immer aufgerufen, da die VM möglicherweise einfach abstürzt. – biziclop

+0

Es gibt auch ein subtiles Datenrennen in Ihrem Code: wenn 'flush()' aufgerufen wird, kann ein anderer Thread noch Nachrichten in Ihrem Logger veröffentlichen. Wenn also 'close()' aufgerufen wird, können 'ungeschriebeneLogs' noch Einträge haben wird still fallen gelassen. – biziclop

Antwort

2

Setzen Sie Ihre Methode in die Finalisierung Methode, wie folgt aus:

@Override protected void finalize() throws Throwable { 
    // TODO Auto-generated method stub 
    // do some crazy stuff here 
    super.finalize(); 
} 

Dies und als Beispiel für die Objektzerstörung.

Für die Daten vor der JVM Speichern wird heruntergefahren, Verwendung Shutdown Haken:

public static void main(final String[] args) { 
    Runtime.getRuntime().addShutdownHook(new Thread() { 
     @Override public void run() { 
      // TODO Auto-generated method stub 
      // do the other crazy stuff in here 
      super.run(); 
     } 
    }); 
} 

Aber beide werden nicht 100% sicher zu bedienen sein. 1) Sie können die JVM ohne alle Finalizers seinen Lauf 2) schließen Wenn Sie den JVM-Prozess über die Task-Manager/kill-Signal, der Shutdown-Haken wird nicht

ausgelöst
+0

Beachten Sie, dass es keinen Sinn macht, beides zu tun. –

+1

Wie meinst du das? Sie befassen sich mit verschiedenen Aspekten im Objektlebenszyklus. Einer handelt mit GC, der andere mit der normalen Abschaltung der JVM. –

+1

@WillHartung Das ist richtig, aber wenn Sie einen Shutdown-Hook registrieren, ist das Objekt sowieso nicht für GC geeignet, bis die VM heruntergefahren wird. Siehe John Bollingers Antwort, warum dies der Fall ist. – biziclop

2

Sie haben zwei verschiedene Fragen gestellt werden töten, wie sichergestellt werden dass die Methode flush() Ihrer Protokollierung aufgerufen wird, bevor das Objekt erfasst wird, und wie sichergestellt wird, dass es aufgerufen wird, bevor das Programm beendet wird. Wie Sie aus Ihrer Recherche erfahren haben, könnte der Logger nicht gesammelt werden, bevor das Programm beendet wird, daher kann ein Finalizer nicht garantieren, dass die Methode aufgerufen wird.

Wenn Sie damit zufrieden wären, dass der Logger vor dem Herunterfahren der VM nicht für die GC geeignet ist, können Sie eine shutdown hook mit der Laufzeit registrieren, die den Logger löscht. Ein solcher Hook muss einen Verweis auf den Logger enthalten, und die Laufzeit wird einen Verweis auf den Hook (einen nicht gestarteten Thread) bis zum Herunterfahren beibehalten, sodass der Logger für GC nicht geeignet bleibt, bis die Laufzeitumgebung die Shutdown-Hooks ausführt .

0

Ein alternativer Ansatz zu einem wegspülbaren Verfasser:

  1. eine TransferQueue<LogItem> Implementierung verwendet
  2. schafft ein separaten Protokollschreiber Gewinde
  3. in der Warteschlange ein take() tun. [Blockierung]
  4. offene Protokolldatei, gegebenenfalls Abschneiden tun und Rotation log als
  5. Schreib genommen Element, abtropfen lassen weitere Einträge mit poll() [non-blocking]
  6. flush und Schließen Protokolldatei an dieser Stelle gewünschten
  7. wenn (Anwendung noch ausgeführt wird) Then goTo 3

Dieser Ansatz hat mehrere Vorteile:

  • die Protokollgewinde werden die IO und bündig Kosten leiden, nicht die Fäden Ihre Geschäftslogik ausführt
  • es ist Thread-sicher, da es so lange, schleusenfreien
  • log Artikel Vorlage nur ein Schreib Thread ist als die TransferQueue Implementierung ist, wie LinkedTransferQueue
  • Der Logger-Thread hält die VM am Leben, bis Protokolle geschrieben werden, unter der Annahme thread.setDaemon(false), was der Standardwert ist.
Verwandte Themen