Winston: [3.0.0] Fehlerobjekt wird nicht analysiert oder gedruckt

Erstellt am 29. Mai 2018  ·  68Kommentare  ·  Quelle: winstonjs/winston

Bitte erzählen Sie uns etwas über Ihre Umgebung:

  • _ winston Version? _
  • _ node -v Ausgaben: _ v8.11.1
  • _Betriebssystem? _ (Windows, macOS oder Linux) macOS
  • _Sprache? _ (Alle | TypeScript XX | ES6 / 7 | ES5 | Dart) Alle

Was ist das Problem?

Das Protokollieren eines Knotenobjekts Error führt zu einer leeren Nachricht:

Beispiel:

const winston = require('winston');
const { createLogger, format, transports } = winston;

const logger = createLogger({
  transports: [
    new transports.Console()
  ]
});

let err = new Error('this is a test');
logger.log({level: 'error', message: err});

Resultierende Ausgabe:

% node test.js
{"level":"error","message":{}}

Ebenfalls:

logger.error(new Error('hello'))

Ergebnisse in:

{"level":"error"}

Was erwarten Sie stattdessen?

Ich erwarte, dass der Nachrichtenschlüssel mindestens die Fehlermeldung enthält. Wenn ich einen benutzerdefinierten Formatierer versuche, enthält info auch kein Fehlerobjekt, sodass es irgendwo entfernt werden muss.

Andere Informationen

Lassen Sie mich wissen, wie ich helfen kann - ich freue mich über eine PR, aber ich kenne mich noch nicht mit [email protected] aus, um sie zu finden

bug important

Hilfreichster Kommentar

Nein, das ist für eine Protokollierungsbibliothek nicht akzeptabel.
Der Maintainer sollte einfach ein gut hervorgehobenes Beispiel in Dokumente einfügen, in dem gezeigt wird, wie ein Fehler protokolliert wird, sogar das benutzerdefinierte printf-Format und das Nicht-json-Format definiert werden und wo Sie Fehler mit etwas wie logger.error ("etwas", err) und logger protokollieren können .error (err)
Winston schien großartig zu sein, aber dieses Problem ist unglaublich inakzeptabel

Alle 68 Kommentare

Wir haben eine gewisse Testabdeckung dafür, aber wir brauchen eindeutig mehr. Was ist unter der Decke los:

  1. Ihre Error Instanz wird entlang der objectMode Stream-Pipe-Kette übergeben
  2. Das Standardformat für Logger ist json (siehe: json Formatcode in logform )
  3. message und stack Eigenschaften auf Error sind nicht aufzählbar, was dazu führt, dass JSON.stringify etwas ausgibt, was man nicht erwartet.
console.log(JSON.stringify(new Error('lol nothing here')));
// '{}'

Aus gestalterischer Sicht führte winston@3 formats für genau diese Art von Problem ein, um die Leistung zu steigern. Apropos Leistung, interessanterweise macht pino hier etwas Interessantes . Vielleicht implementiert die Lösung etwas Ähnliches wie asJson im Standardformat json .

Wenn jemand nach einer schnellen Lösung sucht, können Sie enumerateErrorFormat vorerst in das Format Ihres Loggers aufnehmen. Wir werden hoffentlich eine Lösung für dieses Problem vor 3.0.0 nächste Woche (oder kurz danach in 3.0.1 ) finden.

const winston = require('../');
const { createLogger, format, transports } = winston;

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

const logger = createLogger({
  format: format.combine(
    enumerateErrorFormat(),
    format.json()
  ),
  transports: [
    new transports.Console()
  ]
});

// Error as message
console.log('Run FIRST test...');
logger.log({ level: 'error', message: new Error('FIRST test error') });

// Error as info (one argument)
console.log('\nRun SECOND test...');
const err = new Error('SECOND test error');
err.level = 'info';
logger.info(err);

// Error as info (two arguments);
console.log('\nRun THIRD test...');
logger.log('info', new Error('THIRD test error'));

@indexzero , ich habe versucht, Ihrer

Formatierer:
`` ` javascript const level = settings.debug ? 'debug' : 'info'; const printFormat = winston.format.printf(info => $ {info.timestamp} - $ {info.level}: $ {info.message}`);
const enumerateErrorFormat = winston.format (info => {
if (info.message Instanz des Fehlers) {
info.message = Object.assign ({
Nachricht: info.message.message,
Stapel: info.message.stack,
}, info.message);
}}
if (Info-Instanz des Fehlers) {
return Object.assign ({
Nachricht: info.message,
Stapel: info.stack,
}, die Info);
}}
Info zurückgeben;
});

const consoleLogger = winston.createLogger ({
Niveau,
Format: winston.format.timestamp (),
Transporte: [
neue winston.transports.Console ({
Format: winston.format.combine (
winston.format.colorize (),
enumerateErrorFormat (),
printFormat,
),
}),
],
});
Code: Javascript
Versuchen {
// Fehler beim Auslösen von Code
} catch (err) {
logger.error (err);
}}
Output:
2018-06-28T21: 17: 25.140Z - Fehler: undefiniert
Info object: Javascript
{level: '\ u001b [31merror \ u001b [39m', Zeitstempel: '2018-06-28T21: 17: 25.140Z', [Symbol (level)]: 'error'}
`` ``
Wo befindet sich das Nachrichtenattribut im Fehlerprotokoll?

@sandrocsimas Ich habe festgestellt, dass Sie die Funktion enumerateErrorFormat dem Standardformatierer des Loggers

Formatierer

const consoleLogger = winston.createLogger({
  level,
  format: winston.format.combine(
    winston.format.timestamp(),
    enumerateErrorFormat()
  ),
  transports: [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        printFormat,
      ),
    }),
  ],
});

Ich verstehe immer noch nicht warum

Ich glaube, ich habe den gleichen Fehler wie @sandrocsimas.

Hier ist meine Logger-Konfiguration:

logger.js

const winston = require('winston');
const {configure, format} = winston;
const {combine, colorize, timestamp, printf} = format;

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

const myConsoleFormat = printf(info => {
  console.log('** Info Object: **');
  console.log(info);
  console.log('** Winston Output: **');
  return `${info.level}: ${info.message}`;
});

winston.configure({
  transports: [
    new winston.transports.Console({
      format: combine(
        colorize(),
        enumerateErrorFormat(),
        myConsoleFormat
      ),
    })
  ]
});

Wenn ich es mit diesem Codeblock teste:

Test A.

const logger = require('winston');
try {
  throw(new Error());
} catch (err) {
  logger.error(err);
}

Wenn new Error() keinen Nachrichtenwert enthält, erhalte ich folgende Ausgabe:

Ausgang A.

** Info Object: **
{ message: 
   { message: '',
     stack: 'Error\n    at Object.<anonymous> (app.js:21:9)\n    at Module._compile (module.js:652:30)\n    at Object.Module._extensions..js (module.js:663:10)\n    at Module.load (module.js:565:32)\n    at tryModuleLoad (module.js:505:12)\n    at Function.Module._load (module.js:497:3)\n    at Module.require (module.js:596:17)\n    at require (internal/module.js:11:18)\n    at Object.<anonymous> (server.js:11:13)\n    at Module._compile (module.js:652:30)' },
  level: '\u001b[31merror\u001b[39m',
  [Symbol(level)]: 'error',
  [Symbol(message)]: '{"message":{},"level":"error"}' }
