返回 AiToEarn
cloud-watch.logger.ts
根目录 / project / aitoearn-backend / libs / common / src / loggers / cloud-watch.logger.ts
1 import type { CloudWatchLogsClientConfig, Entity } from '@aws-sdk/client-cloudwatch-logs'
2 import type { DestinationStream } from 'pino'
3 import * as os from 'node:os'
4 import { debuglog } from 'node:util'
5 import { CloudWatchLogsClient, CreateLogGroupCommand, CreateLogStreamCommand, PutLogEventsCommand } from '@aws-sdk/client-cloudwatch-logs'
6
7 const log = debuglog('app:cloud-watch:logger')
8
9 export interface CloudWatchLoggerOptions extends CloudWatchLogsClientConfig {
10 accessKeyId?: string
11 secretAccessKey?: string
12 group: string
13 stream?: string
14 entity?: Entity
15 }
16
17 export class CloudWatchLogger implements DestinationStream {
18 private readonly client: CloudWatchLogsClient
19 private writeQueue: Promise<void> = Promise.resolve()
20 private readonly ready: Promise<void>
21
22 constructor(private readonly options: CloudWatchLoggerOptions) {
23 const credentials = options.accessKeyId && options.secretAccessKey
24 ? {
25 accessKeyId: options.accessKeyId,
26 secretAccessKey: options.secretAccessKey,
27 }
28 : options.credentials
29 this.client = new CloudWatchLogsClient({
30 ...options,
31 credentials,
32 })
33
34 log('creating logger')
35
36 this.options.stream = options.stream || `${os.hostname()}-${process.pid}-${Date.now()}`
37
38 this.ready = this.createLogGroup().then(() => this.createLogStream())
39 }
40
41 async createLogGroup() {
42 log(`creating log group: ${this.options.group}`)
43 const command = new CreateLogGroupCommand({
44 logGroupName: this.options.group,
45 })
46 await this.client.send(command)
47 .catch((e) => {
48 log(`creating log group: ${this.options.group} error ${e}`)
49 if (e.name !== 'ResourceAlreadyExistsException')
50 throw e
51 })
52
53 log(`created log group: ${this.options.group}`)
54 }
55
56 async createLogStream() {
57 log(`creating log stream: ${this.options.stream}`)
58 const command = new CreateLogStreamCommand({
59 logGroupName: this.options.group,
60 logStreamName: this.options.stream,
61 })
62 await this.client.send(command)
63 .catch((e) => {
64 log(`creating log stream: ${this.options.stream} error ${e}`)
65 if (e.name !== 'ResourceAlreadyExistsException')
66 throw e
67 })
68 log(`created log stream: ${this.options.stream}`)
69 }
70
71 async write(msg: string): Promise<void> {
72 this.writeQueue = this.writeQueue.then(async () => {
73 await this.ready
74
75 const command = new PutLogEventsCommand({
76 logGroupName: this.options.group,
77 logStreamName: this.options.stream,
78 entity: this.options.entity,
79 logEvents: [
80 {
81 timestamp: Date.now(),
82 message: msg,
83 },
84 ],
85 })
86
87 await this.client.send(command)
88 log(`put logs`)
89 }).catch((e) => {
90 log(`failed to put logs: ${e}`)
91 })
92
93 return this.writeQueue
94 }
95 }
96
96 lines TYPESCRIPT