Winston: [3.0.0] لا يتم تحليل كائن الخطأ أو طباعته

تم إنشاؤها على ٢٩ مايو ٢٠١٨  ·  68تعليقات  ·  مصدر: winstonjs/winston

من فضلك أخبرنا عن بيئتك:

  • _ winston الإصدار؟ _
  • النواتج _ node -v _ v8.11.1
  • _ نظام التشغيل؟ _ (Windows أو macOS أو Linux) macOS
  • _Language؟ _ (الكل | TypeScript XX | ES6 / 7 | ES5 | Dart) الكل

ما المشكلة؟

يؤدي تسجيل كائن Error إلى ظهور رسالة فارغة:

مثال:

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

الناتج الناتج:

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

أيضا:

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

النتائج في:

{"level":"error"}

ماذا تتوقع أن يحدث بدلا من ذلك؟

أتوقع أن يحتوي مفتاح الرسالة على رسالة الخطأ على الأقل. إذا حاولت استخدام مُنسق مخصص ، فإن info أيضًا لا يحتوي على كائن خطأ فيه ، لذا يجب إزالته في مكان ما؟

معلومات أخرى

اسمحوا لي أن أعرف كيف يمكنني المساعدة - يسعدني النقر على العلاقات العامة ولكني لا أعرف طريقي حول [email protected] بما يكفي حتى الآن للعثور عليه

bug important

التعليق الأكثر فائدة

لا حقًا ، هذا غير مقبول لمكتبة التسجيل.
يجب على Maintainer ببساطة وضع مثال مميز جيدًا في المستندات حيث يتم عرض كيفية تسجيل خطأ ، حتى تحديد تنسيق printf مخصص وتنسيق غير json وحيث يمكنك تسجيل خطأ بشيء مثل logger.error ("شيء ما" ، خطأ) ومسجل . Error (Error)
بدا ونستون عظيماً ولكن هذه القضية غير مقبولة بشكل لا يصدق

ال 68 كومينتر

لدينا بعض التغطية الاختبارية لهذا ، لكن من الواضح أننا بحاجة إلى المزيد. ما يحدث تحت الأغطية:

  1. لديك Error يتم تمرير المثال على طول objectMode تيار الأنابيب سلسلة
  2. التنسيق الافتراضي لـ Logger هو json (انظر: json كود التنسيق في logform )
  3. خصائص message و stack على Error غير قابلة للعد مما يتسبب في إخراج JSON.stringify لشيء لا يتوقعه المرء.
console.log(JSON.stringify(new Error('lol nothing here')));
// '{}'

من منظور التصميم ، قدم winston@3 formats لهذا النوع من المشاكل بالضبط لزيادة الأداء. عند الحديث عن الأداء ، من المثير للاهتمام أن pino يفعل شيئًا مثيرًا للاهتمام هنا . ربما يكون الحل هو تنفيذ شيء مشابه لـ asJson json بالتنسيق الافتراضي

إذا كان أي شخص يبحث عن حل سريع يمكنك تضمين enumerateErrorFormat في تنسيق المسجل الخاص بك في الوقت الحالي. نأمل أن يكون لدينا إصلاح لهذا قبل 3.0.0 الأسبوع القادم (أو بعد فترة وجيزة في 3.0.1 )

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 ، حاولت اتباع الحل البديل الخاص بك ، لكنه لا يعمل. هل تعرف لماذا؟

المنسق:
"` ` javascript const level = settings.debug ? 'debug' : 'info'; const printFormat = winston.format.printf(info => $ {info.timestamp} - $ {info.level}: $ {info.message}`)؛
const enumerateErrorFormat = winston.format (info => {
إذا (info.message مثيل خطأ) {
info.message = تعيين الكائن ({
الرسالة: info.message.message ،
كومة: info.message.stack ،
} ، info.message) ؛
}
إذا (معلومات المثيل من الخطأ) {
إرجاع الكائن. تعيين ({
الرسالة: info.message ،
كومة: info.stack ،
}، معلومات)؛
}
معلومات العودة
}) ؛

const consoleLogger = winston.createLogger ({
مستوى،
التنسيق: winston.format.timestamp () ،
النقل: [
winston.transports.Console الجديد ({
التنسيق: winston.format.combine (
winston.format.colorize () ،
تعدادErrorFormat () ،
printFormat ،
) ،
}) ،
] ،
}) ؛
Code: جافا سكريبت
يحاول {
// خطأ في إلقاء التعليمات البرمجية
} catch (يخطئ) {
logger. Error (يخطئ) ؛
}
Output:
2018-06-28 T21: 17: 25.140Z - خطأ: غير محدد
Info object: جافا سكريبت
{المستوى: '\ u001b [31 خطأ \ u001b [39 دقيقة' ، الطابع الزمني: '2018-06-28T21: 17: 25.140Z'، [الرمز (المستوى)]: 'خطأ'}
""
أين هي سمة الرسالة في سجل الأخطاء؟

sandrocsimas لقد لاحظت أنك بحاجة إلى إعطاء وظيفة enumerateErrorFormat

المنسق

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

