Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: remove hooks, add plugins #13

Merged
merged 1 commit into from
May 3, 2024
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
57 changes: 57 additions & 0 deletions .changeset/smooth-apricots-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
"loglayer": major
---

- Removes hooks and adds a plugin system where you can
define multiple hooks to run instead.
- Adds esm and cjs builds to the package

**Breaking Changes**

- The `hooks` option has been removed
- The `setHooks()` method has been removed
- A `plugins` option has been added
- An `addPlugins()` method has been added

*There will be a way to remove / disable specific plugins in a future release.*

**Migrating from 3.x to 4.x**

Your 3.x definition may look like this:

```
{
hooks: {
onBeforeDataOut: (data) => {
// do something with data
return data;
},
shouldSendToLogger: () => {
return true;
}
}
}
```

The 4.x version of this would look like this:

```typescript
{
plugins: [
{
onBeforeDataOut: (data) => {
// do something with data
return data;
},
shouldSendToLogger: () => {
return true;
}
}
]
}
```

Summary:

- Replace `hooks` with `plugins`
- For your existing hooks, move them into the `plugins` array where each entry is an object with the hook definition
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
uses: actions/checkout@v3

- name: Install Dependencies
run: npm install --frozen-lockfile
run: npm install --frozen-lockfile --prefer-offline

- name: Build workspace packages
run: npm run build
Expand Down
2 changes: 1 addition & 1 deletion .github/release.yml → .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v3

- name: Install Dependencies
run: npm install --frozen-lockfile
run: npm install --frozen-lockfile --prefer-offline

- name: Build workspace packages
run: npm run build
Expand Down
2 changes: 1 addition & 1 deletion .github/test.yml → .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
fetch-depth: 2

- name: Install Dependencies
run: npm install --frozen-lockfile
run: npm install --frozen-lockfile --prefer-offline

- name: Build workspace packages
run: npm run build
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,4 @@ lib/

build/
coverage/
dist/
3 changes: 3 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ livetests/
.eslintcache
.eslintrc.js
biome.json
build/
commitlint.config.js
tsup.config.json
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# loglayer

## 3.1.0

