如何使App Engine标准上的Node JS日志与Python一样好

时间:2018-07-24 20:31:43

标签: node.js google-app-engine

我刚刚开始在App Engine标准上运行Nodejs,除了标准日志输出非常糟糕之外,其他所有方面都很棒。在Python(按应用引擎标准)中,您会得到如下所示的非常漂亮的日志:

enter image description here

当有多行时,输出在父级下很好地级联,日志记录级别由左侧的彩色图标等表示。

在Node中,日志如下所示:

enter image description here

如您所见,所有内容都有自己的一行,它们不是树结构,谁知道这些是信息块,警告块或错误块。

我已经尝试将Watson和Bunyan与跟踪库一起使用,如本SO帖子所示:App Engine Node.js: how to link app logs and requests logs

但是它似乎不起作用。对于Google来说,默认情况下像在Python或Java上一样使此功能正常工作非常棒,除非能获得一个有效的演示,否则,我也尝试过将节点lib记录下来,并将其作为google API的一部分。收集无济于事。

有人能给我指出一个如何修复日志的示例,以便我可以匹配Python中已经存在的应用引擎标准吗?

感谢您的帮助!

更新-仍然无法运行

因此,在部署了hello-world示例并将一个记录器添加到应用程序的get方法之后,现在看起来像这样:

app.get('/', (req, res) => {
    var data_block = {
    "glossary": {
        "title": "example glossary",
        "GlossDiv": {
            "title": "S",
            "GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
                    "SortAs": "SGML",
                    "GlossTerm": "Standard Generalized Markup Language",
                    "Acronym": "SGML",
                    "Abbrev": "ISO 8879:1986",
                    "GlossDef": {
                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
                        "GlossSeeAlso": ["GML", "XML"]
                    },
                    "GlossSee": "markup"
                }
            }
        }
    }
}

    console.log("This is a really big log request that would normally not work very well:", data_block)
  res.status(200).send('Hello, world!').end();
});

输出看起来像这样:

enter image description here

您会发现日志与Python日志不同,知道我在做什么错吗?

3 个答案:

答案 0 :(得分:0)

您可以使用Bunyan client library将日志流式传输到Stackdriver。这是一个基于文档和您提供的代码示例的示例。

= vlookup( 'Reg Hrs.','SSS Contribution Table'!A$2:D$32,3,TRUE)

这会将整个JSON块记录为一条单独的消息,但是,如果您要这样做的话,文档还介绍了如何在请求日志下嵌套应用程序日志。

答案 1 :(得分:0)

您可以使用console.log(JSON.stringify(event));而不是console.log(event);来修复JavaScript对象。

答案 2 :(得分:0)

如果您来自App Engine标准Pyton(2),则记录体验非常令人沮丧。 Google Cloud Services上的文档杂乱无章,零散且经常过时。我没有找到有关在Google App Engine node.js标准环境中进行高性能日志记录的最佳做法的示例。那么如何获得体面的汇总日志记录呢?

我认为问题的一部分是这样的事实:对于Python,Google基础架构会根据每个请求(某种程度上是对云功能的早期实现)来调用您的代码,而使用node.js则需要较长的运行时间,通过localhost上的HTTP / TCP / IP实现基础架构。因此,GAE基础结构无法轻松确定哪个输出属于哪个请求。

Google建议:

