我刚刚开始在App Engine标准上运行Nodejs,除了标准日志输出非常糟糕之外,其他所有方面都很棒。在Python(按应用引擎标准)中,您会得到如下所示的非常漂亮的日志:
当有多行时,输出在父级下很好地级联,日志记录级别由左侧的彩色图标等表示。
在Node中,日志如下所示:
如您所见,所有内容都有自己的一行,它们不是树结构,谁知道这些是信息块,警告块或错误块。
我已经尝试将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();
});
输出看起来像这样:
您会发现日志与Python日志不同,知道我在做什么错吗?
答案 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')
})
但是每次请求聚合仍然会丢失您的信息,但是日志记录的体验会变得更好。