ما زلت لا أفهم لماذا

أعتقد أنني أعاني من نفس الخطأ مثلsandrocsimas.

هنا هو بلدي المسجل التكوين:

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

إذا اختبرته باستخدام كتلة التعليمات البرمجية هذه:

اختبار أ

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

حيث لا يحتوي new Error() على قيمة رسالة ، أحصل على هذا الناتج:

الإخراج أ

** 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]

حيث error: [object Object] هو بالضبط ما توقعته

ومع ذلك ، إذا اختبرته باستخدام كتلة التعليمات البرمجية هذه:

اختبار ب

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

حيث يحتوي new Error() على قيمة رسالة ، أحصل على هذا الناتج:

الإخراج ب

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

كما ترى ، أحصل على نفس error: undefined الذي يحصل عليه sandrocsimas كنت أتوقع الحصول على error: [object Object]

ملاحظة ، إذا جربت هذا الجزء من التعليمات البرمجية:

اختبار ج

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

حيث أستخدم logger.log بدلاً من logger.error أحصل على نفس الناتج مثل المخرج A أعلاه

لدي نفس المشكلة. أنا جديد في وينستون. حاولت حل indexzero ولكن لا يعمل. هل لديك أي حل؟

@ nvtuan305 ، هل جربت حل logger.log({level: ____, message: err}); لن يعمل إذا كنت تقوم بعمل logger.info أو logger.error أو أي نكهة أخرى logger.<level> . أنا على يقين من أن هذا خطأ كما حددته أعلاه ويجب إصلاحه في إصدار لاحق.

هل أفتقد شيئًا ما ، أم أنه صداع كامل (أو حتى مستحيل؟) للحصول على نفس النتيجة التي يحصل عليها المرء بسهولة من console.log / error / warn؟

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: {}
}

ما هو كود Winston المكافئ للحصول على نفس الإخراج مثل
console.error('Caught error:', error); ؟

وأين وثائق المعلمات المأخوذة بواسطة طرق الراحة على كائن المسجل؟

تضمين التغريدة

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