** Winston Output: **
error: [object Object]

Wo error: [object Object] genau das ist, was ich erwartet habe

Wenn ich es jedoch mit diesem Codeblock teste:

Test B.

const logger = require('winston');
try {
  throw(new Error('This causes error: undefined'));
} catch (err) {
  logger.error(err);
}

Wenn new Error() einen Nachrichtenwert enthält, erhalte ich folgende Ausgabe:

Ausgang B.

** Info Object: **
{ level: '\u001b[31merror\u001b[39m',
  [Symbol(level)]: 'error',
  [Symbol(message)]: '{"level":"error"}' }
** Winston Output: **
error: undefined

Wie Sie sehen können, bekomme ich die gleichen error: undefined , die @sandrocsimas bekommt. Ich habe erwartet, error: [object Object]

Beachten Sie, wenn ich diesen Codeblock versuche:

Test C.

const logger = require('winston');
try {
  throw(new Error('This should work'));
} catch (err) {
  logger.log({level: 'error', message: err});
}

Wenn ich logger.log anstelle von logger.error erhalte ich die gleiche Ausgabe wie bei Ausgabe A oben

Ich habe das gleiche Problem. Ich bin neu in Winston. Ich habe die @ indexzero- Lösung

@ nvtuan305 , haben Sie die Lösung von @indexzero genau ausprobiert oder ein wenig bearbeitet? Wenn ja, können Sie Beispielcode bereitstellen? Sein Code sollte funktionieren, wenn Sie logger.log({level: ____, message: err}); Es funktioniert nicht, wenn Sie logger.info , logger.error oder eine andere Variante von logger.<level> . Ich bin mir fast sicher, dass dies ein Fehler ist, wie ich oben angegeben habe, und er sollte in einer späteren Version behoben werden.

Vermisse ich etwas oder ist es ein kompletter Kopfschmerz (oder sogar unmöglich?), Die gleiche Ausgabe zu erhalten, die man leicht von console.log / error / warn erhält?

try {
   // ...
  throw new Error('foo');
} catch (e) {
  console.error('Caught error:', e);  // convenient, informative
  logger.error('Caught error:', e);  // nope, the second parameter is something else (couldn't find docs)
  logger.error(`Caught error: ${e}`);  // stack lost
  logger.error(`Caught error: ${JSON.stringify(e)}`);  // Caught error: {}
}

Was ist der äquivalente Winston-Code, um die gleiche Ausgabe wie zu erhalten
console.error('Caught error:', error); ?

Und wo ist die Dokumentation für die Parameter, die von den Convenience-Methoden für das Logger-Objekt übernommen wurden?

@dandv

logger.error('Caught error:', e);

Dies funktioniert nicht, da im Gegensatz zu console.log() das logger.<level>(message) des Winston nur einen Parameter namens message akzeptiert. Dieser Nachrichtenparameter ist entweder ein Objekt oder eine Zeichenfolge (jemand korrigiert mich, wenn ich falsch liege, aber das ist mein Verständnis).

Beachten Sie, dass Sie auch logger.log({level: <level>, message: <message>}) . Um mehr über diese beiden Funktionen zu erfahren, würde ich empfehlen, diesen Teil der Dokumente zu lesen: Winston Docs on Log Levels . Lesen Sie unbedingt die Verwendung der Protokollierungsstufen durch

logger.error(`Caught error: ${e}`);

Ich kann nicht definitiv sagen, warum dies den Stack nicht ausgibt, aber ich weiß, dass dies kein Problem mit Winston ist. Wenn Sie console.log(`Caught error: ${e}`) ausprobieren, ist der Stapel ebenfalls nicht enthalten. Ich habe nicht viel mit Vorlagenliteralen gearbeitet, daher funktionieren Vorlagenliterale entweder nicht gut mit Objekten, oder die Konsole.log von javascript erkennt das Objekt als Fehlerobjekt und gibt daher nur die Eigenschaft message aus. Das ist meine beste Vermutung.

logger.error(`Caught error: ${JSON.stringify(e)}`)

Dieser bringt auf den Punkt, worum es in diesem Bug-Thread geht. Zunächst müssen Sie einige technische Details zu Javascript verstehen. Beachten Sie, dass Sie beim Versuch von console.log(`Caught error: ${JSON.stringify(e)}`) auch dieselbe Ausgabe Caught error: {} . Wie @indexzero erklärte:

message und stack Eigenschaften auf Error sind nicht aufzählbar, was dazu führt, dass JSON.stringify etwas ausgeben, was man nicht erwartet.

Da message und stack Eigenschaften nicht aufzählbar sind, überspringt JSON.stringify diese Eigenschaften, wodurch Sie ein leeres Objekt {} . Um die Aufzählbarkeit besser zu verstehen, empfehle ich, diese Aufzählbarkeit und den Besitz von Eigenschaften zu lesen.

Glücklicherweise haben wir aufgrund der Art und Weise, wie Winston 3.0 entwickelt wurde (Requisiten an das Winston-Team), eine Problemumgehung dafür, die

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

In den Dokumenten- Streams, objectMode und Info-Objekten verfügt das Info-Objekt über zwei Eigenschaften: info.level und info.message . Diese info.message -Eigenschaft ist das Fehlerobjekt, wenn das alles ist, was Sie übergeben haben. Also erstellen wir ein neues Objekt, in dem message.stack und message.message (Stellen Sie sich das als Error.stack und Error.message ) sind jetzt aufzählbar, und wir fügen alle anderen Eigenschaften hinzu, die möglicherweise auch an dieses Fehlerobjekt angehängt sind.

Als nächstes erstellen Sie diesen Logger, der die obige Funktion enumerateErrorFormat() verwendet:

const logger = createLogger({
  format: format.combine(
    enumerateErrorFormat(),
    format.json()
  ),
  transports: [
    new transports.Console()
  ]
});

Dies nimmt alle message Sie übergeben, und prüft, ob es sich um ein Fehlerobjekt handelt. Wenn dies der Fall ist, wird das Aufzählungsproblem behoben. Dann wird die Nachricht an format.json , wodurch jedes Objekt (Fehler oder nicht) stringifiziert wird. Wenn es kein Objekt ist, dann ist es eine Zeichenfolge und format.json effektiv tut nichts und Sie sind frei zu Hause!

Trotzdem wäre es schön, wenn wir diese enumerateErrorFormat nicht erstellen müssten, da Fehlerobjekte häufig protokolliert werden. Soweit ich weiß, arbeitet das Winston-Team an einem Fix, der in einer späteren Version veröffentlicht wird.

Einige abschließende Anmerkungen. Dies funktioniert nur, wenn Sie logger.log({level: <level>, message: <message>}) wobei message das Fehlerobjekt ist. Beispiel:

try {
  throw(new Error('This should work'));
} catch (err) {
  logger.log({level: 'error', message: err});
}

Es gibt einen weiteren Fehler in Winston, bei dem dieser Code nicht funktioniert, wie ich in meinem anderen Beitrag oben erklärt habe:

try {
  throw(new Error('This will not work'));
} catch (err) {
  logger.error(err);
}

Aus irgendeinem Grund ist die Eigenschaft info.message undefiniert, wenn wir logger.error(err) . Hoffentlich kann @indexzero dies herausfinden.

@ SamuelMaddox17 @indexzero Danke! Ich habe versucht, logger.log({level: 'error', message: err}); und es funktioniert

