Winston: La journalisation dans un fichier échoue lors de l'appel de process.exit (0)

Créé le 25 mars 2013  ·  87Commentaires  ·  Source: winstonjs/winston

Lorsque winston est configuré pour se connecter à un fichier et que vous appelez process.exit (0) dans votre code, winston ne finit jamais par créer le fichier journal et ajouter les messages du journal au fichier.

Dans un monde parfait, winston serait heureux de gérer ce cas de bord et d'écrire dans le fichier, mais je serais également en mesure d'appeler flush sur une instance de journalisation afin de forcer manuellement un flush au fichier.

Il peut y avoir un moyen de `` vider '' manuellement pour le moment, mais ce n'est pas documenté et je n'ai pas eu de chance pour fermer / terminer / détruire manuellement une instance de journalisation ou ses flux internes jusqu'à présent d'une manière qui aboutit réellement aux loges en cours d'écriture dans un fichier.

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

Commentaire le plus utile

@danielweck Non, c'est cassé: # 1629 # 1504!

6 ans, c'est vrai, 6 ans pour un bug marqué _important_. C'est à la limite impressionnant.

Tous les 87 commentaires

Dans le nœud, les méthodes console. * Écrivent de manière asynchrone, le transport de fichiers est asynchrone. Vous fermez le processus avant même que le nœud n'ait écrit des données.

Le lundi 25 mars 2013 à 19h45, Yuri Zapuchlak a écrit:

Lorsque winston est configuré pour se connecter à un fichier et que vous appelez process.exit (0) dans votre code, winston ne finit jamais par créer le fichier journal et ajouter les messages du journal au fichier.
Dans un monde parfait, winston serait heureux de gérer ce cas de bord et d'écrire dans le fichier, mais je serais également en mesure d'appeler flush sur une instance de journalisation afin de forcer manuellement un flush au fichier.
Il peut y avoir un moyen de `` vider '' manuellement pour le moment, mais ce n'est pas documenté et je n'ai pas eu de chance pour fermer / terminer / détruire manuellement une instance de journalisation ou ses flux internes jusqu'à présent d'une manière qui aboutit réellement aux loges en cours d'écriture dans un fichier.
var winston = require ('winston'); var logger = new (winston.Logger) ({transports: [new (winston.transports.Console) (), new (winston.transports.File) ({filename: 'winston.log'})]}); logger.info ('informations enregistrées'); logger.error ('erreur enregistrée'); process.exit (0); // les deux journaux sont écrits sur la console, mais pas dans le fichier winston.log /

