@netlify/content-engine
Version:
144 lines • 7.69 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerAdditionalDiagnosticOutputHandler = registerAdditionalDiagnosticOutputHandler;
exports.createStructuredLoggingDiagnosticsMiddleware = createStructuredLoggingDiagnosticsMiddleware;
const constants_1 = require("../constants");
const calc_elapsed_time_1 = require("../util/calc-elapsed-time");
const utils_1 = require("./utils");
function calculateTimeoutDelay(envVarValue, defaultValue, min) {
if (!envVarValue) {
return 0;
}
else if (envVarValue === `1`) {
// Toggling env vars with "1" is quite common - because we allow to set
// specific timeout values with env var, this special case is added
// (1ms timeout makes little sense - that's too low value to be used as-is)
return defaultValue;
}
const parsedToNumber = parseInt(envVarValue, 10);
if (isNaN(parsedToNumber)) {
// It's truthy, but not a number - let's enable it with default value
return defaultValue;
}
// Allow to custom specific timeout value, but also put some minimal
// timeout bound as there is little usefulness of setting it to
// something less than few seconds.
return Math.max(parsedToNumber, min);
}
const FIVE_MINUTES = 1000 * 60 * 5;
const FIVE_SECONDS = 1000 * 5;
const TEN_MINUTES = 1000 * 60 * 10;
const TEN_SECONDS = 1000 * 10;
const additionalDiagnosticOutputHandlers = [];
function registerAdditionalDiagnosticOutputHandler(handler) {
additionalDiagnosticOutputHandlers.push(handler);
}
function generateAdditionalOutput() {
const extraMessages = [];
for (const handler of additionalDiagnosticOutputHandlers) {
const msg = handler();
if (msg) {
extraMessages.push(msg);
}
}
return extraMessages.length > 0
? `\n\nAdditional debugging logs:\n\n${extraMessages.join(`\n\n`)}`
: ``;
}
function createStructuredLoggingDiagnosticsMiddleware(getStore) {
const stuckStatusDiagnosticTimeoutDelay = calculateTimeoutDelay(process.env.GATSBY_DIAGNOSTIC_STUCK_STATUS_TIMEOUT, FIVE_MINUTES, // default timeout
FIVE_SECONDS);
const stuckStatusWatchdogTimeoutDelay = calculateTimeoutDelay(process.env.GATSBY_WATCHDOG_STUCK_STATUS_TIMEOUT, TEN_MINUTES, // default timeout
TEN_SECONDS);
if (!stuckStatusDiagnosticTimeoutDelay && !stuckStatusWatchdogTimeoutDelay) {
// none of timers are enabled, so this is no-op middleware
return () => { };
}
let displayedStuckStatusDiagnosticWarning = false;
let displayingStuckStatusDiagnosticWarning = false;
let stuckStatusDiagnosticTimer = null;
let stuckStatusWatchdogTimer = null;
let reporter;
function inProgressActivities() {
const { activities } = getStore().getState().logs;
return Object.values(activities)
.filter((activity) => (0, utils_1.isActivityInProgress)(activity.status))
.map((activity) => {
if (!activity.startTime) {
return activity;
}
return {
...activity,
diagnostics_elapsed_seconds: (0, calc_elapsed_time_1.calcElapsedTime)(activity.startTime),
};
});
}
function generateStuckStatusDiagnosticMessage() {
const activities = inProgressActivities();
return Object.values(activities)
.map((activity) => `- Activity "${activity.text}" of type "${activity.type}" is currently in state "${activity.status}"`)
.join(`\n`);
}
return function diagnosticMiddleware(action) {
// ignore diagnostic logs, otherwise diagnostic message itself will reset
// the timers
if (!displayingStuckStatusDiagnosticWarning) {
const currentStatus = getStore().getState().logs.status;
if (!reporter) {
// yuck, we have situation of circular dependencies here
// so this `reporter` import is delayed until it's actually needed
reporter = require(`../reporter`).reporter;
}
if (stuckStatusDiagnosticTimeoutDelay) {
if (stuckStatusDiagnosticTimer) {
clearTimeout(stuckStatusDiagnosticTimer);
stuckStatusDiagnosticTimer = null;
}
if (displayedStuckStatusDiagnosticWarning) {
// using nextTick here to prevent infinite recursion (report.warn would
// result in another call of this function and so on)
process.nextTick(() => {
const activitiesDiagnosticsMessage = generateStuckStatusDiagnosticMessage();
reporter.warn(`This is just diagnostic information (enabled by GATSBY_DIAGNOSTIC_STUCK_STATUS_TIMEOUT):\n\nThere was activity since last diagnostic message. Log action:\n\n${JSON.stringify(action, null, 2)}\n\nCurrently Gatsby is in: "${getStore().getState().logs.status}" state.${activitiesDiagnosticsMessage.length > 0
? `\n\nActivities preventing Gatsby from transitioning to idle state:\n\n${activitiesDiagnosticsMessage}`
: ``}`);
});
displayedStuckStatusDiagnosticWarning = false;
}
if (currentStatus === constants_1.ActivityStatuses.InProgress) {
stuckStatusDiagnosticTimer = setTimeout(function logStuckStatusDiagnostic() {
displayingStuckStatusDiagnosticWarning = true;
reporter.warn(`This is just diagnostic information (enabled by GATSBY_DIAGNOSTIC_STUCK_STATUS_TIMEOUT):\n\nGatsby is in "${getStore().getState().logs.status}" state without any updates for ${(stuckStatusDiagnosticTimeoutDelay / 1000).toFixed(3)} seconds. Activities preventing Gatsby from transitioning to idle state:\n\n${generateStuckStatusDiagnosticMessage()}${stuckStatusWatchdogTimeoutDelay
? `\n\nProcess will be terminated in ${((stuckStatusWatchdogTimeoutDelay -
stuckStatusDiagnosticTimeoutDelay) /
1000).toFixed(3)} seconds if nothing will change.`
: ``}${generateAdditionalOutput()}`);
displayingStuckStatusDiagnosticWarning = false;
displayedStuckStatusDiagnosticWarning = true;
}, stuckStatusDiagnosticTimeoutDelay);
}
}
if (stuckStatusWatchdogTimeoutDelay) {
if (stuckStatusWatchdogTimer) {
clearTimeout(stuckStatusWatchdogTimer);
stuckStatusWatchdogTimer = null;
}
if (currentStatus === constants_1.ActivityStatuses.InProgress) {
stuckStatusWatchdogTimer = setTimeout(function fatalStuckStatusHandler() {
reporter.panic({
id: `11701`,
context: {
activities: inProgressActivities(),
status: getStore().getState().logs.status,
stuckStatusDiagnosticMessage: generateStuckStatusDiagnosticMessage(),
stuckStatusWatchdogTimeoutDelay,
additionalOutput: generateAdditionalOutput(),
},
});
}, stuckStatusWatchdogTimeoutDelay);
}
}
}
};
}
//# sourceMappingURL=diagnostics.js.map
;