Winston: Die Protokollierung in einer Datei schlägt beim Aufrufen von process.exit (0) fehl.

Erstellt am 25. März 2013  ·  87Kommentare  ·  Quelle: winstonjs/winston

Wenn Sie winston für die Protokollierung in einer Datei konfiguriert haben und process.exit (0) in Ihrem Code aufrufen, erstellt winston niemals die Protokolldatei und hängt die Protokollnachrichten an die Datei an.

In einer perfekten Welt würde Winston diesen Randfall gerne bearbeiten und in die Datei schreiben, aber ich wäre auch in der Lage, Flush für eine Logger-Instanz aufzurufen, um einen Flush manuell in die Datei zu erzwingen.

Es mag im Moment eine Möglichkeit geben, manuell zu "spülen", aber es ist weder dokumentiert, noch hatte ich Glück damit, eine Logger-Instanz oder ihre inneren Streams bisher manuell auf eine Weise zu schließen / zu beenden / zu zerstören, die tatsächlich zu den Protokollen führt in eine Datei geschrieben werden ..

var winston = require('winston');
var logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)(),
    new (winston.transports.File)({ filename: 'winston.log' })
  ]
});

logger.info('info logged');
logger.error('error logged');

process.exit(0);
// the two logs get written out to console, but not to the winston.log file/
bug important winston-file

Hilfreichster Kommentar

@danielweck Nein, es ist kaputt: # 1629 # 1504!

6 Jahre, das ist richtig, 6 Jahre für einen Fehler, der als wichtig markiert ist. Es ist grenzwertig beeindruckend.

Alle 87 Kommentare

Im Knoten der Konsole. * Schreiben Methoden asynchron, der Dateitransport ist asynchron. Sie schließen den Prozess, bevor der Knoten überhaupt Daten geschrieben hat.

Am Montag, den 25. März 2013 um 19:45 Uhr schrieb Yuri Zapuchlak:

Wenn Sie winston für die Protokollierung in einer Datei konfiguriert haben und process.exit (0) in Ihrem Code aufrufen, erstellt winston niemals die Protokolldatei und hängt die Protokollnachrichten an die Datei an.
In einer perfekten Welt würde Winston diesen Randfall gerne bearbeiten und in die Datei schreiben, aber ich wäre auch in der Lage, Flush für eine Logger-Instanz aufzurufen, um einen Flush manuell in die Datei zu erzwingen.
Es mag im Moment eine Möglichkeit geben, manuell zu "spülen", aber es ist weder dokumentiert, noch hatte ich Glück damit, eine Logger-Instanz oder ihre inneren Streams bisher manuell auf eine Weise zu schließen / zu beenden / zu zerstören, die tatsächlich zu den Protokollen führt in eine Datei geschrieben werden ..
var winston = erfordern ('winston'); var logger = new (winston.Logger) ({transports: [new (winston.transports.Console) (), new (winston.transports.File) ({Dateiname: 'winston.log'})]}); logger.info ('Info protokolliert'); logger.error ('Fehler protokolliert'); process.exit (0); // Die beiden Protokolle werden in die Konsole geschrieben, nicht jedoch in die Datei winston.log /

- -
Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an (https://github.com/flatiron/winston/issues/228).

Hmm. Wenn in diesem Fall ein Knotenprozess beendet werden muss, aber Informationen unmittelbar vor dem Beenden protokolliert werden müssen, muss die Datei process.exit () in ein setTimeout () mit einer ausreichend großen Zeitdauer eingefügt werden. Dies ist das einzige, was man tun kann, um dies sicherzustellen dass Winston tatsächlich Nachrichten in die Protokolldatei schreibt?

Nur aus Gründen der Klarheit sind console.log und Freunde synchron .

@yzapuchlak Ich habe noch nicht getestet, aber das mache ich auch.

@yzapuchlak setTimeout wird wahrscheinlich ohne Probleme funktionieren, aber ich denke, es wäre ratsam, stattdessen den optionalen Rückrufparameter zu verwenden.

setTimeout von 2000 oder 5000 funktioniert in meinem einfachen Testskript, aber nicht, wenn ich versuche, es in meinem realen Code zu verwenden. Ich weiß also nicht, wie zuverlässig diese potenzielle Lösung sein wird. Ich werde mich später mit diesem Problem befassen, wenn ich auf der optionalen Rückrufseite kein Glück habe.

@dtex Ich habe gerade versucht, den optionalen Rückruf zu verwenden (wie im folgenden Beispielcode gezeigt), aber es führt immer noch nicht dazu, dass die Protokolle in die Datei geschrieben werden. In diesem Fall wird die Datei tatsächlich erstellt, es werden jedoch keine Protokolle darauf geschrieben.

var winston = require('winston');
var logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)(),
    new (winston.transports.File)({ filename: 'winston.log' })
  ]
});

logger.info('winston - info logged');
logger.error('winston - error logged');

logger.info('winston - exiting now', { seriously: true }, function(err, level, msg, meta) {
  console.log('console - CALLING PROCESS EXIT');
  process.exit(0);
});

logger.warn('winston - I probably should only appear in console, not the log file?');

@yzaouchiak Neugierig, ist das Version 0.6.2?