-
Répondez directement à cet e-mail ou consultez-le sur GitHub (https://github.com/flatiron/winston/issues/228).

Hmm. Dans ce cas, si l'on doit tuer un processus de nœud mais a besoin de consigner des informations juste avant de le tuer, il suffit de mettre process.exit () à l'intérieur d'un setTimeout () avec une durée suffisamment longue, la seule chose que l'on puisse faire pour s'assurer que winston écrit réellement des messages dans le fichier journal?

Pour plus de clarté, console.log et les amis sont synchrones .

@yzapuchlak Je n'ai pas réellement testé, mais c'est ce que je fais aussi.

@yzapuchlak setTimeout fonctionnera _probablement_ sans problème, mais je pense qu'il serait sage d'utiliser le paramètre de rappel optionnel à la place.

setTimeout de 2000 ou 5000 fonctionne dans mon script de test simple, mais pas lorsque j'essaye de l'utiliser dans mon vrai code. Donc, je ne sais pas à quel point ce correctif potentiel sera fiable. Je creuserai ce problème plus tard si je n'ai pas de chance sur le front de rappel optionnel.

@dtex J'ai juste essayé d'utiliser le rappel facultatif (comme le montre l'exemple de code suivant), mais cela

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 Curieux, est-ce la version 0.6.2?

Ayant le même problème maintenant, vous devez terminer le processus et faire une note dans le fichier journal. Ce serait bien d'avoir une option de rappel, que nous pourrions utiliser pour quitter le processus uniquement après l'écriture du message. Ou peut-être existe-t-il déjà?

J'ai le même problème, avec Winston 0.7.2.
J'ai testé la solution de contournement setTimeout et callback sans aucun succès.

C'est le même problème que # 288 et aussi le problème du nœud log4js (https://github.com/nomiddlename/log4js-node/issues/148). Je pense que le problème est que la boucle d'événements ne s'exécute pas une fois que le rappel process.exit est terminé - donc tous les appels asynchrones en attente (comme les écritures de fichiers) n'ont pas la possibilité de s'exécuter. Je serais très heureux si quelqu'un pouvait me prouver le contraire et trouver une solution.

Je suis prêt à consacrer du temps à résoudre ce problème si nous pouvons trouver une solution. Le problème clé semble être que WritableStreams ne garantit pas le vidage sur process.exit () ou une exception non gérée. Des solutions alternatives ont-elles été étudiées? Il semble que nous pourrions mettre en cache les journaux en mémoire jusqu'à ce que WritableStream se vide. Ensuite, si une exception process.exit ou non interceptée se produit, nous pouvons écrire de manière synchrone tous les journaux qui n'ont pas été vidés. Cela pourrait produire des journaux en double, mais empêcherait au moins la perte de journal. Des pensées?

Lié à # 288.

Des mises à jour ici? Échec toujours pour moi avec ~ 0.9.0

Ouais, j'utilise toujours le hack setTimeout () mentionné par @yzapuchlak précédemment :(

Je ne vois pas vraiment cela comme un bug de winston de toute façon. Lorsque vous process.exit dans votre code, vous avez pris la décision de tuer complètement tout ce qui est encore en cours de traitement. S'il y a toujours des requêtes HTTP en cours d'exécution, vous ne supposez pas que celles-ci seront également répondues, alors pourquoi supposer que les messages de journal se retrouvent dans votre journal? C'est ainsi que fonctionne le nœud, les choses sont asynchrones pour une raison. Si vous avez utilisé l'approche de rappel ou de setTimeout, rien n'empêche votre application d'enregistrer une autre ligne juste avant que votre setTimeout ne soit terminé ou que le rappel soit appelé.

Le seul moyen de contourner ce problème est d'avoir une journalisation synchrone, mais vous ne parvenez pas à avoir un système asynchrone comme une simple instruction de journal lorsque vous gérez des demandes.

Donc, ce que je suggère, c'est de fermer ce problème avec un correctif habituel. Si vous ne voulez pas égarer les messages du journal, assurez-vous de nettoyer et d'arrêter correctement votre application avant d'appeler process.exit.

edit De plus, tous vos hacks setTimeout sont défectueux et ne résolvent pas le problème car vous n'avez aucune idée du temps qu'il faudra pour que toutes les données soient écrites sur le système d'exploitation et à quelle vitesse vos disques ou ssds les traitent. La seule solution décente ici est d'écouter l'événement drain du flux et de quitter le processus. Mais si votre application enregistre en permanence, cela peut même ne pas être appelé pendant une longue période; p ce qui en fait juste un autre hack parce que vous n'avez pas correctement arrêté votre application avant de quitter.

Le fait est qu'il n'y a actuellement aucun moyen d'attendre (rappel / promesse / quoi que ce soit) pour que les journaux soient vidés. Forcer également la journalisation de la synchronisation permettrait d'ajouter des journaux dans process.on('exit')

Si vous souhaitez avoir une journalisation asynchrone pendant la durée de vie de votre application et une journalisation de synchronisation pendant process.exit, vous devez créer une nouvelle instance winston avec une implémentation de fichier personnalisée qui synchronise les écritures. Et si vous voulez savoir quand les choses sont vidées, vous devez écouter l'événement drain d'un flux.

Je vais essayer ça, merci!

Pour ce que ça vaut, vous pouvez contourner l'utilisation de setTimeout mais vous devez saisir le sous-jacent _stream :

// 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);
  }
});

Comme mentionné ci-dessus, l'utilisation de la fonction de rappel seule n'est pas suffisante pour qu'elle soit correctement vidée dans le fichier. Cela me semble être un bug.

Notez que j'écoute en fait l'événement finish : https://nodejs.org/api/stream.html#stream_event_finish plutôt que d'utiliser l'événement flush ou closed winston car cela ne suffit pas non plus pour qu'il se connecte correctement: /

Essaye celui-là. Cela fonctionne plutôt bien dans mon cas. Au lieu d'appeler directement process.exit nous laissons l'enregistreur appeler process.exit après le vidage du journal.

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);
};

dans votre code:

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

Testé sur NodeJS v4.1.2 et winston 1.1.0

J'utilise quelque chose de similaire à @ 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);
    });
}