Kann dies bitte für logger.error usw. behoben werden?

Es ist umständlich und ausführlich, logger.log , zumal Sie mit logger.error leicht mehrere Argumente hinzufügen können.

Hallo zusammen, ich schaue mir das an. @indexzero : Denken enumerateErrorFormat -Funktionalität standardmäßig zum json -Formatierer hinzuzufügen? Müssen wir uns separat darum kümmern, ob meta ein Error nicht nur ein object (Ich vermute, die Leute werden sich beschweren, wenn wir diesen Fall nicht auch behandeln?) ? Außerdem verwende ich master , aber anscheinend funktioniert logger.error für mich mit der obigen Lösung von @ SamuelMaddox17 :

const winston = require('winston');
const format = winston.format;

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

const logger = winston.createLogger({
  level: 'debug',
  format: format.combine(
    enumerateErrorFormat(),
    format.json()
  ),
  transports: [
    new winston.transports.Console(),
  ],
});

logger.error(new Error('whatever'));

Nach weiteren Untersuchungen scheint das oben erläuterte Problem logger.error nur bei Verwendung des Standardloggers ein Problem zu sein. @DABH , ich habe Ihren Code ausprobiert und er funktioniert für mich, aber wenn ich ihn auf den Standardlogger umschalte, funktioniert er nicht:

const winston = require('winston');
const format = winston.format;

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

winston.configure({
  transports: [
    new winston.transports.Console({
      level: 'debug',
      format: format.combine(
        enumerateErrorFormat(),
        format.json()
      ),
    })
  ]
});

winston.error(new Error('whatever'));

Zweitens stimme ich zu, dass enumerateErrorFormat zum JSON-Format hinzugefügt werden sollte. und Sie haben wahrscheinlich auch Recht mit meta .

Abschließend möchte ich darauf hinweisen, dass das von Codebeispiel dazu

{"message":"whatever","stack":"Error: whatever\n    at Object.<anonymous> (/Users/samuelmaddox/Desktop/winston-test/index.js:33:14)\n    at Module._compile (internal/modules/cjs/loader.js:689:30)\n    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)\n    at Module.load (internal/modules/cjs/loader.js:599:32)\n    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)\n    at Function.Module._load (internal/modules/cjs/loader.js:530:3)\n    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)\n    at startup (internal/bootstrap/node.js:266:19)\n    at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)","level":"error"}

Wie Sie sehen können, erzeugen die Zeilenumbruchzeichen \n bei der Ausgabe des Fehlers mit einer JSON-Funktion keine neuen Zeilen. Dies ist das erwartete Verhalten beim Aufnehmen und Konvertieren eines Objekts in JSON, aber wahrscheinlich nicht das Verhalten, das wir von einem Logger tatsächlich erwarten würden, zumindest bei der Protokollierung an der Konsole.

Vielen Dank, dass Sie sich mehr mit diesem @DABH befasst haben

Zu Ihrer Information, hier bin ich angekommen, nachdem ich ein bisschen damit gespielt habe:

import winston from 'winston';
const format = winston.format;

const printNice = format.printf(info => {
    const {level, message} = info;
    return `Logging Level: ${level} - Logging Message: ${message}`;
});

const enumerateErrorFormat = format(info => {
    if (info.message instanceof Error) {
        info.message = Object.assign({
            message: `${info.message.message}\n============\n${info.message.stack}`
        }, info.message);
    }

    if (info instanceof Error) {
        return Object.assign({
            message: `${info.message}\n============\n${info.stack}`
        }, info);
    }

    return info;
});

const logger = winston.createLogger({
    format: format.combine(
        enumerateErrorFormat(),
        format.json()
    ),
    transports: [
        new winston.transports.Console({
            format: format.combine(
                format.colorize(),
                printNice,
            ),
        })
    ]
});

export default logger;

Das Problem wird durch diesen Fehler verursacht: https://github.com/winstonjs/winston-transport/issues/31

Wir haben dieses Formular definitiv in winston2.x ohne Probleme verwendet. winston.err('some message', err); zusammen mit winston.error(err) die oben genannten enumerateErrorFormat beheben winston.error(err) aber nicht den Anwendungsfall mit err als zweitem Parameter.

@ SamuelMaddox17

logger.log ({level: ____, message: err});

es funktioniert thx

Okay, ich habe etwas entdeckt. Mein Kommentar vom 3. September ist falsch. Dies ist kein Problem mit dem Standardlogger. Dies ist ein Problem, bei dem Sie level und / oder format . @DABH hier ist dein alter Code:

const winston = require('winston');
const format = winston.format;

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

const logger = winston.createLogger({
  level: 'debug',
  format: format.combine(
    enumerateErrorFormat(),
    format.json()
  ),
  transports: [
    new winston.transports.Console(),
  ],
});

logger.error(new Error('whatever'));

Wenn Sie dies entfernen:

const logger = winston.createLogger({
  level: 'debug',
  format: format.combine(
    enumerateErrorFormat(),
    format.json()
  ),
  transports: [
    new winston.transports.Console(),
  ],
});

Und ersetzen Sie es durch Folgendes:

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console({
      level: 'debug',
      format: format.combine(
        enumerateErrorFormat(),
        format.json()
      ),
    }),
  ],
});

Dann taucht das Problem info.message === undefined auf. Ich glaube, es sollte in Ordnung sein, für jeden Transport Ebene und Format anzugeben. und ich bin fast sicher, dass dies in Winston 2.0 erlaubt war.

Hier ist Ihr Codebeispiel mit meiner Codeänderung, damit Sie es einfach ausführen und testen können:

const winston = require('winston');
const format = winston.format;

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console({
      level: 'debug',
      format: format.combine(
        enumerateErrorFormat(),
        format.json()
      ),
    }),
  ],
});

logger.error(new Error('whatever'));

Hoffentlich hilft dies, dem Problem auf den Grund zu gehen.

Ich habe https://github.com/winstonjs/winston/pull/1527 erstellt

Dies umfasst alle Optionen. Da einige Tests jedoch fehlschlagen, habe ich sie vorerst geschlossen. Die Fehler werden angesichts des Fixes erwartet, aber ich glaube nicht, dass ich in der Lage bin, den Aufruf zum Ändern / Löschen der Tests zu tätigen.

Ein fehlgeschlagener Build ist hier https://travis-ci.org/winstonjs/winston/jobs/453012141 und es ist offensichtlich, warum die Tests jetzt fehlschlagen, wenn Sie den Testcode lesen:
https://github.com/winstonjs/winston/blob/c42ab7fdc51b88db180a7dd90c52ce04ddd4e054/test/logger.test.js#L668

Gedanken?

Ich denke, das Problem liegt in dieser Zeile
const info = (msg && !(msg instanceof Error) && msg.message && msg) || {
Nachricht: msg
};
Das Hinzufügen einer Überprüfung der Fehlerinstanz scheint das Problem zu beheben, wie @crowleym hervorhebt

Für alle, die sich noch damit beschäftigen, ist dies der Workaround-Formatierer, den ich mir ausgedacht habe (Ausschnitt aus meinem logger.js-Modul):

const { format, transports, createLogger }     = require("winston");
const { combine, timestamp, colorize, printf } = format;

