Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/Security-Reachability.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:
# validate:package:skip-reachability command so workflow validation does not modify its checkout.
- name: Install Socket CLI
background: true
run: sfw pip install socketsecurity==2.9.2 uv --upgrade
run: sfw pip install socketsecurity==2.9.6 uv --upgrade

# Bring job back to sync execution by awaiting for all async jobs to finish before continuing
- name: Steps - Convert Back To Synchronous Execution - Packages Updates/Setup
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,4 @@ dist
/bin
coverage-output.txt
coverage-summary.md
/.idea
36 changes: 36 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Build Watch",
"type": "node-terminal",
"request": "launch",
"command": "npx tspc -p coverage.tsconfig.json --watch"
},
{
"name": "Vitest Debug",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/vitest/vitest.mjs",
"args": [
"Logger.test.ts",
"--inspect-brk",
"--no-file-parallelism"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"skipFiles": [
"<node_internals>/**"
]
}
],
"compounds": [
{
"name": "Watch + Vitest",
"configurations": [
"Build Watch",
"Vitest Debug"
]
}
]
}
21 changes: 11 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"version": "0.0.9",
"version": "0.0.10",
"name": "@software-hardware-integration-lab/log-engine",
"description": "Logging engine with ability to add plugins for any destination",
"author": {
"name": "Michael Howard",
"email": "Michael_Howard@SHI.com"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/Software-Hardware-Integration-Lab/Log-Engine.git"
Expand Down Expand Up @@ -35,7 +36,7 @@
"prepack": "npm run validate:package",
"postinstall": "ts-patch install -s"
},
"packageManager": "npm@12.0.2",
"packageManager": "npm@12.1.0",
"devEngines": {
"runtime": {
"name": "node",
Expand All @@ -53,9 +54,9 @@
"devDependencies": {
"@azure/storage-blob": "^12.33.0",
"@software-hardware-integration-lab/development-utilities": "~3.0.0",
"@types/node": "~26.5.0",
"@types/node": "~26.6.2",
"@vitest/coverage-v8": "~5.0.1",
"baseline-browser-mapping": "^2.11.24",
"baseline-browser-mapping": "^2.11.25",
"ts-patch": "~4.0.1",
"typescript": "~6.0.3",
"vitest": "~5.0.1"
Expand Down
81 changes: 81 additions & 0 deletions src/Logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { LogLevel } from './interfaces/LogEngine.js';
import { LogEngine } from './LogEngine.js';

export const Logger = {
/**
* Creates a log message with the `Trace` log level.
* @param message The message to log.
* @param additionalContext An optional additional context object to append to the log.
*/
trace(message: string, additionalContext?: string | number | Date): void {
LogEngine.getInstance().log({
additionalContext,
'level': LogLevel.Trace,
message
});
},
/**
* Creates a log message with the `Debug` log level.
* @param message The message to log.
* @param additionalContext An optional additional context object to append to the log.
*/
debug(message: string, additionalContext?: string | number | Date): void {
LogEngine.getInstance().log({
additionalContext,
'level': LogLevel.Debug,
message
});
},
/**
* Creates a log message with the `Information` log level.
* @param message The message to log.
* @param additionalContext An optional additional context object to append to the log.
*/
info(message: string, additionalContext?: string | number | Date): void {
LogEngine.getInstance().log({
additionalContext,
'level': LogLevel.Information,
message
});
},
/**
* Creates a log message with the `Warn` log level.
* @param message The message to log.
* @param additionalContext An optional additional context object to append to the log.
*/
warn(message: string, additionalContext?: string | number | Date): void {
LogEngine.getInstance().log({
additionalContext,
'level': LogLevel.Warning,
message
});
},
/**
* Creates a log message with the `Error` log level.
* @param message The message to log.
* @param error The error to log alongside the message.
* @param additionalContext An optional additional context object to append to the log.
*/
error(message: string, error?: Error, additionalContext?: string | number | Date): void {
LogEngine.getInstance().log({
additionalContext,
'level': LogLevel.Error,
message,
'stack': error?.stack
});
},
/**
* Creates a log message with the `Critical` log level.
* @param message The message to log.
* @param error The error to log alongside the message.
* @param additionalContext An optional additional context object to append to the log.
*/
crit(message: string, error?: Error, additionalContext?: string | number | Date): void {
LogEngine.getInstance().log({
additionalContext,
'level': LogLevel.Critical,
message,
'stack': error?.stack
});
}
};
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ export type {
} from './interfaces/plugins/AzureStorageDestination.js';

export type { LoggingPluginContract } from './interfaces/plugins/LoggingPlugin.js';

export type { Logger } from './Logger.js';
13 changes: 12 additions & 1 deletion src/interfaces/plugins/ConsoleDestination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,22 @@ export type ConsoleDestinationLogLevelMap = Partial<Record<LogLevel, ConsoleDest
export interface ConsoleDestinationOptions extends LoggingPluginConfigurationOptions {
/** Per-level console method overrides for operational log output. */
'logLevelToConsoleMethod'?: ConsoleDestinationLogLevelMap;
/**
* Flag to dictate if timestamp should be shown on logs. Best turned off if your console already has timestamps.
* Defaults to true.
*/
'enableTimestamps'?: boolean;
}

/** Fully resolved options used internally by the console logging destination. */
export interface ResolvedConsoleDestinationOptions extends LoggingPluginConfigurationOptions {
/** Fully resolved level-to-console-method routing for operational log output. */
'logLevelToConsoleMethod': Record<LogLevel, ConsoleDestinationMethod>;
/**
* Flag to dictate if timestamp should be shown on logs. Best turned off if your console already has timestamps.
* Defaults to true.
*/
'enableTimestamps': boolean;
}

/** Default level-to-console-method routing used by the console destination. */
Expand All @@ -32,5 +42,6 @@ export const DEFAULT_CONSOLE_DESTINATION_LOG_LEVEL_MAP: Record<LogLevel, Console
/** Default configuration values used by the console destination. */
export const DEFAULT_CONSOLE_DESTINATION_OPTIONS: ResolvedConsoleDestinationOptions = {
...DEFAULT_LOGGING_PLUGIN_CONFIGURATION_OPTIONS,
'logLevelToConsoleMethod': { ...DEFAULT_CONSOLE_DESTINATION_LOG_LEVEL_MAP }
'logLevelToConsoleMethod': { ...DEFAULT_CONSOLE_DESTINATION_LOG_LEVEL_MAP },
'enableTimestamps': true
};
65 changes: 57 additions & 8 deletions src/plugins/ConsoleDestination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,9 @@ export class ConsoleDestination extends LoggingPlugin {
return Promise.resolve();
}

/** The UTC timestamp of the log creation time, formatted as 'YYYY-MM-DD HH:mm:ss.sss'. */
const createdUtc = ConsoleDestination.#formatTimestamp(log.timeGenerated);

/** The formatted operational log message for console output. */
const logMessage = `${ createdUtc }: ${ ConsoleDestination.#getLogLevelName(log.level) } ${ log.message }`;
const method = this.#getConsoleMethod(log.level);

ConsoleDestination.#writeToConsole(this.#getConsoleMethod(log.level), logMessage, log);
ConsoleDestination.#writeToConsole(method, this.#formatOperationalLog(log));

return Promise.resolve();
}
Expand Down Expand Up @@ -153,7 +149,8 @@ export class ConsoleDestination extends LoggingPlugin {
'logLevelToConsoleMethod': {
...DEFAULT_CONSOLE_DESTINATION_LOG_LEVEL_MAP,
...options.logLevelToConsoleMethod ?? {}
}
},
'enableTimestamps': options.enableTimestamps ?? DEFAULT_CONSOLE_DESTINATION_OPTIONS.enableTimestamps
};

return resolvedOptions;
Expand Down Expand Up @@ -193,8 +190,60 @@ export class ConsoleDestination extends LoggingPlugin {
* @param message The formatted console message.
* @param log The payload to log alongside the message.
*/
static #writeToConsole(method: ConsoleDestinationMethod, message: string, log: AuditLog | OperationalLog): void {
static #writeToConsole(method: ConsoleDestinationMethod, message: string, log?: AuditLog | OperationalLog): void {
// eslint-disable-next-line no-console
console[method](message, log);
}

#formatOperationalLog(log: OperationalLog): string {
let output = '';

/*
*Const createdUtc = ConsoleDestination.#formatTimestamp(log.timeGenerated);
*const logMessage = `${ createdUtc }: ${ ConsoleDestination.#getLogLevelName(log.level) } ${ log.message }`;
*/

if (this.#appliedOptions.enableTimestamps) {
output += `${ ConsoleDestination.#formatTimestamp(log.timeGenerated) }: `;
}

output += `${ ConsoleDestination.#getLogLevelName(log.level) } | ${ log.message } | correlationId: ${ log.correlationId } | userId: ${ log.userId }`;

if (log.requestId) { output += ` | requestId: ${ log.requestId } `; }

if (log.tenantId) { output += ` | tenantId: ${ log.tenantId } `; }

switch (typeof log.additionalContext) {
case 'string':
try {
const parsed = JSON.parse(log.additionalContext) as Record<string, unknown>;

for (const [key, value] of Object.entries(parsed)) {
output += ` | ${ key }: ${ String(value) } `;
}
} catch {
output += ` | additionalContext: ${ log.additionalContext } `;
}

break; case 'number':
output += ` | additionalContext: ${ log.additionalContext } `;

break; case 'object':
if (log.additionalContext instanceof Date) {
output += ` | additionalContext: ${ log.additionalContext.toISOString() } `;
}

break;
case 'undefined':
default:
// Nothing yet
break;
}

if (log.stack) {
output += `\n${ log.stack }`;
}

return output.trimEnd();
}
}
Loading
Loading