Wenn Sie jetzt dasselbe Problem haben, müssen Sie den Prozess beenden und eine Notiz in der Protokolldatei machen. Es wäre schön, eine Rückrufoption zu haben, mit der wir den Prozess erst beenden könnten, nachdem die Nachricht geschrieben wurde. Oder ist es vielleicht schon vorhanden?

Ich habe das gleiche Problem mit Winston 0.7.2.
Ich habe die Problemumgehung für setTimeout und Callback ohne Erfolg getestet.

Dies ist das gleiche Problem wie # 288 und auch das Problem mit dem log4js-Knoten (https://github.com/nomiddlename/log4js-node/issues/148). Ich denke, das Problem ist, dass die Ereignisschleife nicht ausgeführt wird, sobald der Rückruf process.exit abgeschlossen ist. Ausstehende asynchrone Aufrufe (wie das Schreiben von Dateien) haben also keine Chance, ausgeführt zu werden. Ich würde mich sehr freuen, wenn jemand mir das Gegenteil beweisen und eine Lösung finden könnte.

Ich bin bereit, Zeit zu investieren, um dies zu beheben, wenn wir eine Lösung finden können. Das Hauptproblem scheint zu sein, dass WritableStreams kein Flushing für process.exit () oder eine nicht behandelte Ausnahme garantieren. Wurden alternative Lösungen untersucht? Es scheint, als könnten wir die Protokolle im Speicher zwischenspeichern, bis der WritableStream leer ist. Wenn dann eine process.exit- oder nicht erfasste Ausnahme auftritt, können wir alle Protokolle, die nicht geleert wurden, synchron schreiben. Dies könnte zu doppelten Protokollen führen, würde jedoch zumindest den Protokollverlust verhindern. Irgendwelche Gedanken?

Bezogen auf # 288.

Irgendwelche Updates hier? Immer noch scheitern für mich mit ~ 0.9.0

Ja, ich verwende immer noch den von @yzapuchlak zuvor erwähnten setTimeout () - Hack :(

Ich sehe das sowieso nicht wirklich als einen Fehler von Winston. Wenn Sie process.exit in Ihrem Code haben , haben

Die einzige Möglichkeit, dies zu umgehen, ist die synchrone Protokollierung. Dann können Sie jedoch den gesamten Zweck eines asynchronen Systems als einfache Protokollanweisung bei der Bearbeitung von Anforderungen umgehen.

Ich schlage also vor, dieses Problem mit einem nicht zu behebenden Problem zu schließen. Wenn Sie keine Protokollmeldungen verfälschen möchten, stellen Sie sicher, dass Sie Ihre Anwendung ordnungsgemäß bereinigen und herunterfahren, bevor Sie process.exit aufrufen.

Bearbeiten Auch alle Ihre setTimeout-Hacks sind fehlerhaft und beheben das Problem nicht, da Sie keine Ahnung haben, wie lange es dauern wird, bis alle Daten auf das Betriebssystem geschrieben sind und wie schnell Ihre Festplatten oder SSDs sie verarbeiten. Die einzig vernünftige Lösung besteht darin, das drain -Ereignis des Streams anzuhören und den Prozess zu beenden. Wenn Ihre Anwendung jedoch kontinuierlich protokolliert, wird dies möglicherweise nicht einmal für einen längeren Zeitraum aufgerufen. Dies ist nur ein weiterer Hack, da Sie Ihre Anwendung vor dem Beenden nicht ordnungsgemäß heruntergefahren haben.

Die Sache ist, dass es derzeit keine Möglichkeit gibt, auf das Löschen von Protokollen zu warten (Rückruf / Versprechen / was auch immer). Das Erzwingen der Synchronisierungsprotokollierung würde es auch ermöglichen, Protokolle in process.on('exit') hinzuzufügen

Wenn Sie eine asynchrone Protokollierung während der Lebensdauer Ihrer Anwendung und eine Synchronisierungsprotokollierung während process.exit wünschen, sollten Sie eine neue Winston-Instanz mit einer benutzerdefinierten Dateiimplementierung erstellen, die Synchronisierungsschreibvorgänge ausführt. Und wenn Sie wissen möchten, wann die Dinge gespült werden, sollten Sie das Abflussereignis eines Streams abhören.

Ich werde das versuchen, danke!

Für das, was es wert ist, können Sie die Verwendung von setTimeout umgehen, aber Sie müssen sich das zugrunde liegende _stream schnappen:

// log then exit(1)
logger.error("The thing to log", function(err) {
  var numFlushes = 0;
  var numFlushed = 0;
  Object.keys(logger.transports).forEach(function(k) {
    if (logger.transports[k]._stream) {
      numFlushes += 1;
      logger.transports[k]._stream.once("finish", function() {
        numFlushed += 1;
        if (numFlushes === numFlushed) {
          process.exit(1);
        }
      });
      logger.transports[k]._stream.end();
    }
  });
  if (numFlushes === 0) {
    process.exit(1);
  }
});

Wie oben erwähnt, reicht die Verwendung der Rückruffunktion allein nicht aus, um sie korrekt in die Datei zu leeren. Das fühlt sich für mich wie ein Käfer an.

Beachten Sie, dass ich tatsächlich auf das Ereignis finish höre: https://nodejs.org/api/stream.html#stream_event_finish, anstatt das Ereignis flush oder closed von Winston zu verwenden weil auch das nicht ausreicht, um es korrekt zu protokollieren: /

Probier diese. In meinem Fall funktioniert es ziemlich gut. Anstatt process.exit direkt aufzurufen, lassen wir den Logger process.exit aufrufen, nachdem das Protokoll geleert wurde.

logger.js:

var winston = require('winston');

winston.loggers.add('my-logger', {
    console: {
        level: 'debug',
        colorize: true,
        timestamp: true,
        handleExceptions: true
    },
    file: {
        level: 'info',
        colorize: false,
        timestamp: true,
        filename: 'file.log',
        handleExceptions: true
    }
});

var logger = winston.loggers.get('my-logger');


/* ******* *
 * EXPORTS
 * ******* */

exports.exitAfterFlush = function(code) {
    logger.transports.file.on('flush', function() {
        process.exit(code);
    });
};

exports.info = function() {
    logger.info.apply(this, arguments);
};

exports.warn = function() {
    logger.info.apply(this, arguments);
};

exports.error = function() {
    logger.info.apply(this, arguments);
};

in Ihrem Code:

var logger = require('./logger.js');
logger.exitAfterFlush(0);
logger.info('Done!');

Getestet auf NodeJS v4.1.2 und Winston 1.1.0

Ich benutze etwas ähnliches wie @ da-mkay.

function closeAllLoggersAndDisconnect() {
    "use strict";

    let openCount = 0;

    Object.keys(loggerInstance.transports).forEach((key) => {

        let transportInstance = loggerInstance.transports[key];

        if (typeof transportInstance.close === "function") {
            transportInstance.close();
            openCount++;
        }

        transportInstance.on("closed", () => {
            openCount--;
        });

        setInterval(() => {
            if (openCount === 0) {
                if (process.connected) {
                    process.disconnect();
                }
            }
        }, 100);
    });
}

in Ihrem Code:

winston.error("Error Occured", err, () => {
    closeAllLoggersAndDisconnect();
});

Im Idealfall wäre es schön, wenn ich einfach die close -Methode für loggerInstance aufrufen könnte, aber da dies das closed -Ereignis auslöst, sobald der Aufruf von close -Methode für die zugrunde liegenden Transporte, anstatt nach dem tatsächlichen Schließen, funktioniert dies nicht. Es scheint ein Fehler zu sein, dass der Logger geschlossen werden kann, ohne dass die zugrunde liegenden Transporte vollständig geschlossen werden.

Mit @Kegsay werden Schritte und Protokolle in die Datei geschrieben.

Ich benutze auch

Habe immer noch dieses Problem in Version 2.2.0 .

Ist das noch nicht implementiert? Mehr als 3 Jahre alt. Ich möchte Process Exit nicht verwenden.

Ich verstehe, dass dies kein Problem ist, aber es sollte auf jeden Fall eine Funktion geben, mit der alle Schreibvorgänge in Dateien abgeschlossen und alle offenen Inhalte geschlossen werden können, damit der Prozess von selbst beendet werden kann, ohne ihn explizit aufzurufen. So schwer, solche Dinge in Produktionssystemen zu verwenden.

Echo, dass dies wirklich eine Lösung auf Winstons Seite braucht. Ob dies einen synchronen Dateitransport oder einen Shutdown-Hook zum "sicheren" Schließen von Winston bietet, ist mir eigentlich egal, beides wäre eine zufriedenstellende Lösung für mich. Aber nichts zu haben macht es einfach unmöglich, Protokolle zu schreiben und dann zu beenden und sicher zu sein, dass sie tatsächlich geschrieben werden.

Bei synchronen Transporten wird die Ausführung Ihres Codes blockiert, bis die E / A abgeschlossen ist, was die Leistung einschränkt. Es gibt keine Möglichkeit, einen asynchronen Schreibvorgang in einen blockierenden Schreibvorgang umzuwandeln. Wenn Sie also asynchron in die Protokolle schreiben, müssen Sie warten, bis NodeJS dazu kommt, und in der Zwischenzeit kann anderer JS-Code ausgeführt werden.

Auf der anderen Seite können Versprechen synchron storniert, Zeitüberschreitungen synchron gelöscht, Datei- und Datenbankverbindungen synchron geschlossen werden. Eine bessere (leistungsfähigere) Lösung könnte daher darin bestehen, den Überblick über die Dinge zu behalten, die storniert / gelöscht werden müssen. Schließen und schließen Sie sie im Ausnahmebehandler, der winston.log_and_exit() aufruft, oder im optionalen Rückruf von winston.log_and_exit() .

Sicher wäre es schön, wenn Winston alle Dinge im Auge behalten würde, die Sie im Fehlerfall schließen müssten, aber ein solcher Task-Manager wäre wahrscheinlich eine nützliche Sache für sich und verdient wahrscheinlich einen separaten Rahmen (siehe Curleys Gesetz).

Ich sehe hier keine Auswirkungen auf die Leistung. Der Prozess steht kurz vor dem Abbruch. Winston muss nur die Buchhaltung in den letzten Protokollzeilen durchführen. Während dieser Zeit kann IMO problemlos blockiert werden.

Angesichts des Alters dieses Problems und der Tatsache, dass es eine Problemumgehung dafür gibt, halte ich es nicht gerade für jemanden aus, der sich bald in Winston damit befasst.

Es hört sich so an, als würden Sie und ich über verschiedene Zwecke sprechen (schreiben). Es hört sich so an, als ob Sie daran interessiert sind, endgültige Nachrichten synchron in die Datei zu schreiben, während mein Anliegen darin bestand, sicherzustellen, dass die Protokolle, die unmittelbar vor dem Fehler asynchron geschrieben wurden, in die Datei geschrieben werden.

Das synchrone Schreiben der Nachrichten in eine Datei, die unmittelbar vor dem Fehler aufgetreten ist, erfordert, dass alle Nachrichten synchron geschrieben werden. Dies ist das Leistungsproblem.

Andere waren besorgt darüber, dass andere JS-Codes nach einem Fehler nicht ausgeführt werden können, bevor die letzten Protokolle in die Datei geschrieben wurden.

Eine Option, die möglicherweise alle Anforderungen erfüllt, besteht darin, einen benutzerdefinierten Transport zu schreiben

Ist es tatsächlich schwierig, einen Shutdown-Hook zum Anrufen bereitzustellen, der warten würde, bis alles geleert ist, bevor er zurückkehrt und / oder einen Rückruf anruft? Dies würde so ziemlich jeden Anwendungsfall lösen.

Aber lassen Sie mich auch über meinen Anwendungsfall berichten, den Grund, warum mir synchrone Protokolle egal sind. Tests. Es ist mir egal, ob meine Tests ein oder zwei Sekunden länger dauern. Es ist mir wichtig, ob die Protokolle nicht deterministisch sind, da sie möglicherweise ausgeschrieben werden oder nicht, je nachdem, wie schnell sich der Knoten zum Herunterfahren entscheidet. Das ist schlecht Ich stimme von ganzem Herzen zu, dass Asynchron die Standardeinstellung sein sollte. Ich bin nicht der Meinung, dass dies die einzige Option sein sollte, insbesondere angesichts solcher Probleme innerhalb des Knotens.

@mscharley So macht es log4js - es gibt eine log4js.shutdown -Funktion, mit der Sie einen Rückruf bereitstellen können. Es wird warten, bis alle Appender (Winston nennt sie Transporte, glaube ich) mit dem Schreiben fertig sind, bevor der Rückruf aufgerufen wird. Es scheint gut für meine Benutzer zu funktionieren. Es erfordert Unterstützung von den Appendern, da sie geschrieben werden müssen, damit sie auf Abschaltanrufe reagieren können.

Ich versuche übrigens nicht, log4js zu bewerben oder zu sagen, dass es besser ist als Winston - ich möchte nur darauf hinweisen, dass dieser Ansatz funktionieren kann. Es ist möglicherweise nicht der beste Ansatz.

Hier geht es darum, was winston.log_and_exit() macht. Beachten Sie, dass log4js.shutdown() bei Anhängen, die auf beschreibbaren Streams basieren ( appenders.filter(a => a.shutdown).forEach(a => a.shutdown(complete)); ), von writable.end(complete) abhängt, was immer noch ein asynchrones Herunterfahren ist.

Ich denke, in Bezug auf @mscharleys Kommentar besteht der Vorteil darin, dass es einen fileSync Appender gibt, für den nicht die Verwendung von shutdown() erforderlich ist, damit die vollständigen Protokolle beim Knoten in die Datei geschrieben werden Prozess beendet ...

Ja, ein fileSync -Transport wäre in einigen Situationen nützlich, selbst wenn dies eine Verringerung der Nettoleistung wäre.

@jdthorpe Ich denke, Ihre Idee, eine Bibliothek zu haben, die das Schließen von Dingen vor dem Herunterfahren sauber handhabt, ist gut, aber sie verlagert die Verantwortung nicht von Winston weg, um eine Methode bereitzustellen, mit der darauf gewartet werden kann, dass alles auf die Festplatte gespült wird (dh einen Mechanismus zum Schließen von sich selbst bereitstellen), der dann mit einer solchen Bibliothek kombiniert werden könnte, um die Schließmethoden für verschiedene Bibliotheken zusammenzufassen.

Kann dieses Ticket zumindest die Dokumente ändern, damit es nicht so aussieht, als ob es derzeit unterstützt wird ? Ich habe gerade 30 Minuten damit verbracht, dieses Problem zu debuggen, das wenn Sie im Repo nach seriously suchen , das nur im unterstützt wird README . Dies scheint entweder eine nicht unterstützte Funktion oder ein Fehler zu sein, und die Problemumgehungen sind nicht wirklich schmackhaft.

Wann wird das behoben? Dies gibt es seit 3 ​​Jahren und ist ziemlich wichtig.

Lassen Sie uns zumindest einen Rückruf in den Logger einfügen, der tatsächlich funktioniert.

Ich würde sagen, dass vielleicht eine generische shutdown() -Funktion für alle als neue Funktion geschrieben wurde, und diese Funktion ist ziemlich kritisch.

Ja, das ist ein sehr, sehr wichtiges Thema imo

+1 dafür

Ehrlich gesagt halte ich das Hinzufügen von shutdown(callback) angemessen, aber mit der Einschränkung, dass Transporte dies aktiv unterstützen müssen. Einzelne Transporte sind die "Informationsexperten" über ihren zugrunde liegenden Ausgabemechanismus; Sie haben die meisten Informationen darüber, wie es sauber heruntergefahren werden soll.

Alles, was der winston -Kern tut - und definitiv alles, was wir im Benutzerland tun -, erfordert Implementierungsdetails der Transporte, um zu lecken.

winston sollte also nicht die Arbeit erledigen müssen ... aber es muss wirklich eine Möglichkeit bieten, die Arbeit zu erledigen. Dann können wir alle mit den Transportautoren zusammenarbeiten, um das schwere Heben zu erledigen.

Einige Pseudocodes zur Veranschaulichung des folgenden Punktes.

`` `js
Herunterfahren (Rückruf) {
var transportCount = transports.length
var beendet = 0;
// für jeden Transport
if (transport.shutdown) {
transport.shutdown (function () {
// fertige Anzahl erhöhen.
// Wenn alles fertig ist, rufe den ursprünglichen Rückruf auf.
});
} else {
// Transport unterstützt es (noch) nicht.
// fertigen Zähler erhöhen
}}
}}

Würde gerne sehen, dass dies in v3.x behoben ist.

Hier ist meine Lösung (setzt eine Umgebung voraus, die async / await )


Für einen einzelnen Transport:

const logTransport = new winston.transports.File({filename: '/log/app.log'})

global.log = winston.createLogger({transports: [logTransport]})

// helper to exit with a given return code, but only after the logger is finished
// this will return a Promise that will never resolve, thus halting execution
log.exit = (exit = 5) => {
  return new Promise(() => {
    logTransport.on('finished', () => process.exit(exit))
  })
}



Für mehrere Transporte (ungetestet):

`` `js
const logTransports = [new winston.transports.File ({Dateiname: '/log/app.log'})]

global.log = winston.createLogger ({transports: [logTransport]})

// Hilfsprogramm zum Beenden mit einem bestimmten Rückkehrcode, jedoch erst, nachdem der Logger fertig ist
// Dies gibt ein Versprechen zurück, das niemals aufgelöst wird, wodurch die Ausführung angehalten wird
log.exit = (exit = 5) => {
neues Versprechen zurückgeben (() => {
Promise.all (
logTransports.map (transport => neues Versprechen (Auflösung => transport.on ('beendet', Auflösung)))
) .then (() => process.exit (exit))
})
}}

