@aws-amplify/core 4.4.1 → 4.4.2-api-logging.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aws-amplify-core.js +1041 -202
- package/dist/aws-amplify-core.js.map +1 -1
- package/dist/aws-amplify-core.min.js +7 -7
- package/dist/aws-amplify-core.min.js.map +1 -1
- package/lib/Logger/ConsoleLogger.d.ts +12 -1
- package/lib/Logger/ConsoleLogger.js +69 -6
- package/lib/Logger/ConsoleLogger.js.map +1 -1
- package/lib/Platform/version.d.ts +1 -1
- package/lib/Platform/version.js +1 -1
- package/lib/Providers/APIProvider/APILoggingProvider.d.ts +28 -0
- package/lib/Providers/APIProvider/APILoggingProvider.js +320 -0
- package/lib/Providers/APIProvider/APILoggingProvider.js.map +1 -0
- package/lib/Providers/APIProvider/CloudWatchEventFormatter.d.ts +5 -0
- package/lib/Providers/APIProvider/CloudWatchEventFormatter.js +78 -0
- package/lib/Providers/APIProvider/CloudWatchEventFormatter.js.map +1 -0
- package/lib/Providers/APIProvider/Fetch.d.ts +10 -0
- package/lib/Providers/APIProvider/Fetch.js +13 -0
- package/lib/Providers/APIProvider/Fetch.js.map +1 -0
- package/lib/Providers/APIProvider/LoggerReachability/index.d.ts +3 -0
- package/lib/Providers/APIProvider/LoggerReachability/index.js +5 -0
- package/lib/Providers/APIProvider/LoggerReachability/index.js.map +1 -0
- package/lib/Providers/APIProvider/LoggerReachability/index.native.d.ts +3 -0
- package/lib/Providers/APIProvider/LoggerReachability/index.native.js +9 -0
- package/lib/Providers/APIProvider/LoggerReachability/index.native.js.map +1 -0
- package/lib/Providers/APIProvider/index.d.ts +1 -0
- package/lib/Providers/APIProvider/index.js +5 -0
- package/lib/Providers/APIProvider/index.js.map +1 -0
- package/lib/Providers/APIProvider/loggerConnectivity.d.ts +14 -0
- package/lib/Providers/APIProvider/loggerConnectivity.js +55 -0
- package/lib/Providers/APIProvider/loggerConnectivity.js.map +1 -0
- package/lib/Providers/AWSCloudWatchProvider.js +8 -1
- package/lib/Providers/AWSCloudWatchProvider.js.map +1 -1
- package/lib/Providers/index.d.ts +1 -0
- package/lib/Providers/index.js +2 -0
- package/lib/Providers/index.js.map +1 -1
- package/lib/Util/Constants.d.ts +3 -1
- package/lib/Util/Constants.js +4 -0
- package/lib/Util/Constants.js.map +1 -1
- package/lib/Util/Reachability.js +7 -5
- package/lib/Util/Reachability.js.map +1 -1
- package/lib/Util/Retry.js +2 -2
- package/lib/Util/Retry.js.map +1 -1
- package/lib/Util/StringUtils.d.ts +1 -0
- package/lib/Util/StringUtils.js +11 -6
- package/lib/Util/StringUtils.js.map +1 -1
- package/lib/types/index.js +4 -0
- package/lib/types/index.js.map +1 -1
- package/lib/types/types.d.ts +38 -1
- package/lib/types/types.js +4 -0
- package/lib/types/types.js.map +1 -1
- package/lib-esm/Logger/ConsoleLogger.d.ts +12 -1
- package/lib-esm/Logger/ConsoleLogger.js +67 -4
- package/lib-esm/Logger/ConsoleLogger.js.map +1 -1
- package/lib-esm/Platform/version.d.ts +1 -1
- package/lib-esm/Platform/version.js +1 -1
- package/lib-esm/Providers/APIProvider/APILoggingProvider.d.ts +28 -0
- package/lib-esm/Providers/APIProvider/APILoggingProvider.js +315 -0
- package/lib-esm/Providers/APIProvider/APILoggingProvider.js.map +1 -0
- package/lib-esm/Providers/APIProvider/CloudWatchEventFormatter.d.ts +5 -0
- package/lib-esm/Providers/APIProvider/CloudWatchEventFormatter.js +76 -0
- package/lib-esm/Providers/APIProvider/CloudWatchEventFormatter.js.map +1 -0
- package/lib-esm/Providers/APIProvider/Fetch.d.ts +10 -0
- package/lib-esm/Providers/APIProvider/Fetch.js +11 -0
- package/lib-esm/Providers/APIProvider/Fetch.js.map +1 -0
- package/lib-esm/Providers/APIProvider/LoggerReachability/index.d.ts +3 -0
- package/lib-esm/Providers/APIProvider/LoggerReachability/index.js +3 -0
- package/lib-esm/Providers/APIProvider/LoggerReachability/index.js.map +1 -0
- package/lib-esm/Providers/APIProvider/LoggerReachability/index.native.d.ts +3 -0
- package/lib-esm/Providers/APIProvider/LoggerReachability/index.native.js +4 -0
- package/lib-esm/Providers/APIProvider/LoggerReachability/index.native.js.map +1 -0
- package/lib-esm/Providers/APIProvider/index.d.ts +1 -0
- package/lib-esm/Providers/APIProvider/index.js +2 -0
- package/lib-esm/Providers/APIProvider/index.js.map +1 -0
- package/lib-esm/Providers/APIProvider/loggerConnectivity.d.ts +14 -0
- package/lib-esm/Providers/APIProvider/loggerConnectivity.js +50 -0
- package/lib-esm/Providers/APIProvider/loggerConnectivity.js.map +1 -0
- package/lib-esm/Providers/AWSCloudWatchProvider.js +8 -1
- package/lib-esm/Providers/AWSCloudWatchProvider.js.map +1 -1
- package/lib-esm/Providers/index.d.ts +1 -0
- package/lib-esm/Providers/index.js +1 -0
- package/lib-esm/Providers/index.js.map +1 -1
- package/lib-esm/Util/Constants.d.ts +3 -1
- package/lib-esm/Util/Constants.js +3 -1
- package/lib-esm/Util/Constants.js.map +1 -1
- package/lib-esm/Util/Reachability.js +5 -3
- package/lib-esm/Util/Reachability.js.map +1 -1
- package/lib-esm/Util/Retry.js +2 -2
- package/lib-esm/Util/Retry.js.map +1 -1
- package/lib-esm/Util/StringUtils.d.ts +1 -0
- package/lib-esm/Util/StringUtils.js +11 -6
- package/lib-esm/Util/StringUtils.js.map +1 -1
- package/lib-esm/types/index.js +1 -0
- package/lib-esm/types/index.js.map +1 -1
- package/lib-esm/types/types.d.ts +38 -1
- package/lib-esm/types/types.js +3 -0
- package/lib-esm/types/types.js.map +1 -1
- package/package.json +2 -2
- package/src/Logger/ConsoleLogger.ts +54 -2
- package/src/Platform/version.ts +1 -1
- package/src/Providers/APIProvider/APILoggingProvider.ts +286 -0
- package/src/Providers/APIProvider/CloudWatchEventFormatter.ts +100 -0
- package/src/Providers/APIProvider/Fetch.ts +10 -0
- package/src/Providers/APIProvider/LoggerReachability/index.native.ts +4 -0
- package/src/Providers/APIProvider/LoggerReachability/index.ts +2 -0
- package/src/Providers/APIProvider/index.ts +1 -0
- package/src/Providers/APIProvider/loggerConnectivity.ts +49 -0
- package/src/Providers/AWSCloudWatchProvider.ts +9 -1
- package/src/Providers/index.ts +1 -0
- package/src/Util/Constants.ts +4 -0
- package/src/Util/Reachability.ts +9 -7
- package/src/Util/Retry.ts +2 -2
- package/src/Util/StringUtils.ts +15 -6
- package/src/types/types.ts +106 -62
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { ConsoleLogger as Logger, LOG_LEVELS, LOG_TYPE } from '../../Logger';
|
|
2
|
+
import LoggerConnectivity from './loggerConnectivity';
|
|
3
|
+
import {
|
|
4
|
+
LoggingProvider,
|
|
5
|
+
GenericLogEvent,
|
|
6
|
+
APILoggingProviderOptions,
|
|
7
|
+
isLoggerEvent,
|
|
8
|
+
} from '../../types/types';
|
|
9
|
+
import { jitteredExponentialRetry, NonRetryableError } from '../../../';
|
|
10
|
+
import { postOptions } from './Fetch';
|
|
11
|
+
import {
|
|
12
|
+
API_LOGGING_PROVIDER_NAME,
|
|
13
|
+
API_LOGGING_PROVIDER_CATEGORY,
|
|
14
|
+
} from '../../Util/Constants';
|
|
15
|
+
import { getStringByteSize } from '../../Util/StringUtils';
|
|
16
|
+
import { InputLogEvent } from '@aws-sdk/client-cloudwatch-logs';
|
|
17
|
+
|
|
18
|
+
const logger = new Logger('APILoggingProvider');
|
|
19
|
+
const HTTPS = 'https';
|
|
20
|
+
const LOCALHOST = 'http://localhost';
|
|
21
|
+
const DEFAULT_INTERVAL = 3000;
|
|
22
|
+
|
|
23
|
+
// Exclude these to avoid infinite loop
|
|
24
|
+
const DEFAULT_CLASS_EXCLUDE_LIST = [
|
|
25
|
+
'APILoggingProvider',
|
|
26
|
+
'AWSCloudWatch',
|
|
27
|
+
'Retry', // causes a loop because we're using jitteredExponentialRetry below
|
|
28
|
+
];
|
|
29
|
+
const LAMBDA_PAYLOAD_LIMIT = 6 * 1024 * 1024; // 6MB
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
Todo:
|
|
33
|
+
* Refactor this into a FSM - managing state will become complex when remote config functionality gets added in P1
|
|
34
|
+
*/
|
|
35
|
+
class APILoggingProvider implements LoggingProvider {
|
|
36
|
+
static readonly PROVIDER_NAME = API_LOGGING_PROVIDER_NAME;
|
|
37
|
+
static readonly CATEGORY = API_LOGGING_PROVIDER_CATEGORY;
|
|
38
|
+
|
|
39
|
+
private config: APILoggingProviderOptions;
|
|
40
|
+
private connectivity: LoggerConnectivity;
|
|
41
|
+
private online: boolean = false;
|
|
42
|
+
private bufferInterval: ReturnType<typeof setInterval>;
|
|
43
|
+
private eventBuffer: GenericLogEvent[] = [];
|
|
44
|
+
|
|
45
|
+
constructor(config?: APILoggingProviderOptions) {
|
|
46
|
+
this.configure(config);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public getProviderName(): string {
|
|
50
|
+
return APILoggingProvider.PROVIDER_NAME;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public getCategoryName(): string {
|
|
54
|
+
return APILoggingProvider.CATEGORY;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public configure(
|
|
58
|
+
config: APILoggingProviderOptions
|
|
59
|
+
): object | APILoggingProviderOptions {
|
|
60
|
+
try {
|
|
61
|
+
this.validateConfig(config);
|
|
62
|
+
this.config = this.normalizeConfig(config);
|
|
63
|
+
|
|
64
|
+
if (this.config.enabled === true) {
|
|
65
|
+
this.initialize();
|
|
66
|
+
} else {
|
|
67
|
+
this.disable();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return this.config;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
logger.error('Unable to start: ', error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public pushLogs(events: InputLogEvent[]): void {
|
|
77
|
+
if (this.config?.enabled === false) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
events.forEach(event => {
|
|
82
|
+
if (!isLoggerEvent(event)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const {
|
|
87
|
+
message,
|
|
88
|
+
timestamp,
|
|
89
|
+
loggerInfo: {
|
|
90
|
+
level,
|
|
91
|
+
data,
|
|
92
|
+
name,
|
|
93
|
+
error: { message: errorMessage, name: errorName } = {},
|
|
94
|
+
},
|
|
95
|
+
} = event;
|
|
96
|
+
|
|
97
|
+
const error = errorName &&
|
|
98
|
+
errorMessage && {
|
|
99
|
+
errorMessage,
|
|
100
|
+
errorName,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const combinedEvent: GenericLogEvent = {
|
|
104
|
+
level,
|
|
105
|
+
error,
|
|
106
|
+
message,
|
|
107
|
+
context: {
|
|
108
|
+
category: name,
|
|
109
|
+
data: { ...data, ...this.config.metadata },
|
|
110
|
+
logTime: timestamp,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
if (this.verifyEvent(combinedEvent)) {
|
|
115
|
+
this.eventBuffer.push(combinedEvent);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private validateConfig(config: APILoggingProviderOptions): void {
|
|
121
|
+
if (typeof config?.endpoint !== 'string') {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Invalid configuration. \`config.endpoint\` must be a string. Received: ${typeof config?.endpoint}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const protocolStr = config.endpoint.substring(0, 5).toLowerCase();
|
|
128
|
+
const isUsingLocalhost =
|
|
129
|
+
config.endpoint.substring(0, LOCALHOST.length).toLowerCase() ===
|
|
130
|
+
LOCALHOST;
|
|
131
|
+
|
|
132
|
+
if (protocolStr !== HTTPS && !isUsingLocalhost) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Invalid configuration. Only HTTPS endpoints are supported. Received: ${protocolStr}`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private normalizeConfig(
|
|
140
|
+
config: APILoggingProviderOptions
|
|
141
|
+
): APILoggingProviderOptions {
|
|
142
|
+
const normalizedConfig = {
|
|
143
|
+
endpoint: config.endpoint,
|
|
144
|
+
apiKey: config.apiKey,
|
|
145
|
+
metadata: config.metadata,
|
|
146
|
+
enabled: config.enabled ?? true,
|
|
147
|
+
level: config.level ?? LOG_TYPE.WARN,
|
|
148
|
+
bufferInterval: config.bufferInterval ?? DEFAULT_INTERVAL,
|
|
149
|
+
excludeClassList:
|
|
150
|
+
config.excludeClassList instanceof Array
|
|
151
|
+
? [...DEFAULT_CLASS_EXCLUDE_LIST, ...config.excludeClassList]
|
|
152
|
+
: DEFAULT_CLASS_EXCLUDE_LIST,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
return normalizedConfig;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private initialize() {
|
|
159
|
+
this.subscribeConnectivity();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private disable() {
|
|
163
|
+
this.clearInterval();
|
|
164
|
+
this.unsubscribeConnectivity();
|
|
165
|
+
this.eventBuffer = [];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private subscribeConnectivity() {
|
|
169
|
+
if (this.connectivity) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
this.connectivity = new LoggerConnectivity();
|
|
174
|
+
this.connectivity.status().subscribe(({ online }) => {
|
|
175
|
+
if (online && !this.online) {
|
|
176
|
+
this.startInterval();
|
|
177
|
+
} else {
|
|
178
|
+
this.clearInterval();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.online = online;
|
|
182
|
+
logger.debug('Connectivity status: ', online);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private unsubscribeConnectivity() {
|
|
187
|
+
if (this.connectivity) {
|
|
188
|
+
this.connectivity.unsubscribe();
|
|
189
|
+
this.connectivity = undefined;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private startInterval() {
|
|
194
|
+
if (this.bufferInterval) {
|
|
195
|
+
this.clearInterval();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.bufferInterval = setInterval(async () => {
|
|
199
|
+
try {
|
|
200
|
+
await this.uploadEvents();
|
|
201
|
+
} catch (error) {
|
|
202
|
+
logger.error(`Could not upload log events`, error);
|
|
203
|
+
}
|
|
204
|
+
}, this.config.bufferInterval);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private clearInterval() {
|
|
208
|
+
clearInterval(this.bufferInterval);
|
|
209
|
+
this.bufferInterval = undefined;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/*
|
|
213
|
+
Filter out events that don't adhere to config settings
|
|
214
|
+
*/
|
|
215
|
+
private verifyEvent(event: GenericLogEvent): boolean {
|
|
216
|
+
const { level, context } = event;
|
|
217
|
+
|
|
218
|
+
const configLevelValue = LOG_LEVELS[this.config.level];
|
|
219
|
+
const eventLevelValue = LOG_LEVELS[level];
|
|
220
|
+
|
|
221
|
+
if (eventLevelValue < configLevelValue) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (this.config.excludeClassList.includes(context.category)) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private async uploadEvents(): Promise<void> {
|
|
233
|
+
const eventBatch = this.genericLogEventBatch(this.eventBuffer);
|
|
234
|
+
|
|
235
|
+
if (eventBatch.length < 1) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const body = {
|
|
240
|
+
logs: eventBatch,
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const options = postOptions(body);
|
|
244
|
+
|
|
245
|
+
await jitteredExponentialRetry(
|
|
246
|
+
async (endpoint, options) => {
|
|
247
|
+
try {
|
|
248
|
+
await fetch(endpoint, options); // fetch doesnt return info for no-cors request
|
|
249
|
+
} catch (error) {
|
|
250
|
+
// fetch will only fail in the event of a network error. Don't retry
|
|
251
|
+
if (error.message === 'Failed to fetch') {
|
|
252
|
+
throw new NonRetryableError(error.message);
|
|
253
|
+
}
|
|
254
|
+
// all other errors are logged and retried
|
|
255
|
+
logger.warn(error);
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
[this.config.endpoint, options]
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
// remove events from buffer after the request succeeds
|
|
263
|
+
this.eventBuffer.splice(0, eventBatch.length);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private genericLogEventBatch(buffer: GenericLogEvent[]): GenericLogEvent[] {
|
|
267
|
+
let totalByteSize = 0;
|
|
268
|
+
let currentEventIdx = 0;
|
|
269
|
+
|
|
270
|
+
while (currentEventIdx < buffer.length) {
|
|
271
|
+
const currentEvent = buffer[currentEventIdx];
|
|
272
|
+
const eventSize = getStringByteSize(JSON.stringify(currentEvent));
|
|
273
|
+
|
|
274
|
+
if (totalByteSize + eventSize > LAMBDA_PAYLOAD_LIMIT) {
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
totalByteSize += eventSize;
|
|
279
|
+
currentEventIdx += 1;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return buffer.slice(0, currentEventIdx);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export { APILoggingProvider };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { InputLogEvent } from '@aws-sdk/client-cloudwatch-logs';
|
|
2
|
+
import {
|
|
3
|
+
AWS_CLOUDWATCH_BASE_BUFFER_SIZE,
|
|
4
|
+
AWS_CLOUDWATCH_MAX_BATCH_EVENT_SIZE,
|
|
5
|
+
AWS_CLOUDWATCH_MAX_EVENT_SIZE,
|
|
6
|
+
} from '../../Util/Constants';
|
|
7
|
+
import { GenericLogEvent } from '../../types/types';
|
|
8
|
+
import { ConsoleLogger as Logger, getStringByteSize } from '../../';
|
|
9
|
+
|
|
10
|
+
const logger = new Logger('APILoggingProvider');
|
|
11
|
+
const TRUNCATE_MAX_LENGTH = 5000;
|
|
12
|
+
|
|
13
|
+
export const cloudWatchEventFromGeneric = (
|
|
14
|
+
event: GenericLogEvent
|
|
15
|
+
): InputLogEvent => {
|
|
16
|
+
const {
|
|
17
|
+
context: { logTime: timestamp },
|
|
18
|
+
...message
|
|
19
|
+
} = event;
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
timestamp,
|
|
23
|
+
message: JSON.stringify(event),
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const truncateOversizedEvent = (event: InputLogEvent): InputLogEvent => {
|
|
28
|
+
const { timestamp, message } = event;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const messageJson = JSON.parse(message);
|
|
32
|
+
|
|
33
|
+
const truncatedObj = {
|
|
34
|
+
level: messageJson.level,
|
|
35
|
+
class: messageJson.class,
|
|
36
|
+
message: messageJson.message.substring(0, TRUNCATE_MAX_LENGTH),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
if (messageJson.data != null) {
|
|
40
|
+
truncatedObj[
|
|
41
|
+
'data'
|
|
42
|
+
] = `OBJECT SIZE EXCEEDS CLOUDWATCH EVENT LIMIT. Truncated: ${JSON.stringify(
|
|
43
|
+
messageJson.data
|
|
44
|
+
).substring(0, TRUNCATE_MAX_LENGTH)}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
timestamp,
|
|
49
|
+
message: JSON.stringify(truncatedObj),
|
|
50
|
+
};
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logger.warn('Could not truncate oversized event', error);
|
|
53
|
+
|
|
54
|
+
const truncated = JSON.stringify({
|
|
55
|
+
level: 'UNKNOWN',
|
|
56
|
+
class: 'Unknown',
|
|
57
|
+
message:
|
|
58
|
+
'OBJECT SIZE EXCEEDS CLOUDWATCH EVENT LIMIT. Could not parse event to truncate',
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
timestamp,
|
|
63
|
+
message: truncated,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const cloudWatchLogEventBatch = (
|
|
69
|
+
buffer: InputLogEvent[]
|
|
70
|
+
): InputLogEvent[] => {
|
|
71
|
+
let totalByteSize = 0;
|
|
72
|
+
let currentEventIdx = 0;
|
|
73
|
+
|
|
74
|
+
while (currentEventIdx < buffer.length) {
|
|
75
|
+
let currentEvent = buffer[currentEventIdx];
|
|
76
|
+
let eventSize =
|
|
77
|
+
getStringByteSize(currentEvent.message) + AWS_CLOUDWATCH_BASE_BUFFER_SIZE;
|
|
78
|
+
|
|
79
|
+
if (eventSize > AWS_CLOUDWATCH_MAX_EVENT_SIZE) {
|
|
80
|
+
const errString = `Log event exceeds maximum size for CloudWatch logs. Log size: ${eventSize}. Truncating log message.`;
|
|
81
|
+
logger.debug(errString);
|
|
82
|
+
|
|
83
|
+
currentEvent = truncateOversizedEvent(currentEvent);
|
|
84
|
+
buffer[currentEventIdx] = currentEvent;
|
|
85
|
+
|
|
86
|
+
eventSize =
|
|
87
|
+
new TextEncoder().encode(currentEvent.message).length +
|
|
88
|
+
AWS_CLOUDWATCH_BASE_BUFFER_SIZE;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (totalByteSize + eventSize > AWS_CLOUDWATCH_MAX_BATCH_EVENT_SIZE) {
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
totalByteSize += eventSize;
|
|
96
|
+
currentEventIdx += 1;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return buffer.slice(0, currentEventIdx);
|
|
100
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { APILoggingProvider } from './APILoggingProvider';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import Observable, { ZenObservable } from 'zen-observable-ts';
|
|
2
|
+
import { ReachabilityMonitor } from './LoggerReachability';
|
|
3
|
+
|
|
4
|
+
type ConnectionStatus = {
|
|
5
|
+
online: boolean;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// TODO: refactor this to a singleton that supports numerous observers
|
|
9
|
+
export default class LoggerConnectivity {
|
|
10
|
+
private connectionStatus: ConnectionStatus;
|
|
11
|
+
private observer: ZenObservable.SubscriptionObserver<ConnectionStatus>;
|
|
12
|
+
private subscription: ZenObservable.Subscription;
|
|
13
|
+
private timeout: ReturnType<typeof setTimeout>;
|
|
14
|
+
constructor() {
|
|
15
|
+
this.connectionStatus = {
|
|
16
|
+
online: false,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
status(): Observable<ConnectionStatus> {
|
|
21
|
+
if (this.observer) {
|
|
22
|
+
throw new Error('Subscriber already exists');
|
|
23
|
+
}
|
|
24
|
+
return new Observable(observer => {
|
|
25
|
+
this.observer = observer;
|
|
26
|
+
|
|
27
|
+
this.subscription = ReachabilityMonitor.subscribe(({ online }) => {
|
|
28
|
+
this.connectionStatus.online = online;
|
|
29
|
+
|
|
30
|
+
const observerResult = { ...this.connectionStatus };
|
|
31
|
+
|
|
32
|
+
observer.next(observerResult);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return () => {
|
|
36
|
+
clearTimeout(this.timeout);
|
|
37
|
+
this.unsubscribe();
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
unsubscribe() {
|
|
43
|
+
if (this.subscription) {
|
|
44
|
+
clearTimeout(this.timeout);
|
|
45
|
+
this.subscription.unsubscribe();
|
|
46
|
+
this.observer = undefined;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -218,7 +218,15 @@ class AWSCloudWatchProvider implements LoggingProvider {
|
|
|
218
218
|
|
|
219
219
|
public pushLogs(logs: InputLogEvent[]): void {
|
|
220
220
|
logger.debug('pushing log events to Cloudwatch...');
|
|
221
|
-
|
|
221
|
+
const formattedLogs = logs.map(({ message, timestamp }) => ({
|
|
222
|
+
message,
|
|
223
|
+
timestamp,
|
|
224
|
+
}));
|
|
225
|
+
|
|
226
|
+
this._dataTracker.logEvents = [
|
|
227
|
+
...this._dataTracker.logEvents,
|
|
228
|
+
...formattedLogs,
|
|
229
|
+
];
|
|
222
230
|
}
|
|
223
231
|
|
|
224
232
|
private async _validateLogGroupExistsAndCreate(
|
package/src/Providers/index.ts
CHANGED
package/src/Util/Constants.ts
CHANGED
|
@@ -22,6 +22,8 @@ const RETRY_ERROR_CODES = [
|
|
|
22
22
|
'ResourceNotFoundException',
|
|
23
23
|
'InvalidSequenceTokenException',
|
|
24
24
|
];
|
|
25
|
+
const API_LOGGING_PROVIDER_NAME = 'API';
|
|
26
|
+
const API_LOGGING_PROVIDER_CATEGORY = 'Logging';
|
|
25
27
|
|
|
26
28
|
export {
|
|
27
29
|
AWS_CLOUDWATCH_BASE_BUFFER_SIZE,
|
|
@@ -31,4 +33,6 @@ export {
|
|
|
31
33
|
AWS_CLOUDWATCH_PROVIDER_NAME,
|
|
32
34
|
NO_CREDS_ERROR_STRING,
|
|
33
35
|
RETRY_ERROR_CODES,
|
|
36
|
+
API_LOGGING_PROVIDER_NAME,
|
|
37
|
+
API_LOGGING_PROVIDER_CATEGORY,
|
|
34
38
|
};
|
package/src/Util/Reachability.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { browserOrNode, isWebWorker } from '
|
|
1
|
+
import { browserOrNode, isWebWorker } from '../JS';
|
|
2
2
|
import Observable, { ZenObservable } from 'zen-observable-ts';
|
|
3
3
|
|
|
4
4
|
type NetworkStatus = {
|
|
@@ -32,9 +32,10 @@ export default class ReachabilityNavigator implements Reachability {
|
|
|
32
32
|
globalObj.removeEventListener('online', notifyOnline);
|
|
33
33
|
globalObj.removeEventListener('offline', notifyOffline);
|
|
34
34
|
|
|
35
|
-
ReachabilityNavigator._observers =
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
ReachabilityNavigator._observers =
|
|
36
|
+
ReachabilityNavigator._observers.filter(
|
|
37
|
+
_observer => _observer !== observer
|
|
38
|
+
);
|
|
38
39
|
};
|
|
39
40
|
});
|
|
40
41
|
}
|
|
@@ -43,9 +44,10 @@ export default class ReachabilityNavigator implements Reachability {
|
|
|
43
44
|
private static _observerOverride(status: NetworkStatus): void {
|
|
44
45
|
for (const observer of ReachabilityNavigator._observers) {
|
|
45
46
|
if (observer.closed) {
|
|
46
|
-
ReachabilityNavigator._observers =
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
ReachabilityNavigator._observers =
|
|
48
|
+
ReachabilityNavigator._observers.filter(
|
|
49
|
+
_observer => _observer !== observer
|
|
50
|
+
);
|
|
49
51
|
continue;
|
|
50
52
|
}
|
|
51
53
|
observer.next(status);
|
package/src/Util/Retry.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DelayFunction } from '../types';
|
|
2
2
|
import { ConsoleLogger as Logger } from '../Logger/ConsoleLogger';
|
|
3
|
-
const logger = new Logger('
|
|
3
|
+
const logger = new Logger('Retry');
|
|
4
4
|
|
|
5
5
|
export class NonRetryableError extends Error {
|
|
6
6
|
public readonly nonRetryable = true;
|
|
@@ -30,7 +30,7 @@ export async function retry(
|
|
|
30
30
|
logger.debug(
|
|
31
31
|
`${
|
|
32
32
|
functionToRetry.name
|
|
33
|
-
} attempt #${attempt} with
|
|
33
|
+
} attempt #${attempt} with these vars: ${JSON.stringify(args)}`
|
|
34
34
|
);
|
|
35
35
|
|
|
36
36
|
try {
|
package/src/Util/StringUtils.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
+
import { browserOrNode } from '../../';
|
|
1
2
|
export function urlSafeEncode(str: string) {
|
|
2
3
|
return str
|
|
3
4
|
.split('')
|
|
4
|
-
.map(char =>
|
|
5
|
-
char
|
|
6
|
-
.charCodeAt(0)
|
|
7
|
-
.toString(16)
|
|
8
|
-
.padStart(2, '0')
|
|
9
|
-
)
|
|
5
|
+
.map(char => char.charCodeAt(0).toString(16).padStart(2, '0'))
|
|
10
6
|
.join('');
|
|
11
7
|
}
|
|
12
8
|
|
|
@@ -16,3 +12,16 @@ export function urlSafeDecode(hex: string) {
|
|
|
16
12
|
.map(char => String.fromCharCode(parseInt(char, 16)))
|
|
17
13
|
.join('');
|
|
18
14
|
}
|
|
15
|
+
export const getStringByteSize = (str: string): number => {
|
|
16
|
+
if (browserOrNode().isBrowser || Blob !== undefined) {
|
|
17
|
+
return new Blob([str]).size;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (Buffer !== undefined) {
|
|
21
|
+
return Buffer.byteLength(str, 'utf8');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
throw new Error(
|
|
25
|
+
'Cannot compute string length. Blob and Buffer are undefined'
|
|
26
|
+
);
|
|
27
|
+
};
|