function devFormat() {
    const formatMessage = info => `${info.level}: ${info.timestamp} ${info.message}`;
    const formatError   = info => `${info.level}: ${info.timestamp}\n\n${info.stack}\n`;
    const format        = info => info instanceof Error ? formatError(info) : formatMessage(info);

    return combine(timestamp(), printf(format));
}

Aus irgendeinem Grund funktioniert logger.error(new Error("hello")) nur, wenn Sie den Formatierer global in winston.createLogger 🤔 definieren und dann das Fehlerobjekt in info im Formatierer erhalten.

Wenn Sie einen Formatierer pro Transport definieren, müssen Sie logger.log({level: "error", message: new Error("FAILED")}) und das Fehlerobjekt über info.message anstelle von info um auf das Fehlerobjekt zuzugreifen.

Ich würde vermuten, dass es einen Fehler beim Festlegen des Formatfelds in den Transportoptionen gibt.

Das sind nur meine 2 Cent und was für mich funktioniert hat, ich bin neu in Winston und nicht in JavaScript erfahren, also zitiere mich zu nichts.

Mein Ansatz war ein Versuch, die Grundursache zu beheben. Aber die Repo-Besitzer bekommen nicht viel Traktion ...

Ja, das verstehe ich. Ich habe buchstäblich viel Zeit damit verbracht, dies herauszufinden, weil ich neu in Winston bin und dachte, ich würde es vielleicht falsch verwenden oder die Konzepte dahinter noch nicht richtig genug verstanden.

Aber zum Glück bin ich auf ein paar Fäden gestoßen (dieser eingeschlossen), die mir etwas anderes zeigten. Hoffentlich wird dies behoben, damit ich die Problemumgehung nicht weiter verwenden muss.

Dies kann durch wintson-transport , siehe https://github.com/winstonjs/winston-transport/issues/31 für das Problem und https://github.com/winstonjs/winston-transport/pull/ 34 für eine PR.

Die direkte Protokollierung von Fehlerobjekten ist aufgrund ihrer nicht aufzählbaren Eigenschaften immer ein Chaos. Persönlich halte ich dies für eine schlechte Praxis, aber genug Leute in der Gemeinde sind fest davon überzeugt, dass wir sie unterstützen müssen.

Erwägen Sie die Verwendung von https://github.com/winstonjs/logform/pull/59 als Format zur Unterstützung solcher Verhaltensweisen. Auf der positiven Seite werden alle Randfälle zusammengefasst, die so häufig auftreten, wenn Fehler als Protokollnachrichten behandelt werden. Auf der anderen Seite wäre es ein weiteres Format, für das sich die Leute anmelden müssten (ähnlich wie bei .splat() ).

@indexzero Ich stimme eher zu, aber die direkte Fehlerprotokollierung kann in Verbindung mit dem benutzerdefinierten Protokollierungsformat / printf hilfreich sein, wenn mehrere Error -Typen unterschiedlich angezeigt werden müssen, und ich erinnere mich nicht, dass Winston 2.x dies versucht hat bekämpfe diese Praxis, wie es aus der Box erlaubt war.

Die vorgeschlagene Lösung für enumerateErrorFormat funktioniert also, unterstützt jedoch nicht das Format logger.error('some message', err) . Weil weder info.message noch info instanceof Error . Ich möchte auch auf ein anderes Problem mit dieser Lösung hinweisen. Ich protokolliere derzeit einen Fehler, der von superagent

    try {
      const response = await request
        .get('https://some/endpoint')
        .set('Authorization', bearerToken);
      logger.info('successfully received response');
      return response.body;
    } catch (e) {
      logger.error('An error was caught while getting programs');
      logger.error(e); // <<< THE ERROR LOG  
    }

Wenn wir einfach Object.assign verwenden, wird der Stapel gekühlt und die Nachricht wird gesetzt! ABER alle anderen Informationen, die Teil des Fehlers waren, werden ebenfalls protokolliert. Dies kann sehr gefährlich sein, wenn Fehler vertrauliche Daten wie Authorization Headers (die in diesem Fall als Teil des Fehlerobjekts enthalten sind).

Aber dann könnte man sagen. Dies ist kein Fehler von Winston, es ist nicht der Fehler von Winston, dass Superagent diese Daten zum Fehler hinzufügt. GENAU! Da jedoch alles flach auf dem info -Objekt gespeichert ist, wird es sehr schwierig, die alten Informationen beizubehalten und den Rest nicht zu überschreiben.

Es wäre fast schön, wenn bei Verwendung von logger.error. Es wurde angenommen, dass der zweite Parameter ein Fehler war, und er wurde als info.error auf das info -Objekt gesetzt. Wenn Sie sich dann anders anmelden, wäre die Schnittstelle { level: "error", error: YOUR ERROR OBJECT}

Ich spucke hier wirklich nur, aber die neue Oberfläche war definitiv ein wenig frustrierend (alles auf Info).

Um den Punkt, den ich angesprochen habe, näher zu erläutern, nehmen wir an, dass Sie logger.error( e ) wobei e vom Typ Fehler ist.

Dann haben Sie Winston in Ihrem Code wie folgt konfiguriert:

winston.configure({
    format: combine(
      timestamp(),
      enumerateErrorFormat(),
      ...
    ),
  });

Der Zeitstempel wird auf das Fehlerobjekt 😱 verschoben. Macht das wirklich Sinn? Denken Sie darüber nach. Das Fehlerobjekt, das Sie gesendet haben, erhält dynamisch eine neue Requisite. Zeitstempel.

Ich denke, die beste Lösung für dieses Problem wäre die Unterstützung der folgenden Syntax

logger.error('An error occurred when doing something important', { error: e } );

und dann können Sie intern einen Fehlerformatierer erstellen, der nach einem Fehlerfeld sucht!

Update zu diesen Leuten:

In der Hoffnung, dies in den nächsten Tagen zu vernähen und zu versenden. Es ist die vorletzte Ausgabe in unserem [email protected] Tracker

Hallo Leute - bitte besuchen Sie https://github.com/winstonjs/winston/pull/1562. Dies war der letzte Punkt auf unserer 3.2.0 Veröffentlichung von

Bis ein Fix veröffentlicht ist, verwende ich die folgende Problemumgehung:

logger.error = item => {
  logger.log({ level: 'error', message: item instanceof Error ? item.stack : item });
};

Ich bin auf dem neuesten Winston (3.2.1) und erhalte immer noch undefined wenn ich einen Fehler an logger.error

@ezze hatte eine großartige Lösung, aber die Stapelverfolgung wird auf eine neue Zeile gezwungen. Hier ist eine leicht modifizierte Version, die es in eine Zeile bringt (so dass es von einem einfachen Grep von Protokolldateien erfasst wird).

logger.error = item => {
  const message = item instanceof Error
    ? item.stack.replace('\n', '').replace('    ', ' - trace: ')
    : item;
  logger.log({ level: 'error', message });
};

mit Ausgabe <Error message> - trace: <stack trace>

Wenn es mit dem neuesten Winston einen einfacheren Weg gibt, lassen Sie es mich bitte @indexzero wissen. Ich bin neu in der Bibliothek und folgte den Dokumenten

Ich habe gerade den Link gesehen, den Sie zur PR gepostet haben. Bedeutet dies, dass zum Übergeben eines Fehlers an logger.error eine Nachrichtenzeichenfolge erforderlich ist, dann der Fehler?

try {
  someThing();
} catch(error) {
  logger.error(error); // what I would like to do
  logger.error('special message', error); // what I believe is required?
}

