部署Nest.Js至GAE(三):搭配 winston 將log寫在GCP
前言
由於GAE(Google App Engine)無法將檔案寫在本地端
需要搭配GCP另個logging 服務,記錄log
預設GCP只有針對HTTP連入/連出寫log,如果有批次作業、自行額外記錄的log,都不會寫入
從GAE頁面 > 服務 > 診斷下方的工具點開 > 記錄檔 進入
他有另個名字叫stack driver
但在GCP網頁上並沒標名清楚,而GCP官方文件也寫得不清不楚
再找相關套件時真的會一頭霧水,不知道能不能使用
GCP官方文件在Node.Js推薦使用winston
或Bunyan
我當初開發時是直接使用熟悉的log4js
由npm 上可以搜尋到log4js-stackdriver-appender
不過我當初並不確定stackdriver 就是 上述路徑的記錄檔
而此套件3年前發佈後就再也沒更新
所以我是照官方的改用winston寫log,GCP有提供@google-cloud/logging-winston支援
改寫winston成功後看其傳入參數,也如同log4js套件所要求的。故推測log4js-stackdriver-appender應也能順利連接
由於改winston成功了,就沒再花心力回去測試log4js了
使用nest-winston
界接
nest-winston使用方式可以參考官方教學:npm / nest-winston
由於我期望Nest.Js初始化的所有資訊也寫入GCP,以避免有執行錯誤時未記錄到
故我是將logger 寫在main.ts
裡
一、安裝所有相關套件
npm install --save nest-winston winston @google-cloud/logging-winston
二、增加winston.config.ts
參數檔
避免main看起來太過凌亂,我有增一winston.config.ts
專門放置參數檔
由於平常開發時期不會希望log都寫上GCP,畢竟超過太多量的話,是會被收費的…
故我有增加邏輯,平常開發時不會寫入,有需要測試到這段時,再放開註解就好
- 要記得將程式碼裡的「專案id」改成您的專案id
- logging.json,為GCP上IAM的權限,建立一新的角色增加權限下載下來就可以直接使用了
- 在GAE上不必特別設定,填好專案ID後,GCP上可以直接連通
import { LoggingWinston } from '@google-cloud/logging-winston';
import winston from 'winston';
import { utilities as nestWinstonModuleUtilities } from 'nest-winston';
export default () => {
const transports: any[] = [new winston.transports.Console()];
if (process.env.NODE_ENV === 'production') {
transports.push(new LoggingWinston({ projectId: '專案id', labels: { name: '專案id', version: '1.0.0' } }));
} else {
const logPath = `${process.cwd()}/logs`;
// 只有 error 等級的錯誤 , 才會將訊息寫到 error.log 檔案中
transports.push(new winston.transports.File({ filename: `${logPath}/error.log`, level: 'error' }));
// info or 以上的等級的訊息 , 將訊息寫入 combined.log 檔案中
transports.push(new winston.transports.File({ filename: `${logPath}/info.log`, tailable: true }));
// 測試寫到GCP時才需要
// process.env.GOOGLE_APPLICATION_CREDENTIALS = `${process.cwd()}/environment/logging.json`;
// transports.push(new LoggingWinston({ projectId: '專案id' }));
}
return {
format: winston.format.combine(
winston.format.timestamp(),
winston.format.ms(),
winston.format.prettyPrint(),
nestWinstonModuleUtilities.format.nestLike(),
),
transports,
}
}
三、調整main.ts
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
logger: WinstonModule.createLogger(winstonConfig())
});
四、使用方式
不必修改太過原先程式碼,直接用Nest.Js官方教學的的logger寫法就好
如下範例
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Product } from './products.entity';
@Injectable()
export class ProductsService {
logger = new Logger('ProductsService');
constructor(@InjectRepository(Product)
private readonly productRepository: Repository<Product>) {}
async getMany(): Promise<Product[]> {
const products = await this.productRepository.find();
this.logger.log(poroducts);
return products;
}
}
注意事項
winston是使用基本的stdout、stderr產生log
故this.logger.debug
、this.logger.verbose
不會顯示在console上,亦不會寫入到GCP
查了許久都說是增加VSCode的setting.json
,但我加了仍是不會顯示= =
須避開使用這2個,否則開發期用logger來判斷程式執行狀況的話,會以為程式都沒有成功執行
參考資料
官方文件 / @google-cloud/logging-winston