هذا لا يعمل لأنه ، على عكس console.log() ، فإن logger.<level>(message) winston يأخذ معلمة واحدة فقط تسمى message. معلمة الرسالة هذه هي إما كائن أو سلسلة (

لاحظ أنه يمكنك أيضًا استخدام logger.log({level: <level>, message: <message>}) . لمعرفة المزيد حول هاتين الوظيفتين ، أوصي بقراءة هذا الجزء من المستندات: Winston Docs on Log Levels . تأكد من قراءة استخدام مستويات التسجيل

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

لا أستطيع أن أقول بشكل قاطع لماذا لا ينتج هذا المكدس ، لكنني أعرف أن هذه ليست مشكلة مع Winston. إذا جربت console.log(`Caught error: ${e}`) فإنه لا يشمل أيضًا المكدس. لم أعمل مع القوالب الحرفية كثيرًا ، لذا إما أن القوالب الحرفية لا تعمل بشكل جيد مع الكائنات ، أو أن console.log الخاص بجافا سكريبت يتعرف على الكائن ككائن خطأ وبالتالي ينتج فقط خاصية الرسالة. هذا هو أفضل تخميني.

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

هذا واحد يصل إلى قلب موضوع هذا الخطأ. أولاً يجب أن تفهم بعض التفاصيل الفنية حول جافا سكريبت. لاحظ أنك إذا جربت console.log(`Caught error: ${JSON.stringify(e)}`) فستحصل أيضًا على نفس الناتج Caught error: {} . كما أوضح indexzero :

خصائص message و stack على Error غير قابلة للعد مما يؤدي إلى إخراج JSON.stringify لشيء لا يتوقعه المرء.

بشكل أساسي ، نظرًا لأن خصائص message و stack غير قابلة للعد ، فإن JSON.stringify يتخطى تلك الخصائص وهو ما ينتهي بك المطاف بكائن فارغ {} . لفهم التعداد بشكل أفضل ، أوصي بقراءة هذا التعداد وملكية العقارات .

لحسن الحظ ، نظرًا للطريقة التي تم بها تصميم Winston 3.0 (الدعائم لفريق Winston) ، لدينا حل بديل لهذا الذي قدمهindexzero . سأساعد في شرح ذلك. تقوم أولاً بإنشاء هذه الوظيفة:

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

من تدفقات المستندات info.level ، و info.message . هذه الخاصية info.message هي كائن الخطأ إذا كان هذا هو كل ما قمت بتمريره. لذلك قمنا بإنشاء كائن جديد حيث message.stack و message.message (فكر فيه على أنه Error.stack و Error.message ) الآن قابلان للعد ، ونقوم بتضمين أي خصائص أخرى قد تكون مرتبطة أيضًا بكائن الخطأ هذا.

بعد ذلك ، ستنشئ هذا المسجل الذي يستخدم الوظيفة enumerateErrorFormat() أعلاه:

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

سيستغرق هذا أي message تمرره وتحقق مما إذا كان كائن خطأ. إذا كان الأمر كذلك ، فسيتم إصلاح مشكلة التعداد. ثم تقوم بتمرير الرسالة إلى format.json والتي ستعمل على ضبط أي كائن (خطأ أم لا). إذا لم يكن كائنًا ، فهو عبارة عن سلسلة و format.json effectivley لا يفعل شيئًا ، وأنت في المنزل مجانًا!

ومع ذلك ، سيكون من الجيد ألا نضطر إلى إنشاء enumerateErrorFormat لأن كائنات الخطأ يتم تسجيلها بشكل شائع. كما أفهمها ، يعمل فريق Winston على إصلاح سيتم إصداره في إصدار لاحق.

بعض الملاحظات النهائية. يعمل هذا فقط إذا كنت تستخدم logger.log({level: <level>, message: <message>}) حيث تكون الرسالة هي كائن الخطأ. مثال:

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

هناك خطأ آخر في Winston حيث لا يعمل هذا الرمز ، كما أوضحت في رسالتي الأخرى أعلاه:

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

لسبب ما ، تكون الخاصية info.message غير محددة عندما نستخدم logger.error(err) . نأمل أن يتمكنindexzero من معرفة هذا.

@ SamuelMaddox17indexzero شكرا لك! حاولت استخدام logger.log({level: 'error', message: err}); وهو يعمل

هل يمكن إصلاح هذا من فضلك لخطأ المسجل ، وما إلى ذلك؟

يعد استخدام logger.log أمرًا مرهقًا ومُطولًا ، خاصة أنه مع logger.error يمكنك بسهولة إضافة وسيطات متعددة.

مرحبًا بالجميع ، أنا أبحث في هذا. indexzero : ما زلت أعتقد أن أفضل فكرة هي إضافة وظيفة enumerateErrorFormat إلى المنسق json افتراضيًا؟ هل نحتاج إلى القلق بشكل منفصل بشأن ما إذا كان meta هو Error وليس مجرد object (أعتقد أن الناس سوف يشكون إذا لم نتعامل أيضًا مع هذه الحالة؟) ؟ أيضًا ، أنا أستخدم master ، لكن يبدو أن logger.error يناسبني مع الحل من خلال indexzero / @ 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'));

بعد إجراء مزيد من التحقيق ، يبدو أن مشكلة logger.error شرحتها أعلاه ليست سوى مشكلة عند استخدام أداة التسجيل الافتراضية. DABH ، لقد جربت الكود الخاص بك وهو يعمل معي ، لكن عندما أقوم

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

ثانيًا ، أوافق على إضافة enumerateErrorFormat إلى تنسيق json ؛ وربما تكون على حق أيضًا بشأن meta أيضًا.

أخيرًا ، أود أن أشير إلى مثال الكود الذي قدمه DABH يتسبب في عدم "طباعة جميلة"

{"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"}

كما ترى ، عند إخراج الخطأ بوظيفة إلى JSON ، لا تنشئ أحرف السطر الجديد \n سطورًا جديدة فعلية. هذا هو السلوك المتوقع عند أخذ كائن وتحويله إلى JSON ، ولكنه على الأرجح ليس السلوك الذي نريده بالفعل من المسجل ، على الأقل عند تسجيل الدخول إلى وحدة التحكم.

شكرًا لك على البحث في هذا @ DABH

لمعلوماتك ، هذا هو المكان الذي وصلت إليه بعد اللعب مع هذا قليلاً:

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;

سبب المشكلة هو هذا الخطأ: https://github.com/winstonjs/winston-transport/issues/31

لقد استخدمنا هذا النموذج بالتأكيد في winston2.x دون أي مشاكل. winston.err('some message', err); مع winston.error(err) أعلاه enumerateErrorFormat يصلح winston.error(err) لكن ليس حالة الاستخدام مع Er كمعامل ثان.

@ SamuelMaddox17

logger.log ({level: ____، message: err}) ؛

يعمل THX

حسنًا ، لقد اكتشفت شيئًا. تعليقي من 3 سبتمبر خاطئ. هذه ليست مشكلة مع المسجل الافتراضي. هذه مشكلة عندما تحدد level و / أو format . DABH هذا هو

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

إذا قمت بإزالة هذا:

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

واستبدله بهذا:

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

ثم تظهر مشكلة info.message === undefined . أعتقد أنه يجب أن يكون من الجيد تحديد المستوى والشكل لكل عملية نقل ؛ وأنا على يقين من أن هذا سمح به في Winston 2.0.

إليك نموذج التعليمات البرمجية الخاص بك مع تغيير الكود الخاص بي حتى تتمكن من تشغيل واختبار بسهولة:

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

نأمل أن يساعد هذا في الوصول إلى جذر المشكلة.

لقد أنشأت https://github.com/winstonjs/winston/pull/1527

هذا يغطي جميع الخيارات. لكن بعض الاختبارات تفشل لذا فقد أغلقتها في الوقت الحالي. من المتوقع حدوث حالات الفشل نظرًا للإصلاح ، لكنني لا أعتقد أنني في وضع يسمح لي بإجراء مكالمة لتعديل / حذف الاختبارات.

البناء الفاشل هنا https://travis-ci.org/winstonjs/winston/jobs/453012141 ومن الواضح لماذا تفشل الاختبارات الآن عند قراءة كود الاختبار:
https://github.com/winstonjs/winston/blob/c42ab7fdc51b88db180a7dd90c52ce04ddd4e054/test/logger.test.js#L668

أفكار؟

أعتقد أن المشكلة تكمن في هذا الخط
معلومات const = (msg && !(msg instanceof Error) && msg.message && msg) || {
الرسالة: msg
} ؛
يبدو أن إضافة فحص لمثيل الخطأ يؤدي إلى حل المشكلة كما يشير crowleym

لأي شخص لا يزال يتعامل مع هذا ، هذا هو منسق الحل الذي تمكنت من التوصل إليه (مقتطف من وحدة logger.js الخاصة بي):

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

لسبب ما ، لا يعمل logger.error(new Error("hello")) إلا إذا قمت بتعريف المنسق عالميًا في winston.createLogger 🤔 ثم تحصل على كائن Error info في المنسق.

إذا قمت بتعريف منسق لكل عملية نقل ، فيجب عليك استخدام logger.log({level: "error", message: new Error("FAILED")}) والتعامل مع كائن Error عبر info.message بدلاً من info للوصول إلى كائن Error.

أعتقد أن هناك خطأ عند تعيين حقل التنسيق في خيارات النقل؟

هذان فقط سنتان وما نجح معي ، أنا جديد على وينستون ولست متمرسًا في جافا سكريبت ، لذا لا تقتبس من أي شيء.

كان توجهي محاولة لإصلاح السبب الجذري. لكنها لا تحصل على الكثير من الجاذبية من أصحاب الريبو ...

نعم ، أنا أفهم ذلك. لقد قضيت الكثير من الوقت حرفيًا في اكتشاف هذا لأنني جديد على وينستون واعتقدت أنني ربما أستخدمه بشكل غير صحيح أو لم أستوعب المفاهيم الكامنة وراءه بشكل كافٍ حتى الآن.

لكن لحسن الحظ ، عثرت على بعض الخيوط (بما في ذلك هذه الخيوط) التي أظهرت لي خلاف ذلك. آمل أن يتم إصلاح هذا الأمر حتى لا أضطر إلى الاستمرار في استخدام الحل البديل.

قد يكون هذا بسبب wintson-transport ، راجع https://github.com/winstonjs/winston-transport/issues/31 للمشكلة و https://github.com/winstonjs/winston-transport/pull/ 34 للعلاقات العامة.

دائمًا ما يكون التسجيل المباشر لكائنات الخطأ في حالة من الفوضى بسبب خصائصها غير القابلة للعد. أنا شخصياً أعتبر هذه ممارسة سيئة ، لكن هناك عدد كافٍ من الناس في المجتمع مصرين عليها كشرط يجب علينا دعمها.

النظر في اعتماد https://github.com/winstonjs/logform/pull/59 كتنسيق لدعم مثل هذه السلوكيات. على الجانب الإيجابي ، فإنه يشمل جميع حالات الحافة الشائعة جدًا عند معالجة الأخطاء كرسائل سجل. على الجانب السلبي ، سيكون هناك تنسيق آخر سيحتاج الأشخاص إلى الاشتراك فيه (على غرار .splat() )

indexzero أميل إلى الموافقة ولكن تسجيل الخطأ المباشر يمكن أن يكون مفيدًا عند اقترانه بتنسيق تسجيل مخصص / printf إذا كانت هناك حاجة لعرض أنواع Error مختلف ، ولا أتذكر أن Winston 2.x يحاول ذلك محاربة هذه الممارسة حيث تم السماح لها بالخروج من منطقة الجزاء.

لذا فإن الحل المقترح لـ enumerateErrorFormat يعمل ولكنه لا يدعم التنسيق logger.error('some message', err) . لأن لا info.message أو info هو instanceof Error . أريد أيضًا أن أشير إلى مشكلة أخرى مع هذا الحل. أقوم حاليًا بتسجيل خطأ تم إرجاعه من 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  
    }

إذا استخدمنا Object.assign ببساطة فقم بتبريد المكدس وسيتم تعيين الرسالة! ولكن ، سيتم أيضًا تسجيل أي معلومات أخرى كانت جزءًا من الخطأ. قد يكون هذا خطيرًا للغاية في الحالات التي تحتوي فيها الأخطاء على بيانات حساسة مثل Authorization Headers (والتي يتم تضمينها في هذه الحالة كجزء من كائن الخطأ).

ولكن بعد ذلك قد تقول. هذا ليس خطأ Winston ، وليس خطأ Winston أن الوكيل الفائق يضيف هذه البيانات إلى الخطأ. أنا موافق! ومع ذلك ، نظرًا لأنه يتم تخزين كل شيء بشكل مسطح على الكائن info ، يصبح من الصعب جدًا الاحتفاظ بالمعلومات القديمة وعدم تجاوز الباقي.

سيكون من الرائع تقريبًا ، عند استخدام logger. Error. افترضت أن المعلمة الثانية كانت وخطأ ووضعها على الكائن info كـ info.error ثم إذا قمت بتسجيل الدخول بالطريقة الأخرى ، فستكون الواجهة { level: "error", error: YOUR ERROR OBJECT}

أنا حقًا أتفرج هنا ولكن الواجهة الجديدة كانت بالتأكيد محبطة بعض الشيء (كل شيء على المعلومات).

فقط لتوضيح النقطة التي كنت أذكرها ، افترض أنك logger.error( e ) حيث يمثل e خطأ من النوع.

ثم في التعليمات البرمجية الخاصة بك ، قمت بتكوين Winston على النحو التالي:

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

يتم دفع الطابع الزمني على كائن الخطأ 😱. لا تجعل حقا معنى؟ فكر في ذلك .. كائن الخطأ الذي أرسلته هو الحصول على خاصية جديدة ديناميكيًا .. طابع زمني.

أعتقد أن أفضل حل شامل لهذه المشكلة هو دعم بناء الجملة التالي

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

ثم داخليًا يمكنك إنشاء منسق خطأ يبحث عن حقل الخطأ!

تحديث عن هؤلاء الناس:

على أمل أن يتم خياطة هذا وشحنه في اليومين المقبلين إنه الإصدار التالي إلى آخر إصدار في أداة تعقب [email protected]

مرحبًا يا رفاق - يرجى مراجعة https://github.com/winstonjs/winston/pull/1562. كان هذا هو العنصر الأخير في قائمة التحقق من الإصدار 3.2.0 لذا بمجرد حياكة العلاقات العامة ، سنتمكن من تحريره

حتى يتم نشر الإصلاح ، أستخدم الحل البديل التالي:

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

أنا على أحدث إصدار من winston (3.2.1) وما زلت أحصل على undefined عند تمرير خطأ إلى logger.error

ezze لديه حل رائع لكن تتبع المكدس تم إجباره على سطر جديد. هذه نسخة معدلة قليلاً تضعها في سطر واحد (لذلك تم التقاطها بواسطة grep بسيط من ملفات السجل)

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

بإخراج <Error message> - trace: <stack trace>

إذا كانت هناك طريقة أسهل للقيام بذلك باستخدام أحدث إصدار من winston ، فيرجى إبلاغي بذلكindexzero. أنا جديد في المكتبة وكنت أتابع المستندات

لقد رأيت للتو الرابط الذي نشرته على العلاقات العامة. هل هذا يعني أن تمرير خطأ إلى logger.error يتطلب سلسلة رسالة ، ثم الخطأ؟

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 انتهى الأمر بوجود مشكلتين تحدث عنهما هذا الخيط. الأول هو ما طرحه الملصق الأصلي ، والثاني هو المشكلة التي طرحتها وهي نفس مشكلتك. أعتقد أنهم أصلحوا مشكلة الملصقات الأصلية فقط. كنت أقصد التحقق أكثر للتأكد من ثم فتح مشكلة جديدة إذا كان الأمر كذلك. لم يكن لدي الوقت الكافي للغوص بشكل أعمق للأسف في غضون ذلك ، إذا كنت تستخدم logger.log({level: 'error', message: err}); حيث يكون err كائن خطأ ، فسيعمل.

لا تزال تواجه هذه المشكلة ، ضاع الكثير من الوقت لمعرفة ذلك ، الحل من @ the-vampiire يعمل بشكل جيد.

لماذا هذه التذكرة مغلقة؟

يعد تجاوز logger.error هو الحل الأفضل حتى الآن لأنه لا يضيف خاصية طابع زمني إلى كائن خطأ يتم تمريره كوسيطة واحدة للخطأ (). يتوقع معظم الناس على الأرجح أن كائنات الخطأ غير قابلة للتغيير. إذا لم تقم أيضًا بتجاوز logger.info وكل طريقة أخرى ذات صلة بالمستوى ، فمن المفاجئ ألا تعمل الأشياء كما هو متوقع. مرة أخرى ، ما لم ترغب في تعديل الكائن الخاص بك ، بغض النظر عن نوعه ، فلا ترسله مباشرةً إلى طريقة Winston logger.

الميزة مدعومة منذ [email protected]

استخدام المثال:

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

سم مكعب @ HRK44 @ مصاص دماء

أنا أيضا ما زلت أواجه المشكلة.
عندما أتصل بالخطأ مثل logger.error(error); أحصل على undefined .
فقط إذا أسميته مثل logger.error('Something went wrong', error) أحصل على الخطأ الكامل ويمكنني تحليله.

أنا أيضا ما زلت أواجه المشكلة.
عندما أتصل بالخطأ مثل logger.error(error); أحصل على undefined .
فقط إذا أسميته مثل logger.error('Something went wrong', error) أحصل على الخطأ الكامل ويمكنني تحليله.
هل أضفت هذا؟

format.errors({ stack: true })

نعم ، لا تزال نفس المشكلة. أحاول إعادة إنتاجه في جوهره.

@ OBrown92 لقد واجهت نفس المشكلة اليوم. أستطيع أن أؤكد أنه يعمل ، إذا تم تطبيق format.errors({ stack: true }) على المسجل ، وليس على النقل. في هذه الحالة من الممكن استخدام كل من logger.error(error); و logger.error('Something went wrong', error) . ومع ذلك ، هناك مشكلة ، عندما أحاول تطبيق format.errors({ stack: true }) للنقل المختار. في هذه الحالة ، أحصل على undefined مقابل logger.error(error); ، لكن logger.error('Something went wrong', error) يعمل بشكل صحيح.

لست متأكدًا مما إذا كان هذا السلوك متوقعًا أم أنه خطأ ، لكنني قضيت الكثير من الوقت للعثور على السبب ، لذا يرجى إصلاحه أو ذكر ذلك في مكان ما في وثائقك. سيكون حقا مفيدا.

على أي حال ، أنا ممتن جدًا لعملكم في هذا المشروع الرائع.

كنت أواجه نفس المشكلة لذا كتبت هذه الحزمة ، utils-deep-clone . تحقق من ذلك.

format.errors is not a function ... حسنًا هذه مفاجأة.

تضمين التغريدة
هل قمت باستيراد التنسيقات من حزمة وينستون؟
استخدام المثال:
const { format } = require('winston')
أو
const winston = require('winston'); const { format } = winston;

aybhalala yepp ، على الرغم من أنه لا يهم أن مكدس الأخطاء يتم تمريره إلى printf بدونه.

تحديث: نظرًا لأنه لا تزال هناك مشكلات في هذا الأمر ، فقد كنت أفعل ما يلي لفترة من الوقت وكان يعمل بشكل رائع

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

^^ هذا هو الاستخدام المتوقع. نظرًا لأن البشر البحتين لن يكتشفوا ذلك أبدًا ، فيجب توثيقه.

بما أن البشر البحتين لن يكتشفوا ذلك أبدًا ، يجب توثيقه

😂 +1 لهذا الرفيق

أستخدم winston مع وسيلة نقل تابعة لجهة خارجية ( @google-cloud/logging-winston ) لذلك لدي سيطرة أقل على بناء الجملة. بالإضافة إلى أنني أجد هذا أكثر سهولة:

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

عند تسجيل الدخول إلى وحدة التحكم ، أقوم بربط المكدس بالرسالة. لكن النتيجة شيء من هذا القبيل:

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

نظرًا لأن winston تسلسل meta.message في الرسالة الأصلية ، فهناك رسالة غريبة andsomething والرسالة المكررة التي تتم طباعتها أيضًا في المكدس. هذا موصوف في # 1660.

يبدو أن # 1664 يحاول إصلاح هذا. في غضون ذلك ، كتبت منسقًا يقوم "بالتراجع" عن هذا التسلسل: https://github.com/winstonjs/winston/issues/1660#issuecomment -512226578

تضمين التغريدة

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

