Skip to content
Draft
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 .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
build/
node_modules/
coverage/
build/
.vscode/
.nyc_output/
1,096 changes: 610 additions & 486 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cf-nodejs-logging-support",
"version": "7.4.2",
"version": "7.5.2-beta.0",
"description": "Logging tool for Cloud Foundry",
"keywords": [
"logging",
Expand Down Expand Up @@ -61,6 +61,7 @@
"README.md"
],
"dependencies": {
"@opentelemetry/api-logs": "^0.51.0",
"ajv": "^8.11.0",
"json-stringify-safe": "^5.0.1",
"jsonwebtoken": "^9.0.3",
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ exports = module.exports; // re-assign exports

export default rootLogger;
export * from "./lib/config/interfaces";
export * from "./lib/logger/record";
export * from "./lib/logger/level";
export * from "./lib/logger/logger";
export * from "./lib/plugins/interfaces";
export * from "./lib/plugins/defaultOutput";
export * from "./lib/plugins/otelOutput";
3 changes: 3 additions & 0 deletions src/lib/config/default/config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,11 @@
},
"DetailName": {
"enum": [
"errorMessage",
"errorName",
"level",
"message",
"rawStacktrace",
"requestReceivedAt",
"responseSentAt",
"responseTimeMs",
Expand Down
3 changes: 3 additions & 0 deletions src/lib/config/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ export enum DetailName {
WrittenTs = "writtenTs",
Message = "message",
Stacktrace = "stacktrace",
RawStacktrace = "rawStacktrace",
ErrorName = "errorName",
ErrorMessage = "errorMessage",
Level = "level"
}

Expand Down
24 changes: 0 additions & 24 deletions src/lib/helper/levelUtils.ts

This file was deleted.

31 changes: 31 additions & 0 deletions src/lib/helper/pluginProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { OutputPlugin } from "../plugins/interfaces";

export default class PluginProvider {
private static instance: PluginProvider;
private outputPlugins: OutputPlugin[];

private constructor() {
this.outputPlugins = [];
}

static getInstance(): PluginProvider {
if (!PluginProvider.instance) {
PluginProvider.instance = new PluginProvider();
}
return PluginProvider.instance;
}

addOutputPlugin(outputPlugin: OutputPlugin) {
this.outputPlugins.push(outputPlugin);
}

setOutputPlugins(outputPlugins: OutputPlugin[]) {
this.outputPlugins = outputPlugins;
}

getOutputPlugins(): OutputPlugin[] {
return this.outputPlugins;
}
}


26 changes: 26 additions & 0 deletions src/lib/logger/level.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,29 @@ export enum Level {
Debug = 4,
Silly = 5
}

export class LevelUtils {
private static readonly defaultLevel: Level = Level.Info

static getLevel(level: String | Level): Level {
if (typeof level === 'string') {
const key = level.charAt(0).toUpperCase() + level.slice(1).toLowerCase();
const lvl: Level = Level[key as keyof typeof Level]
if (lvl !== undefined) {
return lvl;
}
} else {
return level as Level
}
return LevelUtils.defaultLevel;
}

static getName(level: Level): string {
return Level[level].toLowerCase()
}

static isLevelEnabled(threshold: Level, level: Level) {
if (level <= Level.Off) return false;
return level <= threshold
}
}
32 changes: 7 additions & 25 deletions src/lib/logger/logger.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import LevelUtils from '../helper/levelUtils';
import { isValidObject } from '../middleware/utils';
import { Level } from './level';
import { Level, LevelUtils } from './level';
import RecordFactory from './recordFactory';
import RecordWriter from './recordWriter';
import Context from './context';
import PluginProvider from '../helper/pluginProvider';

export class Logger {
private parent?: Logger = undefined
private context?: Context;
private registeredCustomFields: Array<string> = [];
private customFields: Map<string, any> = new Map<string, any>()
private recordFactory: RecordFactory;
private recordWriter: RecordWriter;

protected loggingLevelThreshold: Level = Level.Inherit

constructor(parent?: Logger, context?: Context) {
Expand All @@ -23,7 +22,6 @@ export class Logger {
this.context = context;
}
this.recordFactory = RecordFactory.getInstance();
this.recordWriter = RecordWriter.getInstance();
}

createLogger(customFields?: Map<string, any> | Object, createNewContext?: boolean): Logger {
Expand All @@ -37,11 +35,7 @@ export class Logger {
}

setLoggingLevel(level: string | Level) {
if (typeof level === 'string') {
this.loggingLevelThreshold = LevelUtils.getLevel(level)
} else {
this.loggingLevelThreshold = level
}
this.loggingLevelThreshold = LevelUtils.getLevel(level)
}

getLoggingLevel(): string {
Expand All @@ -55,26 +49,14 @@ export class Logger {
if (this.loggingLevelThreshold == Level.Inherit) {
return this.parent!.isLoggingLevel(level)
}
if (typeof level === 'string') {
return LevelUtils.isLevelEnabled(this.loggingLevelThreshold, LevelUtils.getLevel(level))
} else {
return LevelUtils.isLevelEnabled(this.loggingLevelThreshold, level)
}
return LevelUtils.isLevelEnabled(this.loggingLevelThreshold, LevelUtils.getLevel(level))
}

logMessage(level: string | Level, ...args: any) {
if (!this.isLoggingLevel(level)) return;
const loggerCustomFields = this.getCustomFieldsFromLogger(this);

let levelName: string;
if (typeof level === 'string') {
levelName = level;
} else {
levelName = LevelUtils.getName(level);
}

const record = this.recordFactory.buildMsgRecord(this.registeredCustomFields, loggerCustomFields, levelName, args, this.context);
this.recordWriter.writeLog(record);
const record = this.recordFactory.buildMsgRecord(this.registeredCustomFields, loggerCustomFields, LevelUtils.getLevel(level), args, this.context);
PluginProvider.getInstance().getOutputPlugins().forEach(output => { output.writeRecord(record) })
}

error(...args: any) {
Expand Down
39 changes: 31 additions & 8 deletions src/lib/logger/record.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
export default class Record {
payload: any
metadata: any
import { Level } from "./level"

constructor(level: string) {
export class Record {
payload: { [key: string]: any }
metadata: RecordMetadata

constructor(type: RecordType, level: Level) {
this.payload = {}
this.metadata = {
level: level
}
this.metadata = new RecordMetadata(type, level)
}
}

export class RecordMetadata {
type: RecordType
level: Level
message?: string
rawStacktrace?: string
stacktrace?: string[]
errorName?: string
errorMessage?: string
customFieldNames: string[]

constructor(type: RecordType, level: Level) {
this.type = type
this.level = level
this.customFieldNames = new Array<string>()
}
}
}

export type RecordFieldValue = string | string[] | number | boolean

export enum RecordType {
Request, Message
}
27 changes: 18 additions & 9 deletions src/lib/logger/recordFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { CustomFieldsFormat, CustomFieldsTypeConversion, Output } from '../confi
import StacktraceUtils from '../helper/stacktraceUtils';
import { isValidObject } from '../middleware/utils';
import Cache from './cache';
import Record from './record';
import { Record, RecordType } from './record';
import Context from './context';
import SourceUtils from './sourceUtils';
import { Level } from './level';

export default class RecordFactory {

Expand All @@ -34,18 +35,24 @@ export default class RecordFactory {
}

// init a new record and assign fields with output "msg-log"
buildMsgRecord(registeredCustomFields: Array<string>, loggerCustomFields: Map<string, any>, levelName: string, args: Array<any>, context?: Context): Record {
buildMsgRecord(registeredCustomFields: Array<string>, loggerCustomFields: Map<string, any>, level: Level, args: Array<any>, context?: Context): Record {
const lastArg = args[args.length - 1];
let customFieldsFromArgs = new Map<string, any>();
let record = new Record(levelName)
let record = new Record(RecordType.Message, level)


if (typeof lastArg === "object") {
if (this.stacktraceUtils.isErrorWithStacktrace(lastArg)) {
record.metadata.stacktrace = this.stacktraceUtils.prepareStacktrace(lastArg.stack);
record.metadata.rawStacktrace = lastArg.stack
record.metadata.errorMessage = lastArg.message
record.metadata.errorName = lastArg.name
} else if (isValidObject(lastArg)) {
if (this.stacktraceUtils.isErrorWithStacktrace(lastArg._error)) {
record.metadata.stacktrace = this.stacktraceUtils.prepareStacktrace(lastArg._error.stack);
record.metadata.rawStacktrace = lastArg._error.stack
record.metadata.errorMessage = lastArg._error.message
record.metadata.errorName = lastArg._error.name
delete lastArg._error;
}
customFieldsFromArgs = new Map<string, any>(Object.entries(lastArg));
Expand Down Expand Up @@ -77,8 +84,8 @@ export default class RecordFactory {
}

// init a new record and assign fields with output "req-log"
buildReqRecord(levelName: string, req: any, res: any, context: Context): Record {
let record = new Record(levelName)
buildReqRecord(level: Level, req: any, res: any, context: Context): Record {
let record = new Record(RecordType.Request, level)

// assign static fields from cache
const cacheFields = this.config.getCacheReqFields();
Expand All @@ -88,7 +95,7 @@ export default class RecordFactory {
// assign dynamic fields
this.addDynamicFields(record, Output.ReqLog, req, res);

// assign values request context
// assign values request context
this.addContext(record, context);

// assign custom fields
Expand All @@ -98,7 +105,7 @@ export default class RecordFactory {
return record;
}

private addCustomFields(record: Record, registeredCustomFields: Array<string>, loggerCustomFields: Map<string, any>,
private addCustomFields(record: Record, registeredCustomFields: Array<string>, loggerCustomFields: Map<string, any>,
customFieldsFromArgs: Map<string, any> = new Map()) {
const providedFields = new Map<string, any>([...loggerCustomFields, ...customFieldsFromArgs]);
const customFieldsFormat = this.config.getConfig().customFieldsFormat!;
Expand Down Expand Up @@ -129,6 +136,8 @@ export default class RecordFactory {
indexedCustomFields[key] = value;
}
}

record.metadata.customFieldNames.push(key)
});

// Write custom fields in the correct order and correlates i to the place in registeredCustomFields
Expand Down
33 changes: 0 additions & 33 deletions src/lib/logger/recordWriter.ts

This file was deleted.

Loading