Skip to content

Commit 33a7266

Browse files
authored
fix: parse structured logs, and handle ANSI escape codes in logs (#620)
`firebase-functions/logger` writes structured logs to stdout and stderr by default, and makes these pretty with ANSI color codes. When functions-framework intercepts these messages to assign execution IDs, it handles them incorrectly in two ways. First, when the log emitted on stdout or stderr is already a structured log, we override (ignore) the severity set by the logger. This commit avoids changing severity if it's already set. Second, when parsing the message and attempting to determine if it's already a json object/structured log, it doesn't handle ANSI escape codes (https://en.wikipedia.org/wiki/ANSI_escape_code) used to control color, so parsing these will fail. This means that severity handling falls back to just looking at whether the message came from stdout or stderr, so debug-level and warn-level logs aren't handled correctly. This commit strips all ANSI escape codes that control terminal color. This is a minor bummer because it's a whole lot less pretty, but color-coding and smarter color coding generally seems less important than correct log-level handling. Fixes #617.
1 parent 47003fd commit 33a7266

File tree

3 files changed

+51
-5
lines changed

3 files changed

+51
-5
lines changed

.eslintrc.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
{
2-
"extends": "./node_modules/gts"
2+
"extends": "./node_modules/gts",
3+
"rules": {
4+
"no-control-regex": 0
5+
}
36
}

src/logger.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,24 @@ export function getModifiedData(
127127
return data;
128128
}
129129
const {isJSON, processedData} = processData(data, encoding);
130-
let dataWithContext;
130+
131+
let dataWithContext: {
132+
message: string | Uint8Array;
133+
'logging.googleapis.com/labels': {execution_id: string | undefined};
134+
'logging.googleapis.com/trace': string | undefined;
135+
'logging.googleapis.com/spanId': string | undefined;
136+
severity?: string | undefined;
137+
};
131138
if (isJSON) {
132139
dataWithContext = getJSONWithContext(processedData, currentContext);
140+
if (stderr && !(SEVERITY in dataWithContext)) {
141+
dataWithContext[SEVERITY] = 'ERROR';
142+
}
133143
} else {
134144
dataWithContext = getTextWithContext(processedData, currentContext);
135-
}
136-
if (stderr) {
137-
dataWithContext[SEVERITY] = 'ERROR';
145+
if (stderr) {
146+
dataWithContext[SEVERITY] = 'ERROR';
147+
}
138148
}
139149

140150
return JSON.stringify(dataWithContext) + '\n';
@@ -178,6 +188,9 @@ function processData(data: Uint8Array | string, encoding?: BufferEncoding) {
178188
return {isJSON: false, processedData: data};
179189
}
180190

191+
// strip any leading ANSI color codes from the decoded data
192+
// to parse colored JSON objects correctly
193+
decodedData = decodedData.replace(/\x1b[[(?);]{0,2}(;?\d)*./g, '');
181194
try {
182195
return {isJSON: true, processedData: JSON.parse(decodedData)};
183196
} catch (e) {

test/logger.ts

+30
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,34 @@ describe('getModifiedData', () => {
148148
) + '\n';
149149
assert.equal(modifiedData, expectedOutput);
150150
});
151+
152+
it('parses firebase warning severity and message', () => {
153+
const modifiedData = <string>(
154+
getModifiedData(
155+
'\u001b[33m{"severity":"WARNING","message":"testing warning log level"}\u001b[39m\n',
156+
undefined,
157+
true
158+
)
159+
);
160+
assert.equal('WARNING', JSON.parse(modifiedData)['severity']);
161+
assert.equal(
162+
'testing warning log level',
163+
JSON.parse(modifiedData)['message']
164+
);
165+
});
166+
167+
it('parses firebase error severity and message', () => {
168+
const modifiedData = <string>(
169+
getModifiedData(
170+
'\u001b[31m{"severity":"ERROR","message":"testing error log level"}\u001b[39m\n',
171+
undefined,
172+
true
173+
)
174+
);
175+
assert.equal('ERROR', JSON.parse(modifiedData)['severity']);
176+
assert.equal(
177+
'testing error log level',
178+
JSON.parse(modifiedData)['message']
179+
);
180+
});
151181
});

0 commit comments

Comments
 (0)