@ the-vampiire Es gab 2 Probleme, über die in diesem Thread gesprochen wurde. Das erste ist das, was das Originalplakat angesprochen hat, und das zweite ist das Problem, das ich angesprochen habe und das mit Ihrem Problem identisch ist. Ich denke, sie haben nur das Problem mit den Originalplakaten behoben. Ich wollte weiter prüfen, um sicherzugehen, und dann eine neue Ausgabe öffnen, wenn dies der Fall ist. Ich hatte leider keine Zeit tiefer zu tauchen. Wenn Sie in der Zwischenzeit logger.log({level: 'error', message: err}); wobei err ein Fehlerobjekt ist, funktioniert es.

Wenn Sie dieses Problem immer noch haben und viel Zeit verloren haben, um dies herauszufinden, funktioniert die Lösung von @ the-vampiire gut.

Warum ist dieses Ticket geschlossen?

Das Überschreiben von logger.error ist bislang die beste Lösung, da einem Fehlerobjekt, das als einzelnes Argument an error () übergeben wird, keine Zeitstempeleigenschaft hinzugefügt wird. Die meisten Leute erwarten wahrscheinlich, dass Fehlerobjekte unveränderlich sind. Wenn Sie logger.info und jede andere Level-bezogene Methode nicht überschreiben, ist es eine Überraschung, wenn die Dinge nicht wie erwartet funktionieren. Wenn Sie nicht möchten, dass Ihr Objekt unabhängig von seinem Typ geändert wird, senden Sie es nicht direkt an eine Winston-Logger-Methode.

Die Funktion wird seit [email protected] unterstützt

Anwendungsbeispiel:

const winston = require('winston');
const { transports, format } = winston;

const print = format.printf((info) => {
  const log = `${info.level}: ${info.message}`;

  return info.stack
    ? `${log}\n${info.stack}`
    : log;
});

const logger = winston.createLogger({
  level: 'debug',
  format: format.combine(
    format.errors({ stack: true }),
    print,
  ),
  transports: [new transports.Console()],
});

const error = new Error('Ooops');

logger.error(error);
logger.error('An error occurred:', error);

cc @ HRK44 @ the-vampiire

Ich bin auch immer noch auf das Problem gestoßen.
Wenn ich den Fehler wie logger.error(error); nenne, bekomme ich nur undefined .
Nur wenn ich es wie logger.error('Something went wrong', error) nenne, erhalte ich den vollständigen Fehler und kann ihn analysieren.

Ich bin auch immer noch auf das Problem gestoßen.
Wenn ich den Fehler wie logger.error(error); nenne, bekomme ich nur undefined .
Nur wenn ich es wie logger.error('Something went wrong', error) nenne, erhalte ich den vollständigen Fehler und kann ihn analysieren.
Hast du das hinzugefügt?

format.errors({ stack: true })

Ja, immer noch das gleiche Problem. Ich versuche es in einem Kern zu reproduzieren.

@ OBrown92 Ich habe heute das gleiche Problem konfrontiert. Ich kann bestätigen, dass es funktioniert, wenn format.errors({ stack: true }) auf den Logger angewendet wird, nicht auf den Transport. In diesem Fall können sowohl logger.error(error); als auch logger.error('Something went wrong', error) . Es gibt jedoch ein Problem, wenn ich versuche, format.errors({ stack: true }) auf den ausgewählten Transport anzuwenden. In diesem Fall bekomme ich undefined für logger.error(error); , aber logger.error('Something went wrong', error) funktioniert einwandfrei.

Ich bin mir nicht sicher, ob es sich um ein erwartetes Verhalten oder um einen Fehler handelt, aber ich habe viel Zeit darauf verwendet, die Ursache zu finden. Bitte beheben Sie dies oder erwähnen Sie dies irgendwo in Ihrer Dokumentation. Es wäre wirklich hilfreich.

Wie auch immer, ich bin sehr dankbar für Ihre Arbeit an diesem großartigen Projekt.

Ich hatte das gleiche Problem, also habe ich dieses Paket geschrieben, utils-deep-clone . Hör zu.

format.errors is not a function ... nun, das ist eine Überraschung.

@holmberd
Haben Sie die Formate aus dem Winston-Paket importiert?
Anwendungsbeispiel:
const { format } = require('winston')
Oder
const winston = require('winston'); const { format } = winston;

@aybhalala yepp, obwohl es keine Rolle spielt, dass der Fehlerstapel ohne ihn an printf wird.

Update: Da es immer noch Probleme damit gibt, mache ich seit einiger Zeit Folgendes und es hat großartig funktioniert

// Grab the default winston logger
const winston = require('winston');

const { format } = winston;
const { combine, timestamp } = format;

// Custom format function that will look for an error object and log out the stack and if 
// its not production, the error itself
const myFormat = format.printf((info) => {
  const { timestamp: tmsmp, level, message, error, ...rest } = info;
  let log = `${tmsmp} - ${level}:\t${message}`;
  // Only if there is an error
  if ( error ) {
    if ( error.stack) log = `${log}\n${error.stack}`;
    if (process.env.NODE_ENV !== 'production') log = `${log}\n${JSON.stringify(error, null, 2)}`;
  }
  // Check if rest is object
  if ( !( Object.keys(rest).length === 0 && rest.constructor === Object ) ) {
    log = `${log}\n${JSON.stringify(rest, null, 2)}`;
  }
  return log;
});

 winston.configure({
    transports: [
      new winston.transports.Console({
        level: 'debug',
        timestamp: true,
        handleExceptions: true
    }),
  ];
    format: combine(
      trace(),
      timestamp(),
      myFormat
    ),
  });


// Finally you log like this
logger.error('An error occurred!!!', { error } );

^^ Das ist die erwartete Verwendung. Da bloße Sterbliche dies niemals herausfinden werden, sollte dies dokumentiert werden.

Da bloße Sterbliche dies niemals herausfinden werden, sollte dies dokumentiert werden

😂 +1 zu diesem Kumpel

Ich verwende Winston mit einem Transport von Drittanbietern ( @google-cloud/logging-winston ), damit ich etwas weniger Kontrolle über die Syntax habe. Außerdem finde ich das nur intuitiver:

const error = new Error('something bad happened');
logger.error('was doing this and', error);

Bei der Anmeldung an der Konsole verkette ich den Stapel mit der Nachricht. Aber das Ergebnis ist ungefähr so:

ERROR was doing this andsomething bad happened Error: something bad happened <rest of the stack.>

Da Winston meta.message mit der ursprünglichen Nachricht verknüpft, gibt es die seltsamen andsomething und die doppelte Nachricht, die ebenfalls im Stapel gedruckt wird. Dies ist in # 1660 beschrieben.

Es sieht so aus, als würde # 1664 versuchen, dies zu beheben. In der Zwischenzeit habe ich einen Formatierer geschrieben, der diese Verkettung "rückgängig macht": https://github.com/winstonjs/winston/issues/1660#issuecomment -512226578

@dandv

logger.error('Caught error:', e);

Dies funktioniert nicht, da im Gegensatz zu console.log() das logger.<level>(message) des Winston nur einen Parameter namens message akzeptiert. Dieser Nachrichtenparameter ist entweder ein Objekt oder eine Zeichenfolge (jemand korrigiert mich, wenn ich falsch liege, aber das ist mein Verständnis).