- Added new configuration option `muteContext` and `muteMetadata` to disable context and metadata logging.
Expand Down
140 changes: 88 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ logLayer
- [Serializing errors](#serializing-errors)
- [Data output options](#data-output-options)
- [Child logger](#child-logger)
- [Hooks](#hooks)
- [Set / update hooks outside of configuration](#set--update-hooks-outside-of-configuration)
- [Plugins](#plugins)
- [Add plugins outside of configuration](#add-plugins-outside-of-configuration)
- [Modify / create object data before being sent to the logging library](#modify--create-object-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)
- [Disable / enable logging](#disable--enable-logging)
Expand Down Expand Up @@ -373,37 +373,10 @@ interface LogLayerConfig {
*/
fieldName?: string
}
hooks?: {
/**
* Called after the assembly of the data object that contains
* the metadata / context / error data before being sent to the destination logging
* library.
*
* - The shape of `data` varies depending on your `fieldName` configuration
* for metadata / context / error. The metadata / context / error data is a *shallow* clone.
* - If data was not found for assembly, `undefined` is used as the `data` input.
* - You can also create your own object and return it to be sent to the logging library.
*
* @param Object [params.data] The object containing metadata / context / error data. This is `undefined` if there is no object with data.
* @param LogLevel [params.logLevel] The log level of the data.
*
* @returns [Object] The object to be sent to the destination logging
* library or null / undefined to not pass an object through.
*/
onBeforeDataOut?: HookAssembledDataFn
/**
* 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 / troubleshooting.
*
* @param MessageDataType[] messages An array of message data that corresponds to what was entered in
* info(...messages), warn(...messages), error(...messages), debug(...messages), etc.
* @param Object [data] The data object that contains the context / metadata / error data.
This is `undefined` if there is no data. If `onBeforeDataOut` was defined, uses the data processed from it.
*
* @returns [boolean] If true, sends data to the logger, if false does not.
*/
shouldSendToLogger?: HookShouldSendToLoggerFn
}
/**
* An array of plugins to be executed in the order they are defined.
*/
plugins?: Array<LogLayerPlugin>
}
```

Expand Down Expand Up @@ -542,27 +515,67 @@ const parentLog = new LogLayer(<config>).withContext({ some: 'data' })
const childLog = parentLog.child()
```

### Hooks
### Plugins

#### Set / update hooks outside of configuration
Plugins are executed in the order they are defined.

`LogLayer#setHooks(hooks: LogLayerHooksConfig)`
```typescript
export interface LogLayerPlugin {
/**
* If true, the plugin will skip execution
*/
disabled?: boolean;
onBeforeDataOut?(params: PluginBeforeDataOutParams): Record<string, any> | null | undefined;
shouldSendToLogger?(params: PluginShouldSendToLoggerParams): boolean;
}
```

The event lifecycle is as follows:

Update hook callback definitions. This is an alternative
to the `hooks` config option. Only hooks defined will be replaced.
1. `onBeforeDataOut` is called to modify the data object before it is sent to the logging library.
2. `shouldSendToLogger` is called to determine if the log entry should be sent to the logging library.

#### Add plugins outside of configuration

`LogLayer#addPlugins(plugins: Array<LogLayerPlugin>)`

This adds new plugins to the existing configuration.

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

`(data?: Record<string, any>) => Record<string, any> | null | undefined`
```typescript
export interface PluginBeforeDataOutParams {
/**
* Log level of the data
*/
logLevel: LogLevel;
/**
* The object containing metadata / context / error data. This
* is `undefined` if there is no object with data.
*/
data?: Record<string, any>;
}
```

`onBeforeDataOut(params: PluginBeforeDataOutParams) => Record<string, any> | null | undefined`

The callback `onBeforeDataOut` can be used to modify the data object
that contains the context / metadata / error data or create a custom object
before it is sent out to the logging library.

```typescript
import { LoggerType, LogLayer, HookAssembledDataFn } from 'loglayer'
Return `null` or `undefined` to not modify the data object.

Subsequent plugins will have the `data` property updated from the results of the previous plugin if a result was returned from it.

const onBeforeDataOut: HookAssembledDataFn = (data) => {
```typescript
import {
LoggerType,
LogLayer,
PluginBeforeDataOutFn,
PluginBeforeDataOutParams,
} from 'loglayer'

const onBeforeDataOut: PluginBeforeDataOutFn = (data: PluginBeforeDataOutParams) => {
if (data) {
data.modified = true
}
Expand All @@ -572,9 +585,9 @@ const onBeforeDataOut: HookAssembledDataFn = (data) => {

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

log.withContext({ test: 'data' }).info('this is a test message')
Expand All @@ -590,22 +603,45 @@ log.withContext({ test: 'data' }).info('this is a test message')

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

`(params: { messages: MessageDataType[], logLevel: LogLevel, data?: Data }) => boolean`
```typescript
export interface PluginShouldSendToLoggerParams {
/**
* Message data that is copied from the original.
*/
messages: MessageDataType[];
/**
* Log level of the message
*/
logLevel: LogLevel;
/**
* The object containing metadata / context / error data. This
* is `undefined` if there is no object with data.
*/
data?: Record<string, any>;
}
```

`shouldSendToLogger(params: PluginShouldSendToLoggerParams) => boolean`

The callback `shouldSendToLogger` is 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 / troubleshooting.
messages for debugging / troubleshooting. If multiple plugins are defined, all must return true for the log entry to be sent.

*Parameters*

- `messages`: The parameters sent via `info()`, `warn()`, `error()`, `debug()`, etc. Most will use `messages[0]`. This data is copied from the original.
- `[data]`: The data object that contains the context / metadata / error data. This is `null` if there is no data.
If `onBeforeDataOut` was defined, uses the data processed from it.
* If `onBeforeDataOut` was used, this will be the result of the data processed from all plugins that defined it.

```typescript
import { LoggerType, LogLayer, HookAssembledDataFn } from 'loglayer'

const shouldSendToLogger: boolean = ({ messages }) => {
import {
LoggerType,
LogLayer,
PluginShouldSendToLoggerFn,
PluginShouldSendToLoggerParams
} from 'loglayer'

const shouldSendToLogger: PluginShouldSendToLoggerFn = ({ messages }: PluginShouldSendToLoggerParams) => {
// Define custom logic here (ex: regex) to determine if the log should be sent out or not

// Read the first parameter of info() / warn() / error() / debug() / etc
Expand All @@ -618,9 +654,9 @@ const shouldSendToLogger: boolean = ({ messages }) => {

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

// Will not send the log entry to the logger
Expand Down
3 changes: 2 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
".turbo",
"build/*",
"coverage/*",
".pnpm-store/*"
".pnpm-store/*",
"dist/*"
]
},
"linter": {
Expand Down
15 changes: 0 additions & 15 deletions jest.config.js

This file was deleted.

Loading
Loading