dans votre code:

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

Idéalement, ce serait bien si je pouvais simplement appeler la méthode close sur le loggerInstance , mais puisque cela émet l'événement closed dès qu'il a fini d'appeler le close sur les transports sous-jacents, plutôt qu'après leur fermeture, cela ne fonctionnera pas. Il semble que ce soit un bogue que le logger puisse être fermé sans que les transports sous-jacents soient complètement fermés.

Les étapes et les journaux de

En utilisant également les étapes de

Il y a toujours ce problème sur la version 2.2.0 .

N'est-ce toujours pas mis en œuvre? Plus de 3 ans. Je ne veux pas utiliser la sortie de processus.

Je comprends que ce n'est pas un problème, mais il devrait certainement y avoir une fonction pour terminer toutes les écritures dans les fichiers et fermer tout élément ouvert afin que le processus puisse se terminer seul sans l'appeler explicitement. Tellement difficile d'utiliser de telles choses dans les systèmes de production.

Faisant écho au fait que cela nécessite vraiment une solution du côté de Winston. Qu'il s'agisse de fournir un transport de fichier synchrone ou de fournir un hook d'arrêt pour fermer Winston «en toute sécurité», je m'en fiche, ce serait une solution satisfaisante pour moi. Mais n'avoir rien rend tout simplement impossible d'écrire des journaux puis de quitter et d'être assuré qu'ils seront réellement écrits.

Le problème avec les transports synchrones, c'est que l'exécution de votre code sera bloquée jusqu'à ce que l'IO se termine, ce qui limite les performances. Il n'y a aucun moyen de convertir une écriture asynchrone en écriture bloquante, ce qui signifie qu'une fois que vous écrivez de manière asynchrone dans les journaux, vous devez attendre que NodeJS y parvienne, et entre-temps, un autre code JS peut être exécuté.

D'autre part, les promesses peuvent être annulées de manière synchrone, les délais d'attente peuvent être effacés de manière synchrone, les connexions de fichiers et de bases de données peuvent être fermées de manière synchrone, donc une meilleure solution (plus performante) pourrait être de garder une trace des choses qui doivent être annulées / effacées / fermé et fermez-les dans le gestionnaire d'exceptions qui appelle winston.log_and_exit() , ou le rappel facultatif à winston.log_and_exit() .

Bien sûr, ce serait bien si Winston gardait une trace de toutes les choses que vous auriez besoin de fermer en cas d'erreur, mais un tel gestionnaire de tâches serait probablement une chose utile en soi, et mérite probablement un cadre séparé (voir Loi de Curley).

Je ne vois pas comment les performances ont un impact ici. Le processus est sur le point d'être tué, winston n'a besoin que de faire la comptabilité sur les lignes de journal finales pendant lesquelles il peut heureusement bloquer l'OMI.

Compte tenu de l'âge de ce problème et du fait qu'il existe une solution de contournement, je ne tiens pas vraiment à ce que quiconque aborde ce problème dans winston de si tôt.

Il semble que vous et moi parlons (écrivant) à travers des objectifs. Il semble que vous soyez intéressé par l'écriture synchrone des messages finaux dans un fichier, alors que mon souci était de m'assurer que les journaux qui étaient écrits de manière asynchrone juste avant l'erreur soient écrits dans un fichier.

L'écriture des messages dans un fichier qui s'est produit juste avant l'erreur de manière synchrone nécessiterait que tous les messages soient écrits de manière synchrone, et c'est le problème de performances.

D'autres ont été préoccupés par le fait d'arrêter l'exécution d'autres codes JS après une erreur mais avant que les derniers journaux n'aient été écrits dans le fichier.