要写入日志条目,建议您集成Bunyan或 带云记录功能的Winston插件。 (source

但是对于由node.js / Express提供的异步/单进程服务器方法,使用进程内Cloud Logging客户端通常是一个有问题的方法。我的测试表明,每个记录器调用都会向Stackdriver / Clound Logging发出一个出站HTTP请求。批处理似乎是可能的,但是在失败的情况下很难实施。

登录到stdout / stderr并在一个单独的过程(由App Engine基础结构提供)中将日志从那里传输到日志查看器提供了更好的去噪功能。而且它应该便宜一些,因为您显然不用为在那里花费的周期付费(?)。如Google所述,应该是可能的:

您可以将简单的文本字符串发送到stdout和stderr。琴弦 将在日志查看器中显示为消息,[…]如果要过滤 这些日志字符串按严重性级别在日志查看器中,您需要格式化 它们作为结构化数据。有关更多信息,请参见结构化日志记录。 如果您要将应用程序日志中的条目与请求日志相关联, 您的结构化应用程序日志条目需要包含请求的跟踪 标识符。 […]您可以在App Engine中使用相同的技术 应用。 (source

要获得结构化的日志输出(基本上是JSON行),例如Pino是正确的方法。您需要进行一些设置,以将Pino字段名称映射到Stackdriver / Google Cloud Logging使用的名称。

在Google字段名称中,有趣的重复词有些分散:

必须完成一些映射。为Google / Stackdriver设定Pino如下:

// https://github.com/pinojs/pino/blob/master/docs/api.md#level-string
// https://cloud.google.com/logging/docs/agent/configuration#special-fields
function _levelToSeverity (level) {
  if (level < 30) { return 'DEBUG' }
  if (level <= 30) { return 'INFO' }
  if (level <= 39) { return 'NOTICE' }
  if (level <= 49) { return 'WARNING' }
  if (level <= 59) { return 'ERROR' }
  if (level <= 69) { return 'CRITICAL' } // also fatal
  if (level <= 79) { return 'ALERT' }
  if (level <= 99) { return 'EMERGENCY' }
  return 'DEFAULT'
}
const logger = pino({
  // Default Level: `info` or taken from the environment Variable LOG_LEVEL
  // levels below this get supressed so set this to `debug` or `trace`
  // if you want to be able to reveal details in the App Engine logs
  level: process.env.LOG_LEVEL || 'info',
  formatters: { // Here the Mapping is happening
    level (label, number) {
      return { level: number, severity: _levelToSeverity(number) }
    }
  },
  messageKey: 'message' // AppEngine / StackDriver want's this key
  // Enable Pretty Printing on the development Server
  prettyPrint: (process.env.NODE_ENV || 'development') === 'development',
})

这使我们能够以Google日志查看器可以消化的格式写出日志,并允许我们“向下钻取”日志。

下一步是启用每个HTTP请求的日志记录。 pino-http使用上面定义的记录器,并且可以连接到Google设备以使用Google的请求ID:

const httpLogger = require('pino-http')({
  logger: logger,
  genReqId: function (req) {
    // get a Google rrovided request ID to correlate entries
    return (
      req.headers['x-appengine-request-log-id'] ||
      req.headers['x-cloud-trace-context'] ||
      req.id
    )
  },
  useLevel: 'debug'
})

这会将日志实例添加到每个Express-Request中,并且可以在路由哈希器中以req.log.info('bar')等使用。您还可以将结构化字段添加到req.log.info({user: 'mike', action: 'create'}, 'foo')等日志中。这些字段可以在日志查看器中查询。

我通常不使用pino-http,因为它会增加很多混乱。相反,我自己生成了requestid。

如果您要将应用程序日志中的条目与请求日志相关联, 您的结构化应用程序日志条目需要包含请求的跟踪 标识符。您可以从 X-Cloud-Trace-Context请求标头。在您的结构化日志条目中, 将ID写入名为logging.googleapis.com/trace的字段。欲了解更多 有关X-Cloud-Trace-Context标头的信息 (X-Cloud-Trace-Context)。 (source

这可以通过Express-中间件完成:

const gaeTraceMiddleware = (req, res, next) => {
  // Add log correlation to nest all log messages beneath request log in Log Viewer.
  const traceHeader = req.header('X-Cloud-Trace-Context')
  if (traceHeader && process.env.GOOGLE_CLOUD_PROJECT) {
    const [trace] = traceHeader.split('/')
    req.log = res.log = req.log.child({
      'logging.googleapis.com/trace': `projects/${process.env.GOOGLE_CLOUD_PROJECT}/traces/${trace}`,
      'appengine.googleapis.com/request_id': req.header('x-appengine-request-log-id')
    })
  }
  next()
}

现在,连接到gaeTraceMiddleware的Express实例可以使用res.log为Google Log Viewer输出内容。在那里,它几乎与Pytohn日志一样整齐:

const app = express()
app.use(httpLogger)
app.use(gaeTraceMiddleware)

// App Engine Instance Start
app.get('/_ah/warmup', (req, res) => {
  res.log.info('Warmup')
  res.send('HOT')
})

但是每次请求聚合仍然会丢失您的信息,但是日志记录的体验会变得更好。

相关问题