@azure/monitor-opentelemetry
Version:
Azure Monitor OpenTelemetry (Node.js)
223 lines • 9.13 kB
JavaScript
// 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