Une option qui pourrait satisfaire les besoins de tout le monde est d'écrire un transport personnalisé qui écrit de manière asynchrone, et conserve un tampon de messages non encore confirmés comme écrits (vidés) dans le fichier, qui pourraient être écrits dans le fichier de manière synchrone en cas d'erreur (au risque de dupliquer les messages , dans le cas où NodeJS a déjà fini d'écrire dans le fichier, mais que l'événement vidé n'a pas encore été traité ...).

Est-il réellement difficile de fournir un hook d'arrêt à appeler qui attendrait que tout soit vidé avant de revenir et / ou d'appeler un rappel? Cela résoudrait à peu près tous les cas d'utilisation.

Mais laissez-moi également vous parler de mon cas d'utilisation, la raison pour laquelle je ne me soucie pas des journaux synchrones. Des tests. Je m'en fiche si mes tests prennent une seconde ou deux de plus à s'exécuter. Je me soucie si les journaux ne sont pas déterministes, car ils peuvent ou non être écrits en fonction de la rapidité avec laquelle le nœud décide de s'arrêter. C'est mauvais . Je suis tout à fait d'accord pour dire que l'asynchrone doit être la valeur par défaut. Je ne suis pas d'accord pour dire que cela devrait être la seule option, en particulier à la lumière de problèmes comme celui-ci à l'intérieur du nœud lui-même.

@mscharley voici comment log4js le fait - il y a une fonction log4js.shutdown que vous pouvez fournir un rappel. Il faudra attendre de s'assurer que tous les appenders (winston les appelle transports, je pense) ont fini d'écrire avant d'appeler le rappel. Cela semble bien fonctionner pour mes utilisateurs. Il nécessite le support des appenders, en ce sens qu'ils doivent être écrits pour pouvoir répondre aux appels d'arrêt.

Je n'essaye pas de promouvoir log4js ou de dire que c'est mieux que winston, au fait - juste en soulignant que cette approche peut fonctionner. Ce n'est peut-être pas la meilleure approche.

C'est à peu près ce que fait winston.log_and_exit() . Notez que log4js.shutdown() dépend de writable.end(complete) dans le cas d'ajouts basés sur des flux inscriptibles, ( appenders.filter(a => a.shutdown).forEach(a => a.shutdown(complete)); ) qui est toujours un arrêt asynchrone.

Je pense qu'en ce qui @mscharley , l'avantage est qu'il existe un appender fileSync , qui ne nécessite pas l'utilisation de shutdown() pour avoir les journaux complets écrits dans le fichier lorsque le nœud le processus se ferme ...

Oui, avoir un transport fileSync serait utile dans certaines situations, même si cela entraînerait une diminution nette des performances.

@jdthorpe Je pense que votre idée d'avoir une bibliothèque qui gère la fermeture proprement des choses avant de s'arrêter est une bonne idée, mais cela ne

Ce ticket peut-il au moins modifier les documents pour qu'il ne semble pas être actuellement pris en charge.Je viens de passer 30 minutes à déboguer ce problème qui prétend être pris en lorsque vous recherchez seriously dans le repo, il n'est pris en charge que dans le README . Cela semble être une fonctionnalité non prise en charge ou un bogue et les solutions de contournement ne sont pas vraiment trop acceptables.

Quand cela sera-t-il corrigé? Cela existe depuis 3 ans maintenant et c'est assez important.

Au moins, mettons un rappel dans l'enregistreur qui fonctionne réellement.

Je dirais que j'ai peut-être écrit une fonction générique shutdown() pour tout le monde en tant que nouvelle fonctionnalité, et cette fonctionnalité est assez critique.

Oui, c'est une question très, très importante imo

+1 pour cela

Franchement, je pense que l'ajout de shutdown(callback) semble approprié, mais avec l'avertissement que les transports doivent prendre en charge activement. Les transports individuels sont les «experts en information» sur leur mécanisme de sortie sous-jacent; ils ont le plus d'informations sur la façon dont il doit s'arrêter proprement.

Tout ce que fait le noyau winston - et certainement tout ce que nous faisons dans la zone utilisateur - nécessite que les détails d'implémentation des transports fuient.

Donc winston ne devrait pas avoir besoin de faire le travail ... mais il doit vraiment fournir un moyen pour que le travail soit fait. Ensuite, nous pouvons tous aller travailler avec les auteurs de Transport pour faire le gros du travail.

Quelques psuedo-code pour illustrer le point ci-dessous.

`` `js
shutdown (rappel) {
var transportCount = transports.length
var terminé = 0;
// pour chaque transport
if (transport.shutdown) {
transport.shutdown (fonction () {
// augmente le nombre fini.
// si tout est terminé, appelle le rappel d'origine.
});
} autre {
// Transport ne le prend pas (encore) en charge.
// incrémenter le compteur fini
}
}

J'adorerais voir cela corrigé dans la v3.x.

Voici ma solution (suppose un environnement qui prend en charge async / await )


Pour un seul 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))
  })
}



Pour plusieurs transports (non testés):

`` `js
const log Transports = [nouveau fichier winston.transports.File ({nom de fichier: '/log/app.log'})]

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

// aide à quitter avec un code de retour donné, mais seulement après la fin de l'enregistreur
// cela renverra une promesse qui ne se résoudra jamais, interrompant ainsi l'exécution
log.exit = (sortie = 5) => {
retourne une nouvelle promesse (() => {
Promise.all (
logTransport.map (transport => nouvelle promesse (résoudre => transport.on ('terminé', résoudre)))
) .then (() => process.exit (sortie))
})
}

</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
}

