node.js - How to optionally set Winston transports when logging to Stackdriver from GKE
问题描述
I have a Node.js app which runs in a Docker container in Google Kubernetes Engine. I have set up a logging class which uses Winston
(v3.2.1) with two transports defined; one to log to the console and one to log to Stackdriver (using @google-cloud/logging-winston
(v3.0.0)).
With both transports defined, all is good and I can see the logs in Stackdriver. The console logs go to projects/[project-id]/logs/stdout
and the Stackdriver logs go to projects/[project-id]/logs/winston_log
.
However, I want to configure the logger so that when debugging locally, logs are only sent to the console and when running in GKE, logs are only sent to Stackdriver, as follows:
// Configure console logger
private readonly consoleLogger = new winston.transports.Console({
format: combine(
colorize(),
simple(),
printf(context => {
return `[${context.level}]${context.message}`;
}),
),
});
// Configure Stackdriver logger
private readonly stackdriverLogger = new LoggingWinston({
serviceContext: {
service: this.serviceName,
},
});
// Create Winston logger
private readonly logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: json(),
defaultMeta: {
service: this.serviceName,
},
// This line does not work:
transports: [process.env.NODE_ENV === 'development' ? this.consoleLogger : this.stackdriverLogger],
});
The aim here is that if the NODE_ENV
is development
, use the console logger, otherwise use the Stackdriver logger. However, when I deploy this to GKE, I see the following errors in the Stackdriver console log (and nothing in projects/[project-id]/logs/winston_log
):
[winston] Attempt to write logs with no transports { // Logged message }
When I run this code locally on my dev machine with NODE_ENV=development
, I see the logs in my local console and if I set NODE_ENV=production
I see the logs in Stackdriver.
If I remove the ternary operator and have both transports defined and deploy to GKE, I do not see the above error and logging works correctly to both transports:
transports: [this.consoleLogger, this.stackdriverLogger],
Can anyone help me to configure this correctly?
EDIT
Added the full Logger.ts
file for context:
import { LoggerService } from '@nestjs/common';
import * as winston from 'winston';
const { colorize, combine, json, printf, simple } = winston.format;
import { LoggingWinston } from '@google-cloud/logging-winston';
import cls from 'cls-hooked';
import { ConfigManager } from '../config';
import { TraceId } from '../middleware/traceId/constants';
export class Logger implements LoggerService {
private readonly serviceName: string = process.env.SERVICE_NAME;
private readonly consoleLogger = new winston.transports.Console({
format: combine(
colorise(),
simple(),
printf(context => {
return `[${context.level}]${context.message}`;
}),
),
});
private stackdriverLogger = new LoggingWinston({
serviceContext: {
service: this.serviceName,
},
});
private readonly logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: json(),
defaultMeta: {
service: this.serviceName,
},
transports: [process.env.NODE_ENV === 'development' ? this.consoleLogger : this.stackdriverLogger]
});
constructor(private readonly context?: string) {}
public verbose(message: string, context?: string) {
const log = this.buildLog(message, context);
this.logger.verbose(log.message, log.metadata);
}
public debug(message: string, context?: string) {
const log = this.buildLog(message, context);
this.logger.debug(log.message, log.metadata);
}
public log(message: string, context?: string) {
const log = this.buildLog(message, context);
this.logger.info(log.message, log.metadata);
}
public warn(message: string, context?: string) {
const log = this.buildLog(message, context);
this.logger.warn(log.message, log.metadata);
}
public error(message: string, trace?: string, context?: string) {
const log = this.buildLog(message, context, trace);
this.logger.error(log.message, log.metadata);
}
private buildLog(message: string, context?: string, trace?: string) {
const ctx = context || this.context;
const traceId = this.getTraceId();
return {
message: `[${ctx}] ${message}`,
metadata: {
traceId,
source: ctx,
stackTrace: trace,
},
};
}
private getTraceId(): string {
const clsNamespace = cls.getNamespace(TraceId.Namespace);
if (!clsNamespace) {
return null;
}
return clsNamespace.get(TraceId.Key);
}
}
解决方案
所以事实证明问题是@google-cloud/logging-winston
包中有一个错误,导致它抛出这个错误:
UnhandledPromiseRejectionWarning: FetchError: request to http://169.254.169.254/computeMetadata/v1/instance failed, reason: connect ECONNREFUSED 169.254.169.254:80
这已在版本 3.0.6 中得到修复 - 请参阅https://github.com/googleapis/nodejs-logging-winston/issues/389#issuecomment-593727968。
更新后@google-cloud/logging-winston
,我在 Stackdriver 中的 Winston 日志记录工作正常。
推荐阅读
- c++ - 在线绘制和可视化递归树
- java - 在 Java 中的这个例子中 += 赋值的简写是什么?
- java - 如何通过邮件接收反馈内容
- javascript - Firebase JavaScript 排序
- python-3.x - 密码保护python生成的xml文件
- c++ - 在骰子上没有得到正确的预期输出
- python - 我基本上想做一个元素加法,但只使用 for 循环,所以没有 numpy 或 map
- java - 怎么办这个错误 - androidx.appcompat.widget.AppCompatSeekBar 不能转换为 android.view.ViewGroup?
- javascript - 如何在子字符串后修改jquery中的css值?
- scikit-learn - 无法使用 sklearn 中的稀疏矩阵计算 silhouette_score