Beachten Sie, dass Sie auch logger.log({level: <level>, message: <message>}) . Um mehr über diese beiden Funktionen zu erfahren, würde ich empfehlen, diesen Teil der Dokumente zu lesen: Winston Docs on Log Levels . Lesen Sie unbedingt die Verwendung der Protokollierungsstufen durch

logger.error(`Caught error: ${e}`);

Ich kann nicht definitiv sagen, warum dies den Stack nicht ausgibt, aber ich weiß, dass dies kein Problem mit Winston ist. Wenn Sie console.log(`Caught error: ${e}`) ausprobieren, ist der Stapel ebenfalls nicht enthalten. Ich habe nicht viel mit Vorlagenliteralen gearbeitet, daher funktionieren Vorlagenliterale entweder nicht gut mit Objekten, oder die Konsole.log von javascript erkennt das Objekt als Fehlerobjekt und gibt daher nur die Eigenschaft message aus. Das ist meine beste Vermutung.

logger.error(`Caught error: ${JSON.stringify(e)}`)

Dieser bringt auf den Punkt, worum es in diesem Bug-Thread geht. Zunächst müssen Sie einige technische Details zu Javascript verstehen. Beachten Sie, dass Sie beim Versuch von console.log(`Caught error: ${JSON.stringify(e)}`) auch dieselbe Ausgabe Caught error: {} . Wie @indexzero erklärte:

message und stack Eigenschaften auf Error sind nicht aufzählbar, was dazu führt, dass JSON.stringify etwas ausgeben, was man nicht erwartet.

Da message und stack Eigenschaften nicht aufzählbar sind, überspringt JSON.stringify diese Eigenschaften, wodurch Sie ein leeres Objekt {} . Um die Aufzählbarkeit besser zu verstehen, empfehle ich, diese Aufzählbarkeit und den Besitz von Eigenschaften zu lesen.

Glücklicherweise haben wir aufgrund der Art und Weise, wie Winston 3.0 entwickelt wurde (Requisiten an das Winston-Team), eine Problemumgehung dafür, die

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

In den Dokumenten- Streams, objectMode und Info-Objekten verfügt das Info-Objekt über zwei Eigenschaften: info.level und info.message . Diese info.message -Eigenschaft ist das Fehlerobjekt, wenn das alles ist, was Sie übergeben haben. Also erstellen wir ein neues Objekt, in dem message.stack und message.message (Stellen Sie sich das als Error.stack und Error.message ) sind jetzt aufzählbar, und wir fügen alle anderen Eigenschaften hinzu, die möglicherweise auch an dieses Fehlerobjekt angehängt sind.

Als nächstes erstellen Sie diesen Logger, der die obige Funktion enumerateErrorFormat() verwendet:

const logger = createLogger({
  format: format.combine(
    enumerateErrorFormat(),
    format.json()
  ),
  transports: [
    new transports.Console()
  ]
});

Dies nimmt alle message Sie übergeben, und prüft, ob es sich um ein Fehlerobjekt handelt. Wenn dies der Fall ist, wird das Aufzählungsproblem behoben. Dann wird die Nachricht an format.json , wodurch jedes Objekt (Fehler oder nicht) stringifiziert wird. Wenn es kein Objekt ist, dann ist es eine Zeichenfolge und format.json effectivley tut nichts und Sie sind frei zu Hause!

Trotzdem wäre es schön, wenn wir diese enumerateErrorFormat nicht erstellen müssten, da Fehlerobjekte häufig protokolliert werden. Soweit ich weiß, arbeitet das Winston-Team an einem Fix, der in einer späteren Version veröffentlicht wird.

Einige abschließende Anmerkungen. Dies funktioniert nur, wenn Sie logger.log({level: <level>, message: <message>}) wobei message das Fehlerobjekt ist. Beispiel:

try {
  throw(new Error('This should work'));
} catch (err) {
  logger.log({level: 'error', message: err});
}

Es gibt einen weiteren Fehler in Winston, bei dem dieser Code nicht funktioniert, wie ich in meinem anderen Beitrag oben erklärt habe:

try {
  throw(new Error('This will not work'));
} catch (err) {
  logger.error(err);
}

Aus irgendeinem Grund ist die Eigenschaft info.message undefiniert, wenn wir logger.error(err) . Hoffentlich kann @indexzero dies herausfinden.

Sehr gute Erklärung, ich möchte nur hinzufügen, dass mit logger.error( Gefangener Fehler: $ {e} ); Sie den Stapel verlieren, weil das String-Literal in Javascript funktioniert, `${e}` ist genau das gleiche wie e.toString() , daher ist das Drucken nur der Fehlermeldung das erwartete Verhalten.

Dies ist immer noch ein Problem? Ich habe immer noch Probleme damit:

const { createLogger, format, transports } = require('winston')

const { combine } = format

const errorFormatter = format((info) => {
  console.log(info)
  return info
})


const consoleTransport = new transports.Console({
  format: combine(errorFormatter()),
})

const logger = createLogger({
  transports: [
    consoleTransport,
  ],
})

try {
  throw new Error('Error message')
} catch(err) {
  logger.error(err) // info doesnt have the error object
  logger.error('', err) // info have the error object
}

Dies ist immer noch ein Problem? Ich habe immer noch Probleme damit:

const { createLogger, format, transports } = require('winston')

const { combine } = format

const errorFormatter = format((info) => {
  console.log(info)
  return info
})


const consoleTransport = new transports.Console({
  format: combine(errorFormatter()),
})

const logger = createLogger({
  transports: [
    consoleTransport,
  ],
})

try {
  throw new Error('Error message')
} catch(err) {
  logger.error(err) // info doesnt have the error object
  logger.error('', err) // info have the error object
}

selbes Problem hier...

Nachdem ich mir den Quellcode angesehen habe, kann ich sehen, welche Parameter akzeptiert werden:

Schnittstelle LeveledLogMethod {
(Nachricht: Zeichenfolge, Rückruf: LogCallback): Logger;
(Nachricht: Zeichenfolge, Meta: beliebig, Rückruf: LogCallback): Logger;
(message: string, ... meta: any []): Logger;
(infoObject: object): Logger;
}}

Wenn Sie also das Fehlerobjekt als ersten Parameter übergeben, wird nur die Fehlermeldung angezeigt, da nur Zeichenfolgen verstanden werden. Wenn Sie den Fehler im zweiten Parameter übergeben, können Sie auf den Stack-Trace in info.stack zugreifen

Übrigens konnte ich das nirgendwo in den Dokumenten finden

Ich fand zwei Lösungen, die erste ist die Verwendung format.errors , auf erwähnt logform im Stammlogger, dann eine messageFormatter erstellen mit format.printf und ein bedingt hinzufügen stack Feld extrahiert aus info ( format.errors({ stack: true}) wird das hinzufügen).

Die andere Lösung, die ich bevorzugte, war das Hacken in Logger auf Winston-Ebene:

const addArgs = format((info) => {
  const args: any[] = info[Symbol.for('splat')]
  info.args = args ? [...args] : []
  return info
})

const messageFormatter = format.printf(info => {
  const { timestamp: timeString = '', message, args = [] } = info
  const formattedMsgWithArgs = util.formatWithOptions({ colors: true }, message, ...args)
  const msg = `${timeString} - ${info.level}: ${formattedMsgWithArgs}`
  return msg
})

