UNPKG

@azure/monitor-opentelemetry

Version:
223 lines 9.13 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { accessAsync, appendFileAsync, confirmDirExists, getShallowFileSize, readdirAsync, readFileAsync, writeFileAsync, unlinkAsync, } from "../../utils/index.js"; export class DiagFileConsoleLogger { _TAG = "DiagFileConsoleLogger:"; _cleanupTimeOut = 60 * 30 * 1000; // 30 minutes; _fileCleanupTimer = null; _tempDir; _logFileName; _fileFullPath; _backUpNameFormat; _logToFile = false; _logToConsole = true; _maxHistory; _maxSizeBytes; _logDestination; constructor() { this._logDestination = process.env.APPLICATIONINSIGHTS_LOG_DESTINATION; // destination can be one of file, console or file+console if (this._logDestination === "file+console") { this._logToFile = true; } if (this._logDestination === "file") { this._logToFile = true; this._logToConsole = false; } this._maxSizeBytes = 50000; this._maxHistory = 1; this._logFileName = "applicationinsights.log"; // If custom path not provided use temp folder, /tmp for *nix and USERDIR/AppData/Local/Temp for Windows const logFilePath = process.env.APPLICATIONINSIGHTS_LOGDIR; if (!logFilePath) { this._tempDir = path.join(os.tmpdir(), "appInsights-node"); } else { if (path.isAbsolute(logFilePath)) { this._tempDir = logFilePath; } else { this._tempDir = path.join(process.cwd(), logFilePath); } } this._fileFullPath = path.join(this._tempDir, this._logFileName); this._backUpNameFormat = `.${this._logFileName}`; // {currentime}.applicationinsights.log if (this._logToFile) { if (!this._fileCleanupTimer) { this._fileCleanupTimer = setInterval(() => { // eslint-disable-next-line @typescript-eslint/no-floating-promises this._fileCleanupTask(); }, this._cleanupTimeOut); this._fileCleanupTimer.unref(); } } } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types error(message, ...args) { // Filter out warnings about accessing resource attributes before async attributes are settled if (this._shouldFilterResourceAttributeWarning(message, args)) { return; } // eslint-disable-next-line @typescript-eslint/no-floating-promises this.logMessage(message, args); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types warn(message, ...args) { // Filter out warnings about accessing resource attributes before async attributes are settled if (this._shouldFilterResourceAttributeWarning(message, args)) { return; } // eslint-disable-next-line @typescript-eslint/no-floating-promises this.logMessage(message, args); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types info(message, ...args) { // eslint-disable-next-line @typescript-eslint/no-floating-promises this.logMessage(message, args); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types debug(message, ...args) { // eslint-disable-next-line @typescript-eslint/no-floating-promises this.logMessage(message, args); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types verbose(message, ...args) { // eslint-disable-next-line @typescript-eslint/no-floating-promises this.logMessage(message, args); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async logMessage(message, ...optionalParams) { try { const args = message ? [message, ...optionalParams] : optionalParams; if (this._logToFile) { await this._storeToDisk(args); } if (this._logToConsole) { // eslint-disable-next-line no-console console.log(...args); } } catch (err) { // eslint-disable-next-line no-console console.log(this._TAG, `Failed to log to file: ${err && err.message}`); } } /** * Checks if the warning message should be filtered out to avoid showing * non-actionable warnings to customers */ _shouldFilterResourceAttributeWarning(message, args) { const messagesToFilter = [ "accessing resource attributes before async attributes settled", "resource attributes being accessed before async attributes finished", "async attributes settled", "resource attributes accessed before async detection completed", "module @azure/core-tracing has been loaded before @azure/opentelemetry-instrumentation-azure-sdk", ]; if (typeof message === "string") { if (messagesToFilter.some((filterText) => message.toLowerCase().includes(filterText))) { return true; } } // Check if the message is in the args array if (args && Array.isArray(args)) { for (const arg of args) { if (typeof arg === "string") { if (messagesToFilter.some((filterText) => arg.toLowerCase().includes(filterText))) { return true; } } } } // Also check if message starts with the warning text (in case it's formatted differently) if (typeof message === "string") { const messageParts = message.split(" "); if (messageParts.length >= 3 && messageParts[0].toLowerCase() === "accessing" && messageParts[1].toLowerCase() === "resource" && messageParts[2].toLowerCase() === "attributes") { return true; } } return false; } async _storeToDisk(args) { const data = `${args}\r\n`; try { await confirmDirExists(this._tempDir); } catch (err) { // eslint-disable-next-line no-console console.log(this._TAG, `Failed to create directory for log file: ${err && err.message}`); return; } try { await accessAsync(this._fileFullPath, fs.constants.F_OK); } catch (err) { // No file create one try { await appendFileAsync(this._fileFullPath, data); } catch (appendError) { // eslint-disable-next-line no-console console.log(this._TAG, `Failed to put log into file: ${appendError && appendError.message}`); return; } } // Check size const size = await getShallowFileSize(this._fileFullPath); if (size && size > this._maxSizeBytes) { await this._createBackupFile(data); } else { await appendFileAsync(this._fileFullPath, data); } } async _createBackupFile(data) { try { const buffer = await readFileAsync(this._fileFullPath); const backupPath = path.join(this._tempDir, `${new Date().getTime()}.${this._logFileName}`); await writeFileAsync(backupPath, buffer); } catch (err) { // eslint-disable-next-line no-console console.log("Failed to generate backup log file", err); } finally { // Store logs await writeFileAsync(this._fileFullPath, data); } } async _fileCleanupTask() { try { let files = await readdirAsync(this._tempDir); // Filter only backup files files = files.filter((f) => path.basename(f).indexOf(this._backUpNameFormat) > -1); // Sort by creation date files.sort((a, b) => { // Check expiration const aCreationDate = new Date(parseInt(a.split(this._backUpNameFormat)[0])); const bCreationDate = new Date(parseInt(b.split(this._backUpNameFormat)[0])); if (aCreationDate < bCreationDate) { return -1; } else { return 1; } }); const totalFiles = files.length; for (let i = 0; i < totalFiles - this._maxHistory; i++) { const pathToDelete = path.join(this._tempDir, files[i]); await unlinkAsync(pathToDelete); } } catch (err) { // eslint-disable-next-line no-console console.log(this._TAG, `Failed to cleanup log files: ${err && err.message}`); } } } //# sourceMappingURL=diagFileConsoleLogger.js.map