Skip to content

Commit

Permalink
feat: add onBeforeMessageOut callback (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
theogravity authored Sep 8, 2024
1 parent 26ae15b commit 25212bd
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/tidy-crews-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"loglayer": minor
---

Add `onBeforeMessageOut` callback to plugins
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ logLayer
- [Remove a plugin](#remove-a-plugin)
- [Callbacks](#callbacks)
- [Modify / create object data before being sent to the logging library](#modify--create-object-data-before-being-sent-to-the-logging-library)
- [Modify / create message data before being sent to the logging library](#modify--create-message-data-before-being-sent-to-the-logging-library)
- [Conditionally send or not send an entry to the logging library](#conditionally-send-or-not-send-an-entry-to-the-logging-library)
- [Intercept metadata calls](#intercept-metadata-calls)
- [Mocking for tests](#mocking-for-tests)
Expand Down Expand Up @@ -943,6 +944,60 @@ log.withContext({ test: 'data' }).info('this is a test message')
}
```

##### Modify / create message data before being sent to the logging library

```typescript
export interface PluginBeforeMessageOutParams {
/**
* Log level of the message
*/
logLevel: LogLevel;
/**
* Message data that is copied from the original.
*/
messages: MessageDataType[];
}
```

`onBeforeMessageOut(params: PluginOnBeforeMessageOutParams) => MessageDataType[]`

Called after `onBeforeDataOut` and before `shouldSendToLogger`.
This allows you to modify the message data before it is sent to the destination logging library.

*Parameters*

- `messages`: The parameters sent via `info()`, `warn()`, `error()`, `debug()`, etc. Most will use `messages[0]`. This data is copied from the original.
- `logLevel`: The log level of the message.

```typescript
import {
LoggerType,
LogLayer,
PluginBeforeMessageOutParams,
PluginBeforeMessageOutFn,
} from 'loglayer'

const onBeforeMessageOut: PluginBeforeMessageOutFn = (params: PluginBeforeMessageOutParams) => {
return [params.messages[0], 'modified message']
}

const log = new LogLayer({
...
plugins: [{
onBeforeMessageOut,
}]
})

// Assuming your logging library supports multiple parameters and will interpret the %s
log.info('this is a test message: %s')
```

```json
{
"msg": "this is a test message: modified message"
}
```

##### Conditionally send or not send an entry to the logging library

```typescript
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

7 changes: 7 additions & 0 deletions src/LogLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,13 @@ export class LogLayer<ExternalLogger extends LoggerLibrary = LoggerLibrary, Erro
});
}

if (this.pluginManager.hasPlugins(PluginCallbackType.onBeforeMessageOut)) {
params = this.pluginManager.runOnBeforeMessageOut({
messages: [...params],
logLevel,
});
}

if (this.pluginManager.hasPlugins(PluginCallbackType.shouldSendToLogger)) {
const shouldSend = this.pluginManager.runShouldSendToLogger({
messages: [...params],
Expand Down
24 changes: 24 additions & 0 deletions src/__tests__/generic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,30 @@ describe("mute / unmute", () => {
});
});

describe("onBeforeMessageOut", () => {
it("should modify the message", () => {
const log = getLogger();
log.addPlugins([
{
onBeforeMessageOut: ({ messages }) => {
return ["Modified message"];
},
},
]);

const genericLogger = log.getLoggerInstance();

log.info("Test message");

expect(genericLogger.getLine()).toStrictEqual(
expect.objectContaining({
level: LogLevel.info,
data: ["Modified message"],
}),
);
});
});

describe("onMetadataCalled", () => {
describe("withMetadata", () => {
it("should modify the metadata", () => {
Expand Down
24 changes: 24 additions & 0 deletions src/plugins/PluginManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
type LogLayerPlugin,
type MessageDataType,
type PluginBeforeDataOutParams,
type PluginBeforeMessageOutParams,
PluginCallbackType,
type PluginShouldSendToLoggerParams,
} from "../types";
Expand All @@ -9,6 +11,7 @@ const CALLBACK_LIST = [
PluginCallbackType.onBeforeDataOut,
PluginCallbackType.onMetadataCalled,
PluginCallbackType.shouldSendToLogger,
PluginCallbackType.onBeforeMessageOut,
];

interface LogLayerPluginWithTimestamp extends LogLayerPlugin {
Expand All @@ -21,6 +24,7 @@ export class PluginManager<Data extends Record<string, any> = Record<string, any
private onBeforeDataOut: Array<string> = [];
private shouldSendToLogger: Array<string> = [];
private onMetadataCalled: Array<string> = [];
private onBeforeMessageOut: Array<string> = [];

constructor(plugins: Array<LogLayerPlugin>) {
this.idToPlugin = {};
Expand All @@ -47,6 +51,7 @@ export class PluginManager<Data extends Record<string, any> = Record<string, any
this.onBeforeDataOut = [];
this.shouldSendToLogger = [];
this.onMetadataCalled = [];
this.onBeforeMessageOut = [];

const pluginList = Object.values(this.idToPlugin).sort((a, b) => a.registeredAt - b.registeredAt);

Expand Down Expand Up @@ -171,4 +176,23 @@ export class PluginManager<Data extends Record<string, any> = Record<string, any

return data;
}

runOnBeforeMessageOut(params: PluginBeforeMessageOutParams): MessageDataType[] {
let messages = [...params.messages];

for (const pluginId of this.onBeforeMessageOut) {
const plugin = this.idToPlugin[pluginId];

const result = plugin.onBeforeMessageOut!({
messages: messages,
logLevel: params.logLevel,
});

if (result) {
messages = result;
}
}

return messages;
}
}
22 changes: 22 additions & 0 deletions src/plugins/__tests__/PluginManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { beforeEach, describe, expect, it, vi, vitest } from "vitest";
import {
type LogLayerPlugin,
LogLevel,
MessageDataType,
type PluginBeforeDataOutParams,
type PluginBeforeMessageOutParams,
PluginCallbackType,
type PluginShouldSendToLoggerParams,
} from "../../types";
Expand Down Expand Up @@ -88,6 +90,26 @@ describe("PluginManager", () => {
expect(plugins[1].onBeforeDataOut).toHaveBeenCalledOnce();
});

it("runs onBeforeMessageOut and properly respects plugin responses", () => {
pluginManager.addPlugins([
{
id: "message-1",
onBeforeMessageOut: ({ messages }) => {
return [messages[0], "Modified message"];
},
},
]);

const initialParams: PluginBeforeMessageOutParams = {
logLevel: LogLevel.error,
messages: ["Test message"],
};

const result = pluginManager.runOnBeforeMessageOut(initialParams);

expect(result).toEqual(["Test message", "Modified message"]);
});

it("runs shouldSendToLogger and properly respects plugin responses", () => {
const params: PluginShouldSendToLoggerParams = {
logLevel: LogLevel.error,
Expand Down
23 changes: 23 additions & 0 deletions src/types/plugins.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ export interface PluginShouldSendToLoggerParams {
}

export type PluginShouldSendToLoggerFn = (params: PluginShouldSendToLoggerParams) => boolean;

export interface PluginBeforeMessageOutParams {
/**
* Log level of the message
*/
logLevel: LogLevel;
/**
* Message data that is copied from the original.
*/
messages: MessageDataType[];
}

export type PluginBeforeMessageOutFn = (params: PluginBeforeMessageOutParams) => MessageDataType[];

export type PluginOnMetadataCalledFn = (metadata: Record<string, any>) => Record<string, any> | null | undefined;

export interface LogLayerPlugin {
Expand Down Expand Up @@ -59,6 +73,14 @@ export interface LogLayerPlugin {
*/
onBeforeDataOut?(params: PluginBeforeDataOutParams): Record<string, any> | null | undefined;

/**
* Called after `onBeforeDataOut` and before `shouldSendToLogger`.
* This allows you to modify the message data before it is sent to the destination logging library.
*
* @returns [Array] The message data to be sent to the destination logging library.
*/
onBeforeMessageOut?(params: PluginBeforeMessageOutParams): MessageDataType[] | null | undefined;

/**
* Called before the data is sent to the logger. Return false to omit sending
* to the logger. Useful for isolating specific log messages for debugging /
Expand Down Expand Up @@ -92,4 +114,5 @@ export enum PluginCallbackType {
onBeforeDataOut = "onBeforeDataOut",
shouldSendToLogger = "shouldSendToLogger",
onMetadataCalled = "onMetadataCalled",
onBeforeMessageOut = "onBeforeMessageOut",
}

0 comments on commit 25212bd

Please sign in to comment.