Ne pas être un record battu, mais winston-log-and-exit résout également ce problème

Uniquement pour des cas d'utilisation très, très spécifiques. Je ne pouvais pas utiliser winston-log-and-exit dans AWS Lambda où le processus ne se termine pas, mais il se fige entre les exécutions, donc les journaux sont toujours perdus. Cela suppose également que je sache quels messages sont mes derniers messages, ce qui peut ne pas toujours être le cas dans un chemin d'exécution réussi.

@mscharley la solution await ci-dessus fonctionnerait-elle pour votre cas d'utilisation? Peut-être qu'au lieu de log.exit() une meilleure api serait await log.finish() qui attendrait que tous les transports aient fini d'écrire, puis vous laisserait faire n'importe quoi (appelez process.exit , appelez le lamda done rappel, etc.)

C'est essentiellement exactement ce que nous préconisons dans ce numéro, sauf à l'intérieur de Winston. Pourquoi serait-ce un plugin? Cela semble être quelque chose qui devrait revenir plus souvent. Je n'ai pas l'impression que ce que nous faisons est quelque chose de spécial, mais nous avons atteint ce mur sur chaque projet - c'est pourquoi nous avons finalement quitté Winston. La perte de journaux, en particulier à la fin du processus - là où les erreurs sont les plus susceptibles de se produire - semble pour le moins dangereuse.

log4js-node a implémenté une méthode d'arrêt avec vidage de tous les transports et prend un rappel tel que process.exit() . Quelque chose comme ça pourrait être ajouté à winston?

log4js-node c'est alors

les mises à jour? Le problème est très réel.

Pour tout système de journalisation, le calcul est:

  • Si vous devez absolument avoir vos journaux et êtes prêt à payer le coût des temps de réponse plus lents, utilisez un enregistreur synchrone
  • Si vous appelez vous-même process.exit() , attendez que les messages en mémoire tampon soient effacés. Pour winston, le package winston-log-and-exit le fait pour vous.
  • Si un autre code appelle process.exit() et que votre plate-forme a un hook en sortie, utilisez ce hook et attendez que les messages tamponnés soient effacés. winston-log-and-exit peut également vous aider.
  • Si vous utilisez une plate-forme comme AWS Lambda qui n'a pas de hook à la sortie, vous échangez les avantages de la plate-forme contre le risque de perdre des données.

Incidemment, @ recommencez cette méthode log_and_exit() est essentiellement la même que la méthode shutdown() de log-4js avec la garantie supplémentaire que le message de journal fourni sera consigné.

@jdthorpe Mais winston-log-and-exit ne fonctionnera pas facilement sur winston 3

@VRuzhentsov Oh, c'est un problème. Ils devraient vraiment faire quelque chose à ce sujet.

@jdthorpe l'a corrigé avec

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);

Ce qui suit fonctionne pour moi:

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

@ redhat-raptor. Cela fonctionnera la plupart du temps, mais il n'est pas garanti de fonctionner tout le temps. Il se trouve que vos tampons ont été effacés au moment où nextTick () a été appelé, mais en fonction des connexions sur lesquelles reposent vos enregistreurs, cela pourrait facilement entraîner la perte de messages ...

Ce code a fonctionné pour moi:

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

N'est-ce pas pris en charge nativement dans la v3?

https://github.com/winstonjs/winston/blob/master/README.md#awaiting -logs-à-être-écrit-dans-winston

@danielweck Non, c'est cassé: # 1629 # 1504!

6 ans, c'est vrai, 6 ans pour un bug marqué _important_. C'est à la limite impressionnant.

J'utilise une promesse waitForWinston() :

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();
    });  
};

Mise à jour: stream.end() ne vide pas les écritures en attente, donc cette solution est inadéquate. Pour une raison quelconque, dans les tests récents transport.close(); + transport.once('finish', ...); fonctionne très bien ... je ne sais pas pourquoi cela fonctionne maintenant! (il échouait!)