const logger = createLogger({
  format: format.combine(
    addArgs(),
    format.timestamp({ format: 'HH:mm:ss.SSS' })
  ),

  transports: [
    new transports.Console({
      format: format.combine(format.colorize(), messageFormatter),
    }),
  ],
})

const levels = ['debug', 'info', 'error']
levels.forEach((level) => {
  logger[level] = (msg: any, ...remains: any) => {
    if(typeof msg != "string") {
      return logger.log(level, '', msg, ...remains)
    }

    logger.log(level, msg, ...remains)
  }  
})

Es scheint, dass ich auf diese Weise eine Fehlerprotokollierung erhalten kann, die console.log ähnelt

Ich kann überprüfen, ob der Kommentar von format.errors im _parent_-Logger sein muss, korrekt ist. Wenn ich so etwas mache:

winston.loggers.add("default");
const log = winston.loggers.get("default");
/* get a `transportOptions` object and a `transportType` */
transportOptions.format = logform.format.combine(
  logform.format.errors({ stack: true }),
  logform.format.timestamp(),
  logform.format.printf(myFormatter)
);
log.add(new winston.transports[transportType](transportOptions);

Die Behandlung von Error -Objekten erfolgt so, als ob es sich um eine Zeichenfolge handelt. Wenn ich jedoch so etwas mache:

winston.loggers.add("default");
const log = winston.loggers.get("default");
log.format = logform.format.errors({ stack: true });
/* get a `transportOptions` object and a `transportType` */
transportOptions.format = logform.format.combine(
  logform.format.timestamp(),
  logform.format.printf(myFormatter)
);
log.add(new winston.transports[transportType](transportOptions);

Die Behandlung von Error -Objekten funktioniert ordnungsgemäß.

Es scheint mir, dass der Fehler hier ist, dass es keinen Unterschied im Verhalten geben sollte.

Das ist also nach 1 Jahr noch nicht behoben? Muss ich Winston Logger Code hacken, damit es funktioniert?

Ja, das bereitete mir genug Kopfschmerzen, so dass Winston viel mehr Ärger zu machen schien, als es für meinen relativ einfachen Anwendungsfall wert war ... Am Ende schrieb ich nur meine eigene kleine Logger-Klasse, und ich würde anderen empfehlen, dasselbe zu tun, es sei denn, Winston bietet etwas, das Sie wirklich brauchen.

Leute, wirklich? das ist frustrierend...

Ich bin kein Committer, aber ich kann wahrscheinlich zu Recht sagen, dass dies nicht „behoben“ werden wird, weil es nicht kaputt ist. Winston ist es wert, benutzt zu werden. Sie müssen es nur konfigurieren - der beste Rat für mich ist oben unter https://github.com/winstonjs/winston/issues/1338#issuecomment -506354691

Noch nicht ?

Update: Da es immer noch Probleme damit gibt, mache ich seit einiger Zeit Folgendes und es hat großartig funktioniert

// Grab the default winston logger
const winston = require('winston');

const { format } = winston;
const { combine, timestamp } = format;

// Custom format function that will look for an error object and log out the stack and if 
// its not production, the error itself
const myFormat = format.printf((info) => {
  const { timestamp: tmsmp, level, message, error, ...rest } = info;
  let log = `${tmsmp} - ${level}:\t${message}`;
  // Only if there is an error
  if ( error ) {
    if ( error.stack) log = `${log}\n${error.stack}`;
    if (process.env.NODE_ENV !== 'production') log = `${log}\n${JSON.stringify(error, null, 2)}`;
  }
  // Check if rest is object
  if ( !( Object.keys(rest).length === 0 && rest.constructor === Object ) ) {
    log = `${log}\n${JSON.stringify(rest, null, 2)}`;
  }
  return log;
});

 winston.configure({
    transports: [
      new winston.transports.Console({
        level: 'debug',
        timestamp: true,
        handleExceptions: true
    }),
  ];
    format: combine(
      trace(),
      timestamp(),
      myFormat
    ),
  });


// Finally you log like this
logger.error('An error occurred!!!', { error } );

Woher kommt die Spur?

Nein, das ist für eine Protokollierungsbibliothek nicht akzeptabel.
Der Maintainer sollte einfach ein gut hervorgehobenes Beispiel in Dokumente einfügen, in dem gezeigt wird, wie ein Fehler protokolliert wird, sogar das benutzerdefinierte printf-Format und das Nicht-json-Format definiert werden und wo Sie Fehler mit etwas wie logger.error ("etwas", err) und logger protokollieren können .error (err)
Winston schien großartig zu sein, aber dieses Problem ist unglaublich inakzeptabel

Dies ist meine Einstellung zum Protokollieren von Fehlern mit Winston. Es ist nicht einzigartig, viele Leute oben haben funktionierende Lösungen, die auf denselben Konzepten basieren.

Hintergrund
Ich verwende @jsdevtools/ono , um beliebige Objekttypen in benutzerdefinierte Fehler zu verpacken, aber unabhängig davon scheint diese Lösung bei nativen Knotenfehlern (z. B. fs eperm-Fehlern) und benutzerdefinierten Fehlerklassen immer noch gut zu funktionieren.

Erläuterung
Grundsätzlich verlasse ich mich auf format.errors({stack:true}) und format.metadata() . Wie unter https://github.com/winstonjs/winston/issues/1338#issuecomment -532327143 erwähnt, muss dies im übergeordneten Formatierer erfolgen.

Metadaten helfen dabei, alle benutzerdefinierten Eigenschaften des Fehlerobjekts auf info.metadata .

Ich wollte drei Arten von Informationen drucken: die Fehlermeldung, die Stapelverfolgung und die Eigenschaften des Fehlerobjekts. Die Fehlermeldung war bereits Klartext. Ich habe den Stapel info.metadata.stack mit dem Modul pretty-error hübsch gedruckt. Für die Eigenschaften des Fehlerobjekts wollte ich nicht, dass die Stacktrace erneut angezeigt wird. Deshalb habe ich das Objekt geklont und die Stacktrace-Eigenschaft gelöscht. Ich habe das Fehlerobjekt dann mit fast-safe-stringify hübsch gedruckt. Dies ist das gleiche Stringify-Modul, auf das sich Winston stützt.

    const lodash = require("lodash");
    const path = require("path");
    const winston = require("winston");
    const { default: stringify } = require("fast-safe-stringify");
    const logDir = "D:/temp/logs";

    // pretty formatting
    const PrettyError = require("pretty-error");
    const pe = new PrettyError();
    pe.withoutColors()
        .appendStyle({
            'pretty-error > trace':
            {
                display: 'inline'
            },
            'pretty-error > trace > item':
            {
                marginBottom: 0,
                bullet: '"*"'
            }
        })
        // @ts-ignore
        .alias(/.*[\\\/]CelebrityQuery/i, "<project>")
        .alias(/\[CelebrityQuery\][\\\/]?/i, "")
        .skip(/** <strong i="23">@type</strong> {(_:any) => boolean} */ (traceline => {
            if (traceline && traceline.dir) {
                return traceline.dir.toString().startsWith("internal");
            }
            return false;
        }))
        .skipNodeFiles();

    const consoleFormat = winston.format.combine(
        winston.format.colorize(),
        winston.format.timestamp({
            format: 'DD MMM HH:mm:ss'
        }),
        winston.format.printf(info => {
            if (!lodash.isEmpty(info.metadata) && info.metadata.hasOwnProperty("stack")) {
                let dup = lodash.clone(info.metadata);
                delete dup.stack;
                const errBody = stringify(dup, undefined, 4);
                const stack = pe.render({ stack: info.metadata.stack });
                return `${info.timestamp} ${info.level} ${info.message}${errBody}\n${stack}`;
            } else if (lodash.isString(info.message)) {
                return `${info.timestamp} ${info.level} ${info.message}`;
            } else {
                return `${info.timestamp} ${info.level} ${stringify(info.message, undefined, 4)}`;
            }
        })
    );
    const logFormat = winston.format.combine(winston.format.timestamp(), winston.format.json());
    return winston.createLogger({
        level: 'debug',
        format: winston.format.combine(
            winston.format.errors({ stack: true }),
            winston.format.metadata()
        ),
        transports: [
            new winston.transports.Console({
                format: consoleFormat,
                level: 'info',
            }),
            new winston.transports.File({
                filename: path.join(logDir, "stdout.json"),
                format: logFormat,
                level: 'debug',
                maxsize: 1000000,
                tailable: true
            })
        ]
    });

Log Screenshot

PS: Ich finde auch die unter https://github.com/winstonjs/winston/issues/1338#issuecomment -506354691 erwähnte Lösung eine gute Alternative. Verwenden Sie also logger.warn("Oh no", { error: new Error() }) und verweisen Sie dann in Ihrem benutzerdefinierten Formatierer auf info.error .

@tiagonapoli Ihre Lösung zur Verwendung von format.errors für den übergeordneten Logger hat bei mir funktioniert:

const logger = createLogger({
  transports: loggerTransports,
});

logger.format = format.errors({ stack: true });

Es ist ziemlich schmerzhaft, diesen Logger zu konfigurieren ... Könnte er sich nicht einfach wie console.log out

@ will093 gleich hier. Ich war schon wieder bei diesem Thema und verstehe nicht, warum mein console.log nett und sauber ist und das Winston-Format Scheiße ist.

Meine 2 ¢

// Enable console logging when not in production
if (process.env.NODE_ENV !== "production") {
    logger.add(new transports.Console({
        format: format.combine(
            format.colorize(),
            format.simple(),
            format.printf(info => {
                const { level, ...rest } = info;
                let rtn = "";
                // rtn += info.timestamp;
                rtn += "[" + info.level + "] ";
                if (rest.stack) {
                    rtn += rest.message.replace(rest.stack.split("\n")[0].substr(7),"");
                    rtn += "\n";
                    rtn += "[" + level + "] ";
                    rtn += rest.stack.replace(/\n/g, `\n[${level}]\t`);
                } else {
                    rtn += rest.message;
                }
                return rtn;
            }),
        ),
    }));
}

Beispiel für logger.error("Error during schema stitching", e);

image

Die Verwendung der Lösung von @tiagonapoli und @ will093 , es nur dem übergeordneten

const createLogger = () => {
  const logFormatter = winston.format.printf(info => {
    let { timestamp, level, code, stack, message } = info;

    // print out http error code w/ a space if we have one
    code = code ? ` ${code}` : '';
    // print the stack if we have it, message otherwise.
    message = stack || message;

    return `${timestamp} ${level}${code}: ${message}`;
  });

  return winston.createLogger({
    level: 'info',
    // put the errors formatter in the parent for some reason, only needed there:
    format: winston.format.errors({ stack: true }),
    transports: new winston.transports.Console({
      format: winston.format.combine(
        winston.format.timestamp(),
        logFormatter
      ),
  });
};

funktioniert mit einem Stapel, wenn er mit einem Fehler wie dem folgenden aufgerufen wird: logger.error(error) , funktioniert mit einem String, wenn er wie logger.error('a regular message') aufgerufen wird, sieht in meinen Protokollen folgendermaßen aus:

2020-09-23T20:05:30.30Z info: Feathers application started on http://localhost:3030
2020-09-23T20:05:35.40Z info: job queue - redis ready, registering queues...
2020-09-23T20:05:40.25Z error 401: NotAuthenticated: invalid authorization header
    at new NotAuthenticated (/path/to/server/node_modules/@feathersjs/errors/lib/index.js:94:17)
    at Object.<anonymous> (/path/to/server/src/hooks/authentication.js:123:456)
    at /path/to/server/node_modules/@feathersjs/commons/lib/hooks.js:116:46

Dies versucht nicht , Winstons logger.error('message here', error) -Inkompatibilität mit console.log zu lösen, was die komplexere Lösung von

Wenn Sie JSON-Protokolle mögen, können Sie auch die logFormatter hier ablegen und stattdessen winston.format.json() , die den Stapel weiterhin enthalten - aber es ist nicht schön.

Update: Da es immer noch Probleme damit gibt, mache ich seit einiger Zeit Folgendes und es hat großartig funktioniert

// Grab the default winston logger
const winston = require('winston');

const { format } = winston;
const { combine, timestamp } = format;

// Custom format function that will look for an error object and log out the stack and if 
// its not production, the error itself
const myFormat = format.printf((info) => {
  const { timestamp: tmsmp, level, message, error, ...rest } = info;
  let log = `${tmsmp} - ${level}:\t${message}`;
  // Only if there is an error
  if ( error ) {
    if ( error.stack) log = `${log}\n${error.stack}`;
    if (process.env.NODE_ENV !== 'production') log = `${log}\n${JSON.stringify(error, null, 2)}`;
  }
  // Check if rest is object
  if ( !( Object.keys(rest).length === 0 && rest.constructor === Object ) ) {
    log = `${log}\n${JSON.stringify(rest, null, 2)}`;
  }
  return log;
});

 winston.configure({
    transports: [
      new winston.transports.Console({
        level: 'debug',
        timestamp: true,
        handleExceptions: true
    }),
  ];
    format: combine(
      trace(),
      timestamp(),
      myFormat
    ),
  });


// Finally you log like this
logger.error('An error occurred!!!', { error } );

Wo ist die Definition von trace ()?

Update: Da es immer noch Probleme damit gibt, mache ich seit einiger Zeit Folgendes und es hat großartig funktioniert

// Grab the default winston logger
const winston = require('winston');

const { format } = winston;
const { combine, timestamp } = format;

// Custom format function that will look for an error object and log out the stack and if 
// its not production, the error itself
const myFormat = format.printf((info) => {
  const { timestamp: tmsmp, level, message, error, ...rest } = info;
  let log = `${tmsmp} - ${level}:\t${message}`;
  // Only if there is an error
  if ( error ) {
    if ( error.stack) log = `${log}\n${error.stack}`;
    if (process.env.NODE_ENV !== 'production') log = `${log}\n${JSON.stringify(error, null, 2)}`;
  }
  // Check if rest is object
  if ( !( Object.keys(rest).length === 0 && rest.constructor === Object ) ) {
    log = `${log}\n${JSON.stringify(rest, null, 2)}`;
  }
  return log;
});

 winston.configure({
    transports: [
      new winston.transports.Console({
        level: 'debug',
        timestamp: true,
        handleExceptions: true
    }),
  ];
    format: combine(
      trace(),
      timestamp(),
      myFormat
    ),
  });


// Finally you log like this
logger.error('An error occurred!!!', { error } );

Woher kommt die Spur?

eine Antwort darauf?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen