1
1
import * as util from 'util' ;
2
2
import * as cxapi from '@aws-cdk/cx-api' ;
3
3
import * as chalk from 'chalk' ;
4
- import { info , error } from '../../logging' ;
4
+ import * as uuid from 'uuid' ;
5
+ import type { CloudWatchLogEvent } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io' ;
6
+ import { IO , IoHelper } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private' ;
5
7
import { flatten } from '../../util' ;
6
8
import type { SDK } from '../aws-auth' ;
7
9
8
- /**
9
- * After reading events from all CloudWatch log groups
10
- * how long should we wait to read more events.
11
- *
12
- * If there is some error with reading events (i.e. Throttle)
13
- * then this is also how long we wait until we try again
14
- */
15
- const SLEEP = 2_000 ;
16
-
17
- /**
18
- * Represents a CloudWatch Log Event that will be
19
- * printed to the terminal
20
- */
21
- interface CloudWatchLogEvent {
22
- /**
23
- * The log event message
24
- */
25
- readonly message : string ;
26
-
27
- /**
28
- * The name of the log group
29
- */
30
- readonly logGroupName : string ;
31
-
32
- /**
33
- * The time at which the event occurred
34
- */
35
- readonly timestamp : Date ;
36
- }
37
-
38
10
/**
39
11
* Configuration tracking information on the log groups that are
40
12
* being monitored
@@ -54,6 +26,20 @@ interface LogGroupsAccessSettings {
54
26
readonly logGroupsStartTimes : { [ logGroupName : string ] : number } ;
55
27
}
56
28
29
+ export interface CloudWatchLogEventMonitorProps {
30
+ /**
31
+ * The IoHost used for messaging
32
+ */
33
+ readonly ioHelper : IoHelper ;
34
+
35
+ /**
36
+ * The time from which we start reading log messages
37
+ *
38
+ * @default - now
39
+ */
40
+ readonly startTime ?: Date ;
41
+ }
42
+
57
43
export class CloudWatchLogEventMonitor {
58
44
/**
59
45
* Determines which events not to display
@@ -65,18 +51,36 @@ export class CloudWatchLogEventMonitor {
65
51
*/
66
52
private readonly envsLogGroupsAccessSettings = new Map < string , LogGroupsAccessSettings > ( ) ;
67
53
68
- private active = false ;
54
+ /**
55
+ * After reading events from all CloudWatch log groups
56
+ * how long should we wait to read more events.
57
+ *
58
+ * If there is some error with reading events (i.e. Throttle)
59
+ * then this is also how long we wait until we try again
60
+ */
61
+ private readonly pollingInterval : number = 2_000 ;
62
+
63
+ public monitorId ?: string ;
64
+ private readonly ioHelper : IoHelper ;
69
65
70
- constructor ( startTime ?: Date ) {
71
- this . startTime = startTime ?. getTime ( ) ?? Date . now ( ) ;
66
+ constructor ( props : CloudWatchLogEventMonitorProps ) {
67
+ this . startTime = props . startTime ?. getTime ( ) ?? Date . now ( ) ;
68
+ this . ioHelper = props . ioHelper ;
72
69
}
73
70
74
71
/**
75
72
* resume reading/printing events
76
73
*/
77
- public activate ( ) : void {
78
- this . active = true ;
79
- this . scheduleNextTick ( 0 ) ;
74
+ public async activate ( ) : Promise < void > {
75
+ this . monitorId = uuid . v4 ( ) ;
76
+
77
+ await this . ioHelper . notify ( IO . CDK_TOOLKIT_I5032 . msg ( 'Start monitoring log groups' , {
78
+ monitor : this . monitorId ,
79
+ logGroupNames : this . logGroupNames ( ) ,
80
+ } ) ) ;
81
+
82
+ await this . tick ( ) ;
83
+ this . scheduleNextTick ( ) ;
80
84
}
81
85
82
86
/**
@@ -88,9 +92,16 @@ export class CloudWatchLogEventMonitor {
88
92
* Also resets the start time to be when the new deployment was triggered
89
93
* and clears the list of tracked log groups
90
94
*/
91
- public deactivate ( ) : void {
92
- this . active = false ;
95
+ public async deactivate ( ) : Promise < void > {
96
+ const oldMonitorId = this . monitorId ! ;
97
+ this . monitorId = undefined ;
93
98
this . startTime = Date . now ( ) ;
99
+
100
+ await this . ioHelper . notify ( IO . CDK_TOOLKIT_I5034 . msg ( 'Stopped monitoring log groups' , {
101
+ monitor : oldMonitorId ,
102
+ logGroupNames : this . logGroupNames ( ) ,
103
+ } ) ) ;
104
+
94
105
this . envsLogGroupsAccessSettings . clear ( ) ;
95
106
}
96
107
@@ -119,27 +130,41 @@ export class CloudWatchLogEventMonitor {
119
130
} ) ;
120
131
}
121
132
122
- private scheduleNextTick ( sleep : number ) : void {
123
- setTimeout ( ( ) => void this . tick ( ) , sleep ) ;
133
+ private logGroupNames ( ) : string [ ] {
134
+ return Array . from ( this . envsLogGroupsAccessSettings . values ( ) ) . flatMap ( ( settings ) => Object . keys ( settings . logGroupsStartTimes ) ) ;
135
+ }
136
+
137
+ private scheduleNextTick ( ) : void {
138
+ if ( ! this . monitorId ) {
139
+ return ;
140
+ }
141
+
142
+ setTimeout ( ( ) => void this . tick ( ) , this . pollingInterval ) ;
124
143
}
125
144
126
145
private async tick ( ) : Promise < void > {
127
146
// excluding from codecoverage because this
128
147
// doesn't always run (depends on timing)
129
- /* istanbul ignore next */
130
- if ( ! this . active ) {
148
+ /* c8 ignore next */
149
+ if ( ! this . monitorId ) {
131
150
return ;
132
151
}
152
+
133
153
try {
134
154
const events = flatten ( await this . readNewEvents ( ) ) ;
135
- events . forEach ( ( event ) => {
136
- this . print ( event ) ;
137
- } ) ;
138
- } catch ( e ) {
139
- error ( 'Error occurred while monitoring logs: %s' , e ) ;
155
+ for ( const event of events ) {
156
+ await this . print ( event ) ;
157
+ }
158
+
159
+ // We might have been stop()ped while the network call was in progress.
160
+ if ( ! this . monitorId ) {
161
+ return ;
162
+ }
163
+ } catch ( e : any ) {
164
+ await this . ioHelper . notify ( IO . CDK_TOOLKIT_E5035 . msg ( 'Error occurred while monitoring logs: %s' , { error : e } ) ) ;
140
165
}
141
166
142
- this . scheduleNextTick ( SLEEP ) ;
167
+ this . scheduleNextTick ( ) ;
143
168
}
144
169
145
170
/**
@@ -161,15 +186,16 @@ export class CloudWatchLogEventMonitor {
161
186
/**
162
187
* Print out a cloudwatch event
163
188
*/
164
- private print ( event : CloudWatchLogEvent ) : void {
165
- info (
189
+ private async print ( event : CloudWatchLogEvent ) : Promise < void > {
190
+ await this . ioHelper . notify ( IO . CDK_TOOLKIT_I5033 . msg (
166
191
util . format (
167
192
'[%s] %s %s' ,
168
193
chalk . blue ( event . logGroupName ) ,
169
194
chalk . yellow ( event . timestamp . toLocaleTimeString ( ) ) ,
170
195
event . message . trim ( ) ,
171
196
) ,
172
- ) ;
197
+ event ,
198
+ ) ) ;
173
199
}
174
200
175
201
/**
0 commit comments