Dans ma solution de travail ci-dessus, la clé est d'utiliser:
transport._stream.once('finish', finished); transport._stream.end();
à la place de:
transport.once('finish', finished); transport.close();
(la stratégie de timeout ajoutée est spécifique à mon cas d'utilisation, et est probablement facultative / redondante dans le cas général)

Manière plus simple:

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

@danielweck Avez-vous déjà pu trouver une solution à cela? Nous avons récemment mis à niveau vers Winston 3.x mais nous devrons peut-être revenir à 2.x ou trouver un autre enregistreur sans solution ici.

@matttowerssonos , IIRC, ces problèmes sont tous liés à Winston @ 2. Compte tenu de la longueur de ce fil et du fait que vous ne vous interrogez pas sur le commentaire original des affiches, pourquoi ne pas commencer un nouveau fil avec votre problème?

BTW, je ne vois aucun problème avec les journaux perdus sur Winston @ 3 , vous voudrez peut-être inclure votre configuration dans votre nouveau problème.

@cjbarth Cela ne fonctionne pas:

  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 Si je ne me trompe pas, l'événement finish ne sera jamais déclenché sur processLogger car vous n'appelez jamais processLogger.end() . Si vous appelez cela, alors, lorsque Winston a terminé la journalisation, l'événement finish devrait se déclencher.

J'utiliserais également process.disconnect() au lieu de process.exit() , ce qui permet à la file d'attente de se vider. Il se fermera automatiquement lorsque la file d'attente sera vidée.

@stephanoparaskeva Si je ne me trompe pas, l'événement finish ne sera jamais déclenché sur processLogger car vous n'appelez jamais processLogger.end() . Si vous appelez cela, alors, lorsque Winston a terminé la journalisation, l'événement finish devrait se déclencher.

J'utiliserais également process.disconnect() au lieu de process.exit() , ce qui permet à la file d'attente de se vider. Il se fermera automatiquement lorsque la file d'attente sera vidée.

Gotcha, alors quelque chose comme ça?

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 Cela a l'air mieux.

Je lierais généralement le gestionnaire d'événements avant d'appeler processLogger.end() , mais cela ne devrait pas avoir d'importance car l'événement finish ne devrait se déclencher qu'après le tour de la boucle d'événements, moment auquel le gestionnaire sera lié. Je pense simplement qu'écrire le code où le gestionnaire d'événements est lié en premier le rend plus clair.

@cjbarth
Oh, comment liez-vous le gestionnaire d'événements à quelque chose?
Serait-ce:

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

@stephanoparaskeva Oui. C'est la syntaxe correcte pour lier la fonction

{
  this.process.disconnect();
}

À l'événement finish émis par l'objet processLogger .

Cela fonctionne pour moi: https://stackoverflow.com/a/59260151

Si vous n'appelez pas process.exit mais que vous effectuez un arrêt correct à la place, en fermant toutes les connexions et en attendant que la boucle d'événements se vide, il n'y a aucun problème.

Voir ceci: https://stackoverflow.com/a/37592669 pour mieux comprendre ce à quoi @deedf fait référence. Suite à ces conseils, ce problème m'a également été atténué.

Le fait que l'événement d'arrivée n'émette toujours pas après des années est juste embarrassant à ce stade. Cela va être pénible de devoir changer d'enregistreur et de réécrire tous mes transports personnalisés pour eux.

Je ne comprends pas comment cela n'a pas encore été résolu par winston. J'ai un problème en ce que seul le tout premier enregistreur de mon code est connecté à Logstash (en utilisant également winston-elasticsearch). Si j'utilise logger.end (), tout est consigné, mais le programme se ferme trop tôt. essayé d'utiliser toutes les solutions ci-dessus, et aucune n'a vraiment fonctionné.
Je suis vraiment impuissant

Je ne comprends pas comment cela n'a pas encore été résolu par winston. J'ai un problème en ce que seul le tout premier enregistreur de mon code est connecté à Logstash (en utilisant également winston-elasticsearch). Si j'utilise logger.end (), tout est consigné, mais le programme se ferme trop tôt. essayé d'utiliser toutes les solutions ci-dessus, et aucune n'a vraiment fonctionné.
Je suis vraiment impuissant

Avez-vous essayé cela ?

Je ne comprends pas comment les gens ne comprennent pas que ce "problème" n'a pas été "résolu" car il n'y en a pas. Écrivez simplement le code approprié en n'appelant pas exit () et vous serez bien.

Je ne comprends pas comment les gens ne comprennent pas que ce "problème" n'a pas été "résolu" car il n'y en a pas. Écrivez simplement le code approprié en n'appelant pas exit () et vous serez bien.

Donc, notre solution à ce problème est de ne pas appeler process.exit ()?

Je ne comprends pas comment les gens ne comprennent pas que ce "problème" n'a pas été "résolu" car il n'y en a pas. Écrivez simplement le code approprié en n'appelant pas exit () et vous serez bien.

Donc, notre solution à ce problème est de ne pas appeler process.exit ()?

Oui, c'est toujours une mauvaise idée dans nodejs, voir https://stackoverflow.com/a/37592669

Je ne comprends pas comment les gens ne comprennent pas que ce "problème" n'a pas été "résolu" car il n'y en a pas. Écrivez simplement le code approprié en n'appelant pas exit () et vous serez bien.

Donc, notre solution à ce problème est de ne pas appeler process.exit ()?

Oui, c'est toujours une mauvaise idée dans nodejs, voir https://stackoverflow.com/a/37592669

Eh bien, la raison pour laquelle j'utilise personnellement process.exit est que j'écoute uncaughtException et unhandledRejection pour me connecter à tous mes transports, puis quittez l'application. Existe-t-il une meilleure façon de résoudre ce problème?

Je ne comprends pas comment les gens ne comprennent pas que ce "problème" n'a pas été "résolu" car il n'y en a pas. Écrivez simplement le code approprié en n'appelant pas exit () et vous serez bien.

Où avez-vous vu que j'ai dit que j'avais utilisé exit ()?

Je ne comprends pas comment les gens ne comprennent pas que ce "problème" n'a pas été "résolu" car il n'y en a pas. Écrivez simplement le code approprié en n'appelant pas exit () et vous serez bien.

Où avez-vous vu que j'ai dit que j'avais utilisé exit ()?

Désolé de supposer que votre capacité de lecture et de compréhension est suffisante pour comprendre de quoi il s'agit. Vérifiez à nouveau le titre.

Je ne comprends pas comment les gens ne comprennent pas que ce "problème" n'a pas été "résolu" car il n'y en a pas. Écrivez simplement le code approprié en n'appelant pas exit () et vous serez bien.

Où avez-vous vu que j'ai dit que j'avais utilisé exit ()?

Désolé de supposer que votre capacité de lecture et de compréhension est suffisante pour comprendre de quoi il s'agit. Vérifiez à nouveau le titre.

Vous devez être tellement occupé si vous arrivez à un problème qui est ouvert depuis 8 ans et n'offre aucune aide appropriée.

Je ne comprends pas comment les gens ne comprennent pas que ce "problème" n'a pas été "résolu" car il n'y en a pas. Écrivez simplement le code approprié en n'appelant pas exit () et vous serez bien.

Donc, notre solution à ce problème est de ne pas appeler process.exit ()?

Oui, c'est toujours une mauvaise idée dans nodejs, voir https://stackoverflow.com/a/37592669

Eh bien, la raison pour laquelle j'utilise personnellement process.exit est que j'écoute uncaughtException et unhandledRejection pour me connecter à tous mes transports, puis quittez l'application. Existe-t-il une meilleure façon de résoudre ce problème?

Je n'ai pas utilisé winston depuis un moment, mais cela ne sera-t-il pas enregistré de toute façon? sinon, ne renverra-t-il pas l'erreur après l'avoir enregistrée?

Je ne comprends pas comment les gens ne comprennent pas que ce "problème" n'a pas été "résolu" car il n'y en a pas. Écrivez simplement le code approprié en n'appelant pas exit () et vous serez bien.

Où avez-vous vu que j'ai dit que j'avais utilisé exit ()?

Désolé de supposer que votre capacité de lecture et de compréhension est suffisante pour comprendre de quoi il s'agit. Vérifiez à nouveau le titre.

Vous devez être tellement occupé si vous arrivez à un problème qui est ouvert depuis 8 ans et n'offre aucune aide appropriée.

Au moins, je peux résoudre mes propres problèmes sans pleurnicher comme un gamin autorisé.

Cette page vous a été utile?
0 / 5 - 0 notes