</details>
<br>
Then, call with `await` like:

```js
try {
  something()
} catch(e) {   
  //do something with the error
  await log.exit(5) // <--- nothing will happen past this line as the promise will never resolve!
  // <--- process will have exited before it reaches here
  trySomethingElse() // <--- this will never be called, and control wont return to the calling code
}

Kein gebrochener Rekord, aber Winston-Log-and-Exit löst auch dieses Problem

Nur für sehr, sehr spezifische Anwendungsfälle. Ich konnte Winston-Log-and-Exit in AWS Lambda nicht verwenden, wo der Prozess nicht beendet wird, aber zwischen den Ausführungen einfriert, sodass Protokolle immer noch verloren gehen. Es wird auch davon ausgegangen, dass ich weiß, welche Nachrichten meine letzten sind, was bei einem erfolgreichen Ausführungspfad möglicherweise nicht immer der Fall ist.

@mscharley würde die oben genannte await -Lösung für Ihren Anwendungsfall funktionieren? Vielleicht würde anstelle von log.exit() eine bessere API await log.finish() die warten würde, bis alle Transporte fertig geschrieben sind, und Sie dann alles tun lassen könnten (rufen Sie process.exit , rufen Sie die Lamda done Rückruf usw.)

Genau darauf haben wir in dieser Ausgabe gedrängt, außer in Winston. Warum sollte dies ein Plugin sein? Scheint etwas zu sein, das öfter auftauchen sollte. Ich habe nicht das Gefühl, dass das, was wir tun, etwas Besonderes ist, aber wir haben diese Mauer bei jedem Projekt erreicht - weshalb wir uns irgendwann von Winston entfernt haben. Das Verlieren von Protokollen, insbesondere am Ende des Prozesses - wo Fehler am wahrscheinlichsten auftreten - scheint zumindest gefährlich zu sein.

log4js-node hat eine Shutdown-Methode implementiert, die alle Transporte löscht und einen Rückruf wie process.exit() . Könnte so etwas zu Winston hinzugefügt werden?

log4js-node ist es dann

irgendwelche Updates? Das Problem ist sehr aktuell.

Für jedes Protokollierungssystem lautet der Kalkül:

  • Wenn Sie unbedingt über Ihre Protokolle verfügen müssen und bereit sind, die Kosten für langsamere Antwortzeiten zu bezahlen, verwenden Sie einen synchronen Protokollierer
  • Wenn Sie selbst process.exit() anrufen, warten Sie, bis die gepufferten Nachrichten gelöscht sind. Für Winston erledigt das Winston-Log-and-Exit- Paket dies für Sie.
  • Wenn ein anderer Code process.exit() aufruft und Ihre Plattform über einen On-Exit-Hook verfügt, verwenden Sie diesen Hook und warten Sie, bis die gepufferten Nachrichten gelöscht sind. winston-log-and-exit kann auch dabei helfen.
  • Wenn Sie eine Plattform wie AWS Lambda verwenden, die keinen On-Exit-Hook hat, tauschen Sie die Vorteile der Plattform gegen das Risiko des Datenverlusts.

Übrigens: @ begin-again Dies ist die log_and_exit() -Methode im Wesentlichen die gleiche wie die shutdown() -Methode von log-4js mit der zusätzlichen Garantie, dass die bereitgestellte Protokollnachricht protokolliert wird.

@jdthorpe Aber winston-log-and-exit funktioniert nicht einfach mit Winston 3

@VRuzhentsov Oh, das ist ein Problem. Sie sollten wirklich etwas dagegen tun.

@jdthorpe Behoben es mit

const transport = new Transport();
const logger = createLogger({
  transports: [
    transport 
  ]
})

logger.finish = (exitCode) => {
  transport.on('finish', () => process.exit(exitCode));
  transport.close();
};

const shutDown = (exitCode) => {
  logger.info('Shutting down');
  logger.finish(exitCode);
};

shutDown(1);

Folgendes funktioniert für mich:

logger.info('winston - info logged');
logger.error('winston - error logged');
process.nextTick(() => {
    process.exit(0)
})

@ Redhat-Raptor. Das wird die meiste Zeit funktionieren, aber es ist nicht garantiert, dass es die ganze Zeit funktioniert. Es kommt nur vor, dass Ihre Puffer zum Zeitpunkt des Aufrufs von nextTick () gelöscht wurden. Abhängig von den Verbindungen, auf die sich Ihre Logger verlassen, kann dies jedoch leicht zu Nachrichtenverlusten führen ...

Dieser Code hat bei mir funktioniert:

            logger.error(`No value specified for ${value} variable! Node will exit now...`);
            logger._flush(() => {
                process.exit(1);
            })

Wird dies in Version 3 nicht nativ unterstützt?

https://github.com/winstonjs/winston/blob/master/README.md#awaiting -logs-to-write-in-winston

@danielweck Nein, es ist kaputt: # 1629 # 1504!

6 Jahre, das ist richtig, 6 Jahre für einen Fehler, der als wichtig markiert ist. Es ist grenzwertig beeindruckend.

Ich benutze ein waitForWinston() Versprechen:

const winston = require('winston');

const fileTransport = new winston.transports.File({ name: 'file', filename: FILE_PATH });
const consoleTransport = new winston.transports.Console({ name: 'console', stderrLevels: ['error'] });
const transports = [
    fileTransport,
    consoleTransport
];
winston.configure({
    level: 'verbose',
    transports,
    format: winston.format.cli()
});
winston.log('info', 'TEST MSG');
try {
    await waitForWinston();
} catch (err) {
    console.log(err);
}
process.exit(0);
const waitForWinston = async () => {
    return new Promise(async (resolve, _reject) => {
        for (const transport of transports) {
            try {
                await closeWinstonTransportAndWaitForFinish(transport);
            } catch (err) {
                console.log(err);
            }
        }
        resolve();
    });
};
const closeWinstonTransportAndWaitForFinish = async (transport) => {
    if (!transport.close) {
        // e.g. transport.name === 'console'
        return Promise.resolve();
    }
    // e.g. transport.name === 'file'

    return new Promise(async (resolve, _reject) => {
        transport._doneFinish = false;
        function done() {
            if (transport._doneFinish) {
                return; // avoid resolving twice, for example timeout after successful 'finish' event
            }
            transport._doneFinish = true;
            resolve();
        }
        setTimeout(() => { // TODO: use a cancellable setInterval instead
            done();
        }, 5000); // just in case the 'finish' event never occurs
        const finished = () => {
            done();
        };

/* Update: `stream.end()` does not flush pending writes, so this solution is inadequate.  For some reason, in recent tests `transport.close();` + `transport.once('finish', ...);` works just fine ... not sure why this works now though! (it used to fail!) */

/*
        if (transport._stream) {
            transport._stream.once('finish', finished);
            transport._stream.end();
        }
*/
            transport.once('finish', finished);
            transport.close();
    });  
};

Update: stream.end() löscht keine ausstehenden Schreibvorgänge, daher ist diese Lösung nicht ausreichend. Aus irgendeinem Grund funktioniert in den letzten Tests transport.close(); + transport.once('finish', ...); einwandfrei ... ich bin mir nicht sicher, warum dies jetzt funktioniert! (Früher ist es gescheitert!)

In meiner obigen Arbeitslösung besteht der Schlüssel darin, Folgendes zu verwenden:
transport._stream.once('finish', finished); transport._stream.end();
Anstatt von:
transport.once('finish', finished); transport.close();
(Die hinzugefügte Timeout-Strategie ist spezifisch für meinen Anwendungsfall und im allgemeinen Fall wahrscheinlich optional / redundant.)

Einfacherer Weg:

logger.error(`No value specified for ${missing_vars.join(',')} variable! Node will exit now...`, () => {
            process.exit(1);
        });

@danielweck Konnten Sie jemals eine Lösung dafür finden? Wir haben kürzlich ein Upgrade auf Winston 3.x durchgeführt, müssen jedoch möglicherweise auf 2.x zurückgreifen oder hier einen anderen Logger ohne Lösung finden.

@matttowerssonos , IIRC, diese Probleme hängen alle mit Winston @ 2 zusammen. Angesichts der Länge dieses Threads und der Tatsache, dass Sie sich nicht nach dem Kommentar des Originalplakats erkundigen, warum nicht einen neuen Thread mit Ihrem Problem starten?

Übrigens, ich sehe keine Probleme mit verlorenen Protokollen in Winston @ 3 , daher möchten Sie möglicherweise Ihre Konfiguration in Ihr neues Problem aufnehmen.

@cjbarth Das funktioniert nicht:

  process.on(type as any, (err: ErrorExtended) => {
    const processLogger: Logger = loggers.get('processLogger');

    processLogger.error(errorFormatter(err, type));

    processLogger.on('finish', function () {
      processLogger.end();
      console.log('never reaches here');
      process.exit(1);
    });
  });

processExit('uncaughtException');
// log and exit for unhandledRejection events.
processExit('unhandledRejection');

@stephanoparaskeva Wenn ich mich nicht irre, wird das finish -Ereignis niemals auf processLogger ausgelöst, da Sie niemals processLogger.end() anrufen. Wenn Sie das aufrufen, sollte das Ereignis finish ausgelöst werden, wenn Winston mit der Protokollierung fertig ist.

Ich würde auch process.disconnect() anstelle von process.exit() , wodurch die Warteschlange entleert wird. Es wird automatisch beendet, wenn die Warteschlange geleert wird.

@stephanoparaskeva Wenn ich mich nicht irre, wird das finish -Ereignis niemals auf processLogger ausgelöst, da Sie niemals processLogger.end() anrufen. Wenn Sie das aufrufen, sollte das Ereignis finish ausgelöst werden, wenn Winston mit der Protokollierung fertig ist.

Ich würde auch process.disconnect() anstelle von process.exit() , wodurch die Warteschlange entleert wird. Es wird automatisch beendet, wenn die Warteschlange geleert wird.

Gotcha, so etwas?

process.on(type as any, (err: ErrorExtended) => {
  const processLogger: Logger = loggers.get('processLogger');

  processLogger.error(errorFormatter(err, type));
  processLogger.end()

  processLogger.on('finish', function () {
    process.disconnect();
  });
});

// log and exit for uncaughtException events.
processExit('uncaughtException');
// log and exit for unhandledRejection events.
processExit('unhandledRejection');

@stephanoparaskeva Das sieht besser aus.

Normalerweise binde ich den Ereignishandler, bevor ich processLogger.end() , aber es sollte keine Rolle spielen, da das Ereignis finish erst ausgelöst wird, nachdem sich die Ereignisschleife gedreht hat. Zu diesem Zeitpunkt wird der Handler gebunden. Ich denke nur, dass das Schreiben des Codes, an den der Ereignishandler gebunden ist, es klarer macht.

@cjbarth
Oh, wie bindet man den Event-Handler an etwas?
Wäre das:

processLogger.on('finish', () => {
    this.process.disconnect();
  });

@stephanoparaskeva Ja. Das ist die richtige Syntax zum Binden der Funktion

{
  this.process.disconnect();
}

Zum finish Ereignis, das vom Objekt processLogger .

Es funktioniert für mich: https://stackoverflow.com/a/59260151

Wenn Sie process.exit nicht aufrufen, sondern stattdessen ordnungsgemäß herunterfahren, indem Sie alle Verbindungen schließen und darauf warten, dass die Ereignisschleife entleert wird, gibt es überhaupt kein Problem.

Siehe dies: https://stackoverflow.com/a/37592669 , um ein besseres Verständnis dafür zu erhalten, worauf sich @deedf bezieht. Das Befolgen dieses Ratschlags hat dieses Problem auch für mich gelindert.

Die Tatsache, dass das Zielereignis nach Jahren immer noch nicht ausgestrahlt wird, ist an dieser Stelle nur peinlich. Es wird schwierig sein, die Logger zu wechseln und alle meine benutzerdefinierten Transporte für sie neu zu schreiben.

Ich verstehe nicht, wie Winston dies noch nicht gelöst hat. Ich habe ein Problem, dass nur der allererste Logger in meinem Code in Logstash protokolliert wird (auch mit winston-elasticsearch). Wenn ich logger.end () verwende, wird alles protokolliert, aber das Programm wird zu früh beendet. habe versucht, alle oben genannten Lösungen zu verwenden, aber keine hat wirklich funktioniert.
Ich bin wirklich hilflos

Ich verstehe nicht, wie Winston dies noch nicht gelöst hat. Ich habe ein Problem, dass nur der allererste Logger in meinem Code in Logstash protokolliert wird (auch mit winston-elasticsearch). Wenn ich logger.end () verwende, wird alles protokolliert, aber das Programm wird zu früh beendet. habe versucht, alle oben genannten Lösungen zu verwenden, aber keine hat wirklich funktioniert.
Ich bin wirklich hilflos

Haben Sie versucht , diese ?

Ich verstehe nicht, wie die Leute nicht verstehen, dass dieses "Problem" nicht "gelöst" wurde, weil es keines gibt. Schreiben Sie einfach den richtigen Code, indem Sie exit () nicht aufrufen, und alles wird gut.

Ich verstehe nicht, wie die Leute nicht verstehen, dass dieses "Problem" nicht "gelöst" wurde, weil es keines gibt. Schreiben Sie einfach den richtigen Code, indem Sie exit () nicht aufrufen, und alles wird gut.

Unsere Lösung für dieses Problem besteht also darin, process.exit () nicht aufzurufen.

Ich verstehe nicht, wie die Leute nicht verstehen, dass dieses "Problem" nicht "gelöst" wurde, weil es keines gibt. Schreiben Sie einfach den richtigen Code, indem Sie exit () nicht aufrufen, und alles wird gut.

Unsere Lösung für dieses Problem besteht also darin, process.exit () nicht aufzurufen.

Ja, es ist immer eine schlechte Idee in nodejs, siehe https://stackoverflow.com/a/37592669

Ich verstehe nicht, wie die Leute nicht verstehen, dass dieses "Problem" nicht "gelöst" wurde, weil es keines gibt. Schreiben Sie einfach den richtigen Code, indem Sie exit () nicht aufrufen, und alles wird gut.

Unsere Lösung für dieses Problem besteht also darin, process.exit () nicht aufzurufen.

Ja, es ist immer eine schlechte Idee in nodejs, siehe https://stackoverflow.com/a/37592669

Der Grund, warum ich persönlich process.exit verwende, ist, dass ich uncaughtException und unhandledRejection abhöre, um mich bei allen meinen Transporten anzumelden und dann die Anwendung zu beenden. Gibt es einen besseren Weg, um dieses Problem zu lösen?

Ich verstehe nicht, wie die Leute nicht verstehen, dass dieses "Problem" nicht "gelöst" wurde, weil es keines gibt. Schreiben Sie einfach den richtigen Code, indem Sie exit () nicht aufrufen, und alles wird gut.

Wo hast du gesehen, dass ich sagte, ich hätte exit () verwendet?

Ich verstehe nicht, wie die Leute nicht verstehen, dass dieses "Problem" nicht "gelöst" wurde, weil es keines gibt. Schreiben Sie einfach den richtigen Code, indem Sie exit () nicht aufrufen, und alles wird gut.

Wo hast du gesehen, dass ich sagte, ich hätte exit () verwendet?

Es tut uns leid, dass Sie davon ausgehen, dass Ihre Lese- und Verständnisfähigkeit ausreicht, um zu verstehen, worum es bei diesem Fehler geht. Überprüfen Sie den Titel erneut.

Ich verstehe nicht, wie die Leute nicht verstehen, dass dieses "Problem" nicht "gelöst" wurde, weil es keines gibt. Schreiben Sie einfach den richtigen Code, indem Sie exit () nicht aufrufen, und alles wird gut.

Wo hast du gesehen, dass ich sagte, ich hätte exit () verwendet?

Es tut uns leid, dass Sie davon ausgehen, dass Ihre Lese- und Verständnisfähigkeit ausreicht, um zu verstehen, worum es bei diesem Fehler geht. Überprüfen Sie den Titel erneut.

Sie müssen so beschäftigt sein, wenn Sie zu einem Thema kommen, das seit 8 Jahren offen ist und überhaupt keine angemessene Hilfe bietet.

Ich verstehe nicht, wie die Leute nicht verstehen, dass dieses "Problem" nicht "gelöst" wurde, weil es keines gibt. Schreiben Sie einfach den richtigen Code, indem Sie exit () nicht aufrufen, und alles wird gut.

Unsere Lösung für dieses Problem besteht also darin, process.exit () nicht aufzurufen.

Ja, es ist immer eine schlechte Idee in nodejs, siehe https://stackoverflow.com/a/37592669

Der Grund, warum ich persönlich process.exit verwende, ist, dass ich uncaughtException und unhandledRejection abhöre, um mich bei allen meinen Transporten anzumelden und dann die Anwendung zu beenden. Gibt es einen besseren Weg, um dieses Problem zu lösen?

Ich habe Winston schon eine Weile nicht mehr benutzt, aber wird es das trotzdem nicht protokollieren? Wenn nicht, funktioniert das erneute Auslösen des Fehlers nach der Protokollierung nicht?

Ich verstehe nicht, wie die Leute nicht verstehen, dass dieses "Problem" nicht "gelöst" wurde, weil es keines gibt. Schreiben Sie einfach den richtigen Code, indem Sie exit () nicht aufrufen, und alles wird gut.

Wo hast du gesehen, dass ich sagte, ich hätte exit () verwendet?

Es tut uns leid, dass Sie davon ausgehen, dass Ihre Lese- und Verständnisfähigkeit ausreicht, um zu verstehen, worum es bei diesem Fehler geht. Überprüfen Sie den Titel erneut.

Sie müssen so beschäftigt sein, wenn Sie zu einem Thema kommen, das seit 8 Jahren offen ist und überhaupt keine angemessene Hilfe bietet.

Zumindest kann ich meine eigenen Probleme lösen, ohne wie ein berechtigter Gör zu jammern.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen