Winston: 请求:默认元数据中的函数

创建于 2019-03-29  ·  3评论  ·  资料来源: winstonjs/winston

这里的潜在问题有些复杂,所以我将在这里总结一下我们的要求——我们希望看到defaultMetadata能够通过提供一个函数作为字段来包含动态生成的值。 例如:

const logger = createLogger({
  defaultMeta: {
    // Function to be evaluated every time a log is written.
    timeWritten: () => { return `Date.now()`; }
  },
  // ...
});

完整的理由见下文。

请告诉我们您的环境:

  • _ winston版本?_

    • [ ] winston@2

    • [x] winston@3

  • _ node -v输出:_ v10.15.2
  • _操作系统?_ macOS(无关)
  • _语言?_ ES7(无关)

问题是什么?

作为 Winston 的 Stackdriver Logging Transport 的作者,我们希望支持日志请求关联。 async_hooks模块使我们能够存储特定于请求的信息,例如请求 ID。 但是,我们没有办法在Transport端可靠地检索它。

这个问题最好用例子来描述。 当快速连续发出约 100 个请求时,以下代码会产生此输出

const ah = require('async_hooks');
const express = require('express');
const { createLogger } = require('winston');
const TransportStream = require('winston-transport');

// Activate async hooks.
let requestIdHighWater = 0;
const requestIdTable = {};
ah.createHook({
  init: (child, resource, parent) => {
    requestIdTable[child] = requestIdTable[parent];
  }
}).enable();

// OUR CODE

class MyTransport extends TransportStream {
  log(info, next) {
    // From the transport, the request ID is not reliable.
    const actualRequestId = requestIdTable[ah.executionAsyncId()];
    console.log(`Expected: ${info.expectedRequestId}, Actual: ${actualRequestId}`);
    setImmediate(next);
  }
}

// OUR USER'S CODE

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

const app = express();

app.get('/', async (req, res) => {
  // Store a request ID.
  const requestId = requestIdHighWater++;
  requestIdTable[ah.executionAsyncId()] = requestId;
  logger.info('hello', { expectedRequestId: requestId });
  res.sendStatus(200);
});

app.listen(3000);

请注意“预期”和“实际”请求 ID 中的不匹配。 如果我们是MyTransport的作者,我们将无法通过查询当前执行 ID(“实际”)来准确获取正确的请求 ID。 相反,我们必须依靠用户传递正确的请求 ID 作为元数据(“预期”)。 这是因为 Winston 3 在 Transport 延迟调用其log回调时(通过readable-stream )批处理日志。

问题是我们不想依赖用户为我们传递请求 ID; 我们希望它自动发生。 毕竟,用户可能只想写

app.get('/', async (req, res) => {
  logger.info('hello');
  res.sendStatus(200);
});

让我们休息。

你期望会发生什么?

预期的和实际的请求 ID(来自链接的输出)匹配。

其他信息

修复代码以使实际请求 ID 匹配是不可行的,因此解决此问题的方法是允许用户将函数指定为defaultMetadata上的字段,这些字段在调用记录器时被调用。 这样,代码将更改为this ,这允许用户在不知道请求 ID 的情况下编写他们的代码,但需要注意的是,他们提供了一个 thunk 作为defaultMetadata字段(也许我们的模块会提供他们使用)。

最有用的评论

我找到了一种解决方法,可以在实施建议的解决方案之前使用 - 使用 getter:

const logger = createLogger({
  defaultMeta: {
    get timeWritten () { return `Date.now()`; }
  },
  // ...
});

UPD:修复了@mooski 提到的错误

所有3条评论

所以解决这个问题的方法是允许用户将函数指定为 defaultMetadata 上的字段

我在想这可以实现类似于 redux thunks 。 而不是在defaultMetadata中具有函数值属性,而是认为defaultMetadata本身可以是一个可选的函数。 像 thunk 一样,在这种情况下, winston将使用通过调用defaultMetadata返回的值。 换句话说:

const logger = createLogger({
  defaultMeta: () => {
    // Function to be evaluated every time a log is written.
    return {
       timeWritten: Date.now();
       // ..
    }
  }
  // ...
});

我找到了一种解决方法,可以在实施建议的解决方案之前使用 - 使用 getter:

const logger = createLogger({
  defaultMeta: {
    get timeWritten () { return `Date.now()`; }
  },
  // ...
});

UPD:修复了@mooski 提到的错误

〜很棒的解决方案@Alexsey - 对于使用此代码的任何人,请注意timeWritten之后的冒号不应该在那里。〜

更新:由@Alexsey 修复

此页面是否有帮助?
0 / 5 - 0 等级