هذا لا يعمل لأنه ، على عكس console.log() ، فإن logger.<level>(message) winston يأخذ معلمة واحدة فقط تسمى message. معلمة الرسالة هذه هي إما كائن أو سلسلة (

لاحظ أنه يمكنك أيضًا استخدام logger.log({level: <level>, message: <message>}) . لمعرفة المزيد حول هاتين الوظيفتين ، أوصي بقراءة هذا الجزء من المستندات: Winston Docs on Log Levels . تأكد من قراءة استخدام مستويات التسجيل

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

لا أستطيع أن أقول بشكل قاطع لماذا لا ينتج هذا المكدس ، لكنني أعرف أن هذه ليست مشكلة مع Winston. إذا جربت console.log(`Caught error: ${e}`) فإنه لا يشمل أيضًا المكدس. لم أعمل مع القوالب الحرفية كثيرًا ، لذا إما أن القوالب الحرفية لا تعمل بشكل جيد مع الكائنات ، أو أن console.log الخاص بجافا سكريبت يتعرف على الكائن ككائن خطأ وبالتالي ينتج فقط خاصية الرسالة. هذا هو أفضل تخميني.

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

هذا واحد يصل إلى قلب موضوع هذا الخطأ. أولاً يجب أن تفهم بعض التفاصيل الفنية حول جافا سكريبت. لاحظ أنك إذا جربت console.log(`Caught error: ${JSON.stringify(e)}`) فستحصل أيضًا على نفس الناتج Caught error: {} . كما أوضح indexzero :

خصائص message و stack على Error غير قابلة للعد مما يؤدي إلى إخراج JSON.stringify لشيء لا يتوقعه المرء.

بشكل أساسي ، نظرًا لأن خصائص message و stack غير قابلة للعد ، فإن JSON.stringify يتخطى تلك الخصائص وهو ما ينتهي بك المطاف بكائن فارغ {} . لفهم التعداد بشكل أفضل ، أوصي بقراءة هذا التعداد وملكية العقارات .

لحسن الحظ ، نظرًا للطريقة التي تم بها تصميم Winston 3.0 (الدعائم لفريق Winston) ، لدينا حل بديل لهذا الذي قدمهindexzero . سأساعد في شرح ذلك. تقوم أولاً بإنشاء هذه الوظيفة:

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

من تدفقات المستندات info.level ، و info.message . هذه الخاصية info.message هي كائن الخطأ إذا كان هذا هو كل ما قمت بتمريره. لذلك نقوم بإنشاء كائن جديد حيث message.stack و message.message (فكر فيه على أنه Error.stack و Error.message ) الآن قابلان للعد ، ونقوم بتضمين أي خصائص أخرى قد تكون مرتبطة أيضًا بكائن الخطأ هذا.

بعد ذلك ، ستنشئ هذا المسجل الذي يستخدم الوظيفة enumerateErrorFormat() أعلاه:

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

سيستغرق هذا أي message تمرره وتحقق مما إذا كان كائن خطأ. إذا كان الأمر كذلك ، فسيتم إصلاح مشكلة التعداد. ثم تقوم بتمرير الرسالة إلى format.json والتي ستعمل على توتير أي كائن (خطأ أم لا). إذا لم يكن كائنًا ، فهو عبارة عن سلسلة و format.json effectivley لا يفعل شيئًا ، وأنت في المنزل مجانًا!

ومع ذلك ، سيكون من الجيد ألا نضطر إلى إنشاء هذا enumerateErrorFormat لأن كائنات الخطأ يتم تسجيلها بشكل شائع. كما أفهمها ، يعمل فريق Winston على إصلاح سيتم إصداره في إصدار لاحق.

بعض الملاحظات النهائية. يعمل هذا فقط إذا كنت تستخدم logger.log({level: <level>, message: <message>}) حيث تكون الرسالة هي كائن الخطأ. مثال:

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

هناك خطأ آخر في Winston حيث لا يعمل هذا الرمز ، كما أوضحت في رسالتي الأخرى أعلاه:

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

لسبب ما ، تكون الخاصية info.message غير محددة عندما نستخدم logger.error(err) . نأمل أن يتمكنindexzero من معرفة هذا.

شرح جيد جدًا ، أريد فقط إضافة ذلك باستخدام logger.error( خطأ محقق: $ {e} ); تفقد المكدس بسبب الطريقة التي تعمل بها السلسلة الحرفية في جافا سكريبت ، `${e}` هو نفسه e.toString() ، لذا فإن طباعة رسالة الخطأ فقط هو السلوك المتوقع.

وما زالت هذه المسألة؟ ما زلت أواجه مشكلات مع هذا:

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
}

وما زالت هذه المسألة؟ ما زلت أواجه مشكلات مع هذا:

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
}

نفس المشكلة هنا...

بعد الاطلاع على الكود المصدري ، يمكنني معرفة المعلمات التي يقبلها:

واجهة LeveledLogMethod {
(message: string ، callback: LogCallback): المسجل ؛
(message: string ، meta: any ، callback: LogCallback): المسجل ؛
(message: string، ... meta: any []): Logger؛
(infoObject: object): المسجل ؛
}

لذلك إذا قمت بتمرير كائن الخطأ كمعلمة أولى ، فسيأخذ رسالة الخطأ فقط لأنه يفهم السلاسل فقط ، وإذا مررت الخطأ في المعلمة الثانية ، فيمكنك الوصول إلى تتبع المكدس في info.stack

راجع للشغل لم أجد هذا في أي مكان في المستندات

لقد وجدت حلين ، الأول هو استخدام format.errors ، مذكوران في الأصلي ، ثم إنشاء messageFormatter باستخدام format.printf وإضافة حقل stack مستخرجًا بشروط info (سيضيف ذلك format.errors({ stack: true}) ).

الحل الآخر الذي فضلته هو اختراق أجهزة تسجيل مستوى Winston:

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

يبدو أنه بهذه الطريقة يمكنني الحصول على تسجيل خطأ مشابه لـ console.log

يمكنني التحقق من دقة تعليق format.errors على المسجل _parent_. عندما أفعل شيئًا كهذا:

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

يتم التعامل مع الكائن Error كما لو كانوا سلسلة. ومع ذلك ، إذا فعلت شيئًا كهذا:

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

معالجة كائن Error يعمل بشكل صحيح.

يبدو لي أن الخطأ هنا هو أنه لا ينبغي أن يكون هناك اختلاف في السلوك.

إذن هذا لم يتم إصلاحه بعد عام واحد؟ هل يجب علي اختراق كود winston logger لجعله يعمل؟

نعم ، لقد أصابني ذلك بما يكفي من الصداع لدرجة أن وينستون بدأ يبدو وكأنه مشكلة أكثر مما كانت تستحقه بالنسبة لحالة الاستخدام البسيطة نسبيًا ... انتهى بي المطاف فقط بكتابة فصلي الصغير الخاص بالمسجل ، وأنصح الآخرين بفعل الشيء نفسه ما لم يكن وينستون يوفر شيئًا تحتاجه حقًا.

يا رفاق ، حقا؟ هذا امر محبط...

أنا لست ملتزمًا ولكن من المحتمل أن أكون محقًا في القول إن هذا لن يتم "إصلاحه" لأنه لم يتم كسره. ونستون يستحق الاستخدام. تحتاج فقط إلى تكوينه - أفضل نصيحة بالنسبة لي موجودة أعلاه على https://github.com/winstonjs/winston/issues/1338#issuecomment -506354691

ليس بعد ؟

تحديث: نظرًا لأنه لا تزال هناك مشكلات في هذا الأمر ، فقد كنت أفعل ما يلي لفترة من الوقت وكان يعمل بشكل رائع

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

من أين يأتي الأثر؟

لا حقًا ، هذا غير مقبول لمكتبة التسجيل.
يجب على Maintainer ببساطة وضع مثال مميز جيدًا في المستندات حيث يتم عرض كيفية تسجيل خطأ ، حتى تحديد تنسيق printf مخصص وتنسيق غير json وحيث يمكنك تسجيل خطأ بشيء مثل logger.error ("شيء ما" ، خطأ) ومسجل . Error (Error)
بدا ونستون عظيماً ولكن هذه القضية غير مقبولة بشكل لا يصدق

هذا هو رأيي في كيفية تسجيل الأخطاء باستخدام Winston. إنه ليس فريدًا ، فالكثير من اللمحات أعلاه لديهم حلول عملية تستند إلى نفس المفاهيم أيضًا.

خلفية
أنا أستخدم @jsdevtools/ono لالتفاف أنواع الكائنات العشوائية إلى أخطاء مخصصة ، ولكن بغض النظر ، يبدو أن هذا الحل لا يزال يعمل بشكل جيد مع أخطاء العقدة الأصلية (مثل أخطاء fs eperm) ، وفئات الخطأ المخصصة.

تفسير
في الأساس ، أنا أعتمد على format.errors({stack:true}) و format.metadata() . كما ذكر من خلال https://github.com/winstonjs/winston/issues/1338#issuecomment -532327143 ، يجب أن يكون هذا في المنسق الأصل .

تساعد البيانات الوصفية في تحويل كل الخصائص المخصصة لكائن الخطأ إلى info.metadata .

أردت طباعة 3 أنواع من المعلومات: رسالة الخطأ ، وتتبع المكدس ، وخصائص كائن الخطأ. كانت رسالة الخطأ نصًا عاديًا بالفعل. لقد طبعت بشكل رائع المكدس info.metadata.stack باستخدام الوحدة النمطية pretty-error . بالنسبة لخصائص كائن الخطأ ، لم أرغب في ظهور تتبع المكدس مرة أخرى ، لذلك قمت باستنساخ الكائن ، وحذف خاصية تتبع المكدس. ثم قمت بعد ذلك بطباعة كائن الخطأ باستخدام fast-safe-stringify ، وهي نفس وحدة Stringify التي يعتمد عليها Winston.

    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

ملاحظة: أجد أيضًا الحل المذكور في https://github.com/winstonjs/winston/issues/1338#issuecomment -506354691 ليكون بديلاً جيدًا. أي باستخدام logger.warn("Oh no", { error: new Error() }) ، ثم الرجوع إلى info.error في المنسق المخصص.

tiagonapoli لقد نجح الحل الذي format.errors على المسجل الأصلي لي:

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

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

من المؤلم إلى حد ما تكوين هذا المسجل ... ألا يمكن أن يتصرف فقط مثل console.log خارج الصندوق؟

@ will093 نفسه هنا. كان على هذه المسألة مرة أخرى ولا تحصل عليه لماذا بلادي console.log جميلة ونظيفة وشكلها ونستون هو القرف.

بلدي 2 My

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

مثال على logger.error("Error during schema stitching", e);

image

يبدو أن استخدام @ will093 لإضافته إلى الوالد فقط هو أسهل طريقة لدعم أخطاء التسجيل مباشرة واستمرار تسجيل الرسائل - إليك مثال كامل على الحد الأدنى من الإعداد مع الطوابع الزمنية:

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

يعمل مع مكدس عند استدعائه مع وجود خطأ مثل: logger.error(error) ، يعمل مع سلسلة عند استدعائها مثل logger.error('a regular message') ، يبدو هكذا في سجلاتي:

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

هذا لا يحاول حل مشكلة عدم توافق Winston logger.error('message here', error) مع / console.log ، وهو ما يبدو أن حل tiagonapoli الأكثر مشاركة يفعله.

أيضًا ، إذا كنت تحب سجلات json ، فيمكنك إسقاط logFormatter هنا واستخدام winston.format.json() في مكانها ، والتي ستظل تتضمن المكدس - لكنها ليست جميلة.

تحديث: نظرًا لأنه لا تزال هناك مشكلات في هذا الأمر ، فقد كنت أفعل ما يلي لفترة من الوقت وكان يعمل بشكل رائع

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

أين هو تعريف trace ()؟

تحديث: نظرًا لأنه لا تزال هناك مشكلات في هذا الأمر ، فقد كنت أفعل ما يلي لفترة من الوقت وكان يعمل بشكل رائع

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

من أين يأتي الأثر؟

أي إجابة على هذا؟

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات