@loglayer/plugin-filter 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Theo Gravity
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # Log filtering plugin for LogLayer
2
+
3
+ A plugin for [LogLayer](https://loglayer.dev) that filters log messages. You can filter logs using string patterns, regular expressions,
4
+ or [JSON Queries](https://jsonquerylang.org/).
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ npm install @loglayer/plugin-filter
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```typescript
15
+ import { filterPlugin } from '@loglayer/plugin-filter';
16
+
17
+ // Create a filter that only allows error messages
18
+ const filter = filterPlugin({
19
+ // checks the assembled message using an includes()
20
+ messages: ['error'],
21
+ });
22
+
23
+ // Checks the level of the log
24
+ const levelFilter = filterPlugin({
25
+ queries: ['.level == "error" or .level == "warn"'],
26
+ });
27
+ ```
28
+
29
+ ### Configuration
30
+
31
+ The plugin accepts the following configuration options:
32
+
33
+ | Option | Type | Description |
34
+ |--------|------|-----------------------------------------------------------------------------------------------------------------------------------|
35
+ | `messages` | `Array<string \| RegExp>` | Optional. Array of string patterns or regular expressions to match against log messages |
36
+ | `queries` | `string[]` | Optional. Array of JSON queries to filter logs. A JSON Query `filter()` is applied, which each item being part of an OR condition |
37
+ | `debug` | `boolean` | Optional. Enable debug mode for troubleshooting |
38
+ | `disabled` | `boolean` | Optional. Disable the plugin |
39
+
40
+ ## Documentation
41
+
42
+ For more details, visit [https://loglayer.dev/plugins/filter](https://loglayer.dev/plugins/filter)
package/dist/index.cjs ADDED
@@ -0,0 +1,145 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/FilterExecutor.ts
2
+ var _jsonquery = require('@jsonquerylang/jsonquery');
3
+ var FilterExecutor = class {
4
+
5
+
6
+ /**
7
+ * Creates a new FilterExecutor instance.
8
+ *
9
+ * @param config - The filter plugin configuration
10
+ */
11
+ constructor(config) {
12
+ this.config = config;
13
+ this.context = {
14
+ message: "",
15
+ logLevel: "",
16
+ data: {},
17
+ debugItems: []
18
+ };
19
+ }
20
+ /**
21
+ * Resets the context for a new log message check.
22
+ *
23
+ * @param params - The log message parameters
24
+ */
25
+ resetContext(params) {
26
+ this.context = {
27
+ message: _optionalChain([params, 'access', _ => _.messages, 'optionalAccess', _2 => _2.join, 'call', _3 => _3(" ")]) || "",
28
+ logLevel: params.logLevel,
29
+ data: params.data || {},
30
+ debugItems: ["[filter-plugin] ================================="]
31
+ };
32
+ }
33
+ /**
34
+ * Prints debug information if debug mode is enabled.
35
+ */
36
+ printDebugItems() {
37
+ if (this.config.debug) {
38
+ for (const item of this.context.debugItems) {
39
+ console.log(item);
40
+ }
41
+ }
42
+ }
43
+ /**
44
+ * Checks if the current message matches any of the configured patterns.
45
+ * Supports both string and RegExp patterns.
46
+ *
47
+ * @returns true if any pattern matches, false otherwise
48
+ */
49
+ checkMessagePatterns() {
50
+ if (!_optionalChain([this, 'access', _4 => _4.config, 'access', _5 => _5.messages, 'optionalAccess', _6 => _6.length])) {
51
+ return false;
52
+ }
53
+ if (this.config.debug) {
54
+ this.context.debugItems.push(`[filter-plugin] message: ${this.context.message}`);
55
+ }
56
+ for (const pattern of this.config.messages) {
57
+ if (this.config.debug) {
58
+ this.context.debugItems.push(`[filter-plugin] pattern: ${pattern}`);
59
+ }
60
+ const matches = typeof pattern === "string" ? this.context.message.includes(pattern) : pattern.test(this.context.message);
61
+ if (matches) {
62
+ this.context.debugItems.push("[filter-plugin] pattern match: true");
63
+ return true;
64
+ }
65
+ this.context.debugItems.push("[filter-plugin] pattern match: false");
66
+ }
67
+ return false;
68
+ }
69
+ /**
70
+ * Checks if the current message matches any of the configured queries.
71
+ * Uses @jsonquerylang/jsonquery for query execution.
72
+ *
73
+ * @returns true if any query matches, false otherwise
74
+ */
75
+ checkQueries() {
76
+ if (!_optionalChain([this, 'access', _7 => _7.config, 'access', _8 => _8.queries, 'optionalAccess', _9 => _9.length])) {
77
+ return false;
78
+ }
79
+ const query = this.config.queries.map((q) => `(${q.replaceAll(`'`, `"`)})`).join(" or ");
80
+ const queryContext = {
81
+ level: this.context.logLevel,
82
+ message: this.context.message,
83
+ data: this.context.data
84
+ };
85
+ try {
86
+ if (this.config.debug) {
87
+ this.context.debugItems.push(`[filter-plugin] query: filter(${query})`);
88
+ this.context.debugItems.push(`[filter-plugin] input: ${JSON.stringify(queryContext)}`);
89
+ }
90
+ const output = _jsonquery.jsonquery.call(void 0, [queryContext], `filter(${query})`);
91
+ if (this.config.debug) {
92
+ this.context.debugItems.push(`[filter-plugin] query match: ${output.length > 0}`);
93
+ }
94
+ return output.length > 0;
95
+ } catch (e) {
96
+ console.error(`[filter-plugin] Error: ${e}`);
97
+ console.log(`[filter-plugin] query: filter(${query})`);
98
+ console.log(`[filter-plugin] input: ${JSON.stringify(queryContext)}`);
99
+ return false;
100
+ }
101
+ }
102
+ /**
103
+ * Checks if a log message should be allowed based on the configured filters.
104
+ *
105
+ * The filtering logic is:
106
+ * 1. If no filters defined, allow all logs
107
+ * 2. If message patterns match, allow the log
108
+ * 3. If queries match, allow the log
109
+ * 4. Otherwise, filter out the log
110
+ *
111
+ * @param params - The log message parameters
112
+ * @returns true if the log should be allowed, false otherwise
113
+ */
114
+ check(params) {
115
+ this.resetContext(params);
116
+ if (!_optionalChain([this, 'access', _10 => _10.config, 'access', _11 => _11.messages, 'optionalAccess', _12 => _12.length]) && !_optionalChain([this, 'access', _13 => _13.config, 'access', _14 => _14.queries, 'optionalAccess', _15 => _15.length])) {
117
+ this.context.debugItems.push("[filter-plugin] no filters defined, allowing message");
118
+ this.printDebugItems();
119
+ return true;
120
+ }
121
+ if (this.checkMessagePatterns()) {
122
+ this.printDebugItems();
123
+ return true;
124
+ }
125
+ const queryResult = this.checkQueries();
126
+ this.printDebugItems();
127
+ return queryResult;
128
+ }
129
+ };
130
+
131
+ // src/plugin.ts
132
+ function filterPlugin(config) {
133
+ const executor = new FilterExecutor(config);
134
+ return {
135
+ id: config.id,
136
+ disabled: config.disabled,
137
+ shouldSendToLogger: (params) => {
138
+ return executor.check(params);
139
+ }
140
+ };
141
+ }
142
+
143
+
144
+ exports.filterPlugin = filterPlugin;
145
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/loglayer/loglayer/packages/plugins/filter/dist/index.cjs","../src/FilterExecutor.ts","../src/plugin.ts"],"names":[],"mappings":"AAAA;ACAA,qDAA0B;AAyBnB,IAAM,eAAA,EAAN,MAAqB;AAAA,EACT;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOR,WAAA,CAAY,MAAA,EAA4B;AACtC,IAAA,IAAA,CAAK,OAAA,EAAS,MAAA;AACd,IAAA,IAAA,CAAK,QAAA,EAAU;AAAA,MACb,OAAA,EAAS,EAAA;AAAA,MACT,QAAA,EAAU,EAAA;AAAA,MACV,IAAA,EAAM,CAAC,CAAA;AAAA,MACP,UAAA,EAAY,CAAC;AAAA,IACf,CAAA;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,YAAA,CAAa,MAAA,EAA8C;AACjE,IAAA,IAAA,CAAK,QAAA,EAAU;AAAA,MACb,OAAA,kBAAS,MAAA,mBAAO,QAAA,6BAAU,IAAA,mBAAK,GAAG,IAAA,GAAK,EAAA;AAAA,MACvC,QAAA,EAAU,MAAA,CAAO,QAAA;AAAA,MACjB,IAAA,EAAM,MAAA,CAAO,KAAA,GAAQ,CAAC,CAAA;AAAA,MACtB,UAAA,EAAY,CAAC,mDAAmD;AAAA,IAClE,CAAA;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAA,CAAA,EAAwB;AAC9B,IAAA,GAAA,CAAI,IAAA,CAAK,MAAA,CAAO,KAAA,EAAO;AACrB,MAAA,IAAA,CAAA,MAAW,KAAA,GAAQ,IAAA,CAAK,OAAA,CAAQ,UAAA,EAAY;AAC1C,QAAA,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,oBAAA,CAAA,EAAgC;AACtC,IAAA,GAAA,CAAI,iBAAC,IAAA,qBAAK,MAAA,qBAAO,QAAA,6BAAU,QAAA,EAAQ;AACjC,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,GAAA,CAAI,IAAA,CAAK,MAAA,CAAO,KAAA,EAAO;AACrB,MAAA,IAAA,CAAK,OAAA,CAAQ,UAAA,CAAW,IAAA,CAAK,CAAA,yBAAA,EAA4B,IAAA,CAAK,OAAA,CAAQ,OAAO,CAAA,CAAA;AAC/E,IAAA;AAE4C,IAAA;AACnB,MAAA;AAC6C,QAAA;AACpE,MAAA;AAG8D,MAAA;AAEjD,MAAA;AACuD,QAAA;AAC3D,QAAA;AACT,MAAA;AAEmE,MAAA;AACrE,IAAA;AAEO,IAAA;AACT,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQgC,EAAA;AACI,IAAA;AACzB,MAAA;AACT,IAAA;AAE4E,IAAA;AACvD,IAAA;AACC,MAAA;AACE,MAAA;AACH,MAAA;AACrB,IAAA;AAEI,IAAA;AACqB,MAAA;AACiD,QAAA;AACA,QAAA;AACxE,MAAA;AAE2D,MAAA;AAEpC,MAAA;AAC+C,QAAA;AACtE,MAAA;AAEuB,MAAA;AACb,IAAA;AACiC,MAAA;AACU,MAAA;AACe,MAAA;AAC7D,MAAA;AACT,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcuD,EAAA;AAC7B,IAAA;AAG2C,IAAA;AACpC,MAAA;AACR,MAAA;AACd,MAAA;AACT,IAAA;AAGiC,IAAA;AACV,MAAA;AACd,MAAA;AACT,IAAA;AAGsC,IAAA;AACjB,IAAA;AACd,IAAA;AACT,EAAA;AACF;AD9CoF;AACA;AEtGX;AAC7B,EAAA;AAEnC,EAAA;AACM,IAAA;AACM,IAAA;AAC+C,IAAA;AAClC,MAAA;AAC9B,IAAA;AACF,EAAA;AACF;AFuGoF;AACA;AACA","file":"/home/runner/work/loglayer/loglayer/packages/plugins/filter/dist/index.cjs","sourcesContent":[null,"import { jsonquery } from \"@jsonquerylang/jsonquery\";\nimport type { PluginShouldSendToLoggerParams } from \"@loglayer/plugin\";\nimport type { filterPluginParams } from \"./types.js\";\n\n/**\n * Internal context for the filter execution.\n * Contains the current log message details and debug information.\n */\ninterface FilterContext {\n /** The combined log message */\n message: string;\n /** The log level */\n logLevel: string;\n /** Additional log data */\n data: Record<string, any>;\n /** Debug messages for troubleshooting */\n debugItems: string[];\n}\n\n/**\n * Core filtering logic implementation.\n * Handles pattern matching and query-based filtering of log messages.\n *\n * @internal\n */\nexport class FilterExecutor {\n private readonly config: filterPluginParams;\n private context: FilterContext;\n\n /**\n * Creates a new FilterExecutor instance.\n *\n * @param config - The filter plugin configuration\n */\n constructor(config: filterPluginParams) {\n this.config = config;\n this.context = {\n message: \"\",\n logLevel: \"\",\n data: {},\n debugItems: [],\n };\n }\n\n /**\n * Resets the context for a new log message check.\n *\n * @param params - The log message parameters\n */\n private resetContext(params: PluginShouldSendToLoggerParams): void {\n this.context = {\n message: params.messages?.join(\" \") || \"\",\n logLevel: params.logLevel,\n data: params.data || {},\n debugItems: [\"[filter-plugin] =================================\"],\n };\n }\n\n /**\n * Prints debug information if debug mode is enabled.\n */\n private printDebugItems(): void {\n if (this.config.debug) {\n for (const item of this.context.debugItems) {\n console.log(item);\n }\n }\n }\n\n /**\n * Checks if the current message matches any of the configured patterns.\n * Supports both string and RegExp patterns.\n *\n * @returns true if any pattern matches, false otherwise\n */\n private checkMessagePatterns(): boolean {\n if (!this.config.messages?.length) {\n return false;\n }\n\n if (this.config.debug) {\n this.context.debugItems.push(`[filter-plugin] message: ${this.context.message}`);\n }\n\n for (const pattern of this.config.messages) {\n if (this.config.debug) {\n this.context.debugItems.push(`[filter-plugin] pattern: ${pattern}`);\n }\n\n const matches =\n typeof pattern === \"string\" ? this.context.message.includes(pattern) : pattern.test(this.context.message);\n\n if (matches) {\n this.context.debugItems.push(\"[filter-plugin] pattern match: true\");\n return true;\n }\n\n this.context.debugItems.push(\"[filter-plugin] pattern match: false\");\n }\n\n return false;\n }\n\n /**\n * Checks if the current message matches any of the configured queries.\n * Uses @jsonquerylang/jsonquery for query execution.\n *\n * @returns true if any query matches, false otherwise\n */\n private checkQueries(): boolean {\n if (!this.config.queries?.length) {\n return false;\n }\n\n const query = this.config.queries.map((q) => `(${q.replaceAll(`'`, `\"`)})`).join(\" or \");\n const queryContext = {\n level: this.context.logLevel,\n message: this.context.message,\n data: this.context.data,\n };\n\n try {\n if (this.config.debug) {\n this.context.debugItems.push(`[filter-plugin] query: filter(${query})`);\n this.context.debugItems.push(`[filter-plugin] input: ${JSON.stringify(queryContext)}`);\n }\n\n const output = jsonquery([queryContext], `filter(${query})`) as Array<any>;\n\n if (this.config.debug) {\n this.context.debugItems.push(`[filter-plugin] query match: ${output.length > 0}`);\n }\n\n return output.length > 0;\n } catch (e) {\n console.error(`[filter-plugin] Error: ${e}`);\n console.log(`[filter-plugin] query: filter(${query})`);\n console.log(`[filter-plugin] input: ${JSON.stringify(queryContext)}`);\n return false;\n }\n }\n\n /**\n * Checks if a log message should be allowed based on the configured filters.\n *\n * The filtering logic is:\n * 1. If no filters defined, allow all logs\n * 2. If message patterns match, allow the log\n * 3. If queries match, allow the log\n * 4. Otherwise, filter out the log\n *\n * @param params - The log message parameters\n * @returns true if the log should be allowed, false otherwise\n */\n check(params: PluginShouldSendToLoggerParams): boolean {\n this.resetContext(params);\n\n // If no filters defined at all, allow everything\n if (!this.config.messages?.length && !this.config.queries?.length) {\n this.context.debugItems.push(\"[filter-plugin] no filters defined, allowing message\");\n this.printDebugItems();\n return true;\n }\n\n // Check message patterns first\n if (this.checkMessagePatterns()) {\n this.printDebugItems();\n return true;\n }\n\n // Then check queries if message patterns didn't match\n const queryResult = this.checkQueries();\n this.printDebugItems();\n return queryResult;\n }\n}\n","import type { LogLayerPlugin, PluginShouldSendToLoggerParams } from \"@loglayer/plugin\";\nimport { FilterExecutor } from \"./FilterExecutor.js\";\nimport type { filterPluginParams } from \"./types.js\";\n\n/**\n * Creates a new filter plugin instance.\n *\n * The filter plugin allows filtering log messages based on:\n * - String patterns\n * - Regular expressions\n * - JSON queries\n *\n * @example\n * ```typescript\n * // Filter error messages\n * const filter = filterPlugin({\n * messages: ['error'],\n * });\n *\n * // Filter by log level\n * const levelFilter = filterPlugin({\n * queries: ['.level == \"error\" or .level == \"warn\"'],\n * });\n * ```\n *\n * @param config - The filter plugin configuration\n * @returns A LogLayer plugin instance\n */\nexport function filterPlugin(config: filterPluginParams): LogLayerPlugin {\n const executor = new FilterExecutor(config);\n\n return {\n id: config.id,\n disabled: config.disabled,\n shouldSendToLogger: (params: PluginShouldSendToLoggerParams) => {\n return executor.check(params);\n },\n };\n}\n"]}
@@ -0,0 +1,77 @@
1
+ import { LogLayerPluginParams, LogLayerPlugin } from '@loglayer/plugin';
2
+
3
+ /**
4
+ * Configuration parameters for the filter plugin.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * const filter = filterPlugin({
9
+ * id: 'error-filter',
10
+ * messages: ['error'],
11
+ * queries: ['.level == "error"'],
12
+ * debug: true,
13
+ * });
14
+ * ```
15
+ */
16
+ interface filterPluginParams extends LogLayerPluginParams {
17
+ /**
18
+ * Array of string patterns or regular expressions to match against log messages.
19
+ * If any pattern matches, the log will be allowed.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * messages: ['error', /warning\d+/]
24
+ * ```
25
+ */
26
+ messages?: Array<string | RegExp>;
27
+ /**
28
+ * Array of JSON queries to filter logs.
29
+ * If any query matches, the log will be allowed.
30
+ * Uses @jsonquerylang/jsonquery syntax.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * queries: ['.level == "error"', '.data.userId == "123"']
35
+ * ```
36
+ */
37
+ queries?: Array<string>;
38
+ /**
39
+ * Enable debug mode to see detailed information about the filtering process.
40
+ * When enabled, the plugin will log:
41
+ * - Message content
42
+ * - Pattern matching results
43
+ * - Query execution details
44
+ * - Final filtering decision
45
+ *
46
+ * @default false
47
+ */
48
+ debug?: boolean;
49
+ }
50
+
51
+ /**
52
+ * Creates a new filter plugin instance.
53
+ *
54
+ * The filter plugin allows filtering log messages based on:
55
+ * - String patterns
56
+ * - Regular expressions
57
+ * - JSON queries
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * // Filter error messages
62
+ * const filter = filterPlugin({
63
+ * messages: ['error'],
64
+ * });
65
+ *
66
+ * // Filter by log level
67
+ * const levelFilter = filterPlugin({
68
+ * queries: ['.level == "error" or .level == "warn"'],
69
+ * });
70
+ * ```
71
+ *
72
+ * @param config - The filter plugin configuration
73
+ * @returns A LogLayer plugin instance
74
+ */
75
+ declare function filterPlugin(config: filterPluginParams): LogLayerPlugin;
76
+
77
+ export { filterPlugin, type filterPluginParams };
@@ -0,0 +1,77 @@
1
+ import { LogLayerPluginParams, LogLayerPlugin } from '@loglayer/plugin';
2
+
3
+ /**
4
+ * Configuration parameters for the filter plugin.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * const filter = filterPlugin({
9
+ * id: 'error-filter',
10
+ * messages: ['error'],
11
+ * queries: ['.level == "error"'],
12
+ * debug: true,
13
+ * });
14
+ * ```
15
+ */
16
+ interface filterPluginParams extends LogLayerPluginParams {
17
+ /**
18
+ * Array of string patterns or regular expressions to match against log messages.
19
+ * If any pattern matches, the log will be allowed.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * messages: ['error', /warning\d+/]
24
+ * ```
25
+ */
26
+ messages?: Array<string | RegExp>;
27
+ /**
28
+ * Array of JSON queries to filter logs.
29
+ * If any query matches, the log will be allowed.
30
+ * Uses @jsonquerylang/jsonquery syntax.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * queries: ['.level == "error"', '.data.userId == "123"']
35
+ * ```
36
+ */
37
+ queries?: Array<string>;
38
+ /**
39
+ * Enable debug mode to see detailed information about the filtering process.
40
+ * When enabled, the plugin will log:
41
+ * - Message content
42
+ * - Pattern matching results
43
+ * - Query execution details
44
+ * - Final filtering decision
45
+ *
46
+ * @default false
47
+ */
48
+ debug?: boolean;
49
+ }
50
+
51
+ /**
52
+ * Creates a new filter plugin instance.
53
+ *
54
+ * The filter plugin allows filtering log messages based on:
55
+ * - String patterns
56
+ * - Regular expressions
57
+ * - JSON queries
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * // Filter error messages
62
+ * const filter = filterPlugin({
63
+ * messages: ['error'],
64
+ * });
65
+ *
66
+ * // Filter by log level
67
+ * const levelFilter = filterPlugin({
68
+ * queries: ['.level == "error" or .level == "warn"'],
69
+ * });
70
+ * ```
71
+ *
72
+ * @param config - The filter plugin configuration
73
+ * @returns A LogLayer plugin instance
74
+ */
75
+ declare function filterPlugin(config: filterPluginParams): LogLayerPlugin;
76
+
77
+ export { filterPlugin, type filterPluginParams };
package/dist/index.js ADDED
@@ -0,0 +1,145 @@
1
+ // src/FilterExecutor.ts
2
+ import { jsonquery } from "@jsonquerylang/jsonquery";
3
+ var FilterExecutor = class {
4
+ config;
5
+ context;
6
+ /**
7
+ * Creates a new FilterExecutor instance.
8
+ *
9
+ * @param config - The filter plugin configuration
10
+ */
11
+ constructor(config) {
12
+ this.config = config;
13
+ this.context = {
14
+ message: "",
15
+ logLevel: "",
16
+ data: {},
17
+ debugItems: []
18
+ };
19
+ }
20
+ /**
21
+ * Resets the context for a new log message check.
22
+ *
23
+ * @param params - The log message parameters
24
+ */
25
+ resetContext(params) {
26
+ this.context = {
27
+ message: params.messages?.join(" ") || "",
28
+ logLevel: params.logLevel,
29
+ data: params.data || {},
30
+ debugItems: ["[filter-plugin] ================================="]
31
+ };
32
+ }
33
+ /**
34
+ * Prints debug information if debug mode is enabled.
35
+ */
36
+ printDebugItems() {
37
+ if (this.config.debug) {
38
+ for (const item of this.context.debugItems) {
39
+ console.log(item);
40
+ }
41
+ }
42
+ }
43
+ /**
44
+ * Checks if the current message matches any of the configured patterns.
45
+ * Supports both string and RegExp patterns.
46
+ *
47
+ * @returns true if any pattern matches, false otherwise
48
+ */
49
+ checkMessagePatterns() {
50
+ if (!this.config.messages?.length) {
51
+ return false;
52
+ }
53
+ if (this.config.debug) {
54
+ this.context.debugItems.push(`[filter-plugin] message: ${this.context.message}`);
55
+ }
56
+ for (const pattern of this.config.messages) {
57
+ if (this.config.debug) {
58
+ this.context.debugItems.push(`[filter-plugin] pattern: ${pattern}`);
59
+ }
60
+ const matches = typeof pattern === "string" ? this.context.message.includes(pattern) : pattern.test(this.context.message);
61
+ if (matches) {
62
+ this.context.debugItems.push("[filter-plugin] pattern match: true");
63
+ return true;
64
+ }
65
+ this.context.debugItems.push("[filter-plugin] pattern match: false");
66
+ }
67
+ return false;
68
+ }
69
+ /**
70
+ * Checks if the current message matches any of the configured queries.
71
+ * Uses @jsonquerylang/jsonquery for query execution.
72
+ *
73
+ * @returns true if any query matches, false otherwise
74
+ */
75
+ checkQueries() {
76
+ if (!this.config.queries?.length) {
77
+ return false;
78
+ }
79
+ const query = this.config.queries.map((q) => `(${q.replaceAll(`'`, `"`)})`).join(" or ");
80
+ const queryContext = {
81
+ level: this.context.logLevel,
82
+ message: this.context.message,
83
+ data: this.context.data
84
+ };
85
+ try {
86
+ if (this.config.debug) {
87
+ this.context.debugItems.push(`[filter-plugin] query: filter(${query})`);
88
+ this.context.debugItems.push(`[filter-plugin] input: ${JSON.stringify(queryContext)}`);
89
+ }
90
+ const output = jsonquery([queryContext], `filter(${query})`);
91
+ if (this.config.debug) {
92
+ this.context.debugItems.push(`[filter-plugin] query match: ${output.length > 0}`);
93
+ }
94
+ return output.length > 0;
95
+ } catch (e) {
96
+ console.error(`[filter-plugin] Error: ${e}`);
97
+ console.log(`[filter-plugin] query: filter(${query})`);
98
+ console.log(`[filter-plugin] input: ${JSON.stringify(queryContext)}`);
99
+ return false;
100
+ }
101
+ }
102
+ /**
103
+ * Checks if a log message should be allowed based on the configured filters.
104
+ *
105
+ * The filtering logic is:
106
+ * 1. If no filters defined, allow all logs
107
+ * 2. If message patterns match, allow the log
108
+ * 3. If queries match, allow the log
109
+ * 4. Otherwise, filter out the log
110
+ *
111
+ * @param params - The log message parameters
112
+ * @returns true if the log should be allowed, false otherwise
113
+ */
114
+ check(params) {
115
+ this.resetContext(params);
116
+ if (!this.config.messages?.length && !this.config.queries?.length) {
117
+ this.context.debugItems.push("[filter-plugin] no filters defined, allowing message");
118
+ this.printDebugItems();
119
+ return true;
120
+ }
121
+ if (this.checkMessagePatterns()) {
122
+ this.printDebugItems();
123
+ return true;
124
+ }
125
+ const queryResult = this.checkQueries();
126
+ this.printDebugItems();
127
+ return queryResult;
128
+ }
129
+ };
130
+
131
+ // src/plugin.ts
132
+ function filterPlugin(config) {
133
+ const executor = new FilterExecutor(config);
134
+ return {
135
+ id: config.id,
136
+ disabled: config.disabled,
137
+ shouldSendToLogger: (params) => {
138
+ return executor.check(params);
139
+ }
140
+ };
141
+ }
142
+ export {
143
+ filterPlugin
144
+ };
145
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/FilterExecutor.ts","../src/plugin.ts"],"sourcesContent":["import { jsonquery } from \"@jsonquerylang/jsonquery\";\nimport type { PluginShouldSendToLoggerParams } from \"@loglayer/plugin\";\nimport type { filterPluginParams } from \"./types.js\";\n\n/**\n * Internal context for the filter execution.\n * Contains the current log message details and debug information.\n */\ninterface FilterContext {\n /** The combined log message */\n message: string;\n /** The log level */\n logLevel: string;\n /** Additional log data */\n data: Record<string, any>;\n /** Debug messages for troubleshooting */\n debugItems: string[];\n}\n\n/**\n * Core filtering logic implementation.\n * Handles pattern matching and query-based filtering of log messages.\n *\n * @internal\n */\nexport class FilterExecutor {\n private readonly config: filterPluginParams;\n private context: FilterContext;\n\n /**\n * Creates a new FilterExecutor instance.\n *\n * @param config - The filter plugin configuration\n */\n constructor(config: filterPluginParams) {\n this.config = config;\n this.context = {\n message: \"\",\n logLevel: \"\",\n data: {},\n debugItems: [],\n };\n }\n\n /**\n * Resets the context for a new log message check.\n *\n * @param params - The log message parameters\n */\n private resetContext(params: PluginShouldSendToLoggerParams): void {\n this.context = {\n message: params.messages?.join(\" \") || \"\",\n logLevel: params.logLevel,\n data: params.data || {},\n debugItems: [\"[filter-plugin] =================================\"],\n };\n }\n\n /**\n * Prints debug information if debug mode is enabled.\n */\n private printDebugItems(): void {\n if (this.config.debug) {\n for (const item of this.context.debugItems) {\n console.log(item);\n }\n }\n }\n\n /**\n * Checks if the current message matches any of the configured patterns.\n * Supports both string and RegExp patterns.\n *\n * @returns true if any pattern matches, false otherwise\n */\n private checkMessagePatterns(): boolean {\n if (!this.config.messages?.length) {\n return false;\n }\n\n if (this.config.debug) {\n this.context.debugItems.push(`[filter-plugin] message: ${this.context.message}`);\n }\n\n for (const pattern of this.config.messages) {\n if (this.config.debug) {\n this.context.debugItems.push(`[filter-plugin] pattern: ${pattern}`);\n }\n\n const matches =\n typeof pattern === \"string\" ? this.context.message.includes(pattern) : pattern.test(this.context.message);\n\n if (matches) {\n this.context.debugItems.push(\"[filter-plugin] pattern match: true\");\n return true;\n }\n\n this.context.debugItems.push(\"[filter-plugin] pattern match: false\");\n }\n\n return false;\n }\n\n /**\n * Checks if the current message matches any of the configured queries.\n * Uses @jsonquerylang/jsonquery for query execution.\n *\n * @returns true if any query matches, false otherwise\n */\n private checkQueries(): boolean {\n if (!this.config.queries?.length) {\n return false;\n }\n\n const query = this.config.queries.map((q) => `(${q.replaceAll(`'`, `\"`)})`).join(\" or \");\n const queryContext = {\n level: this.context.logLevel,\n message: this.context.message,\n data: this.context.data,\n };\n\n try {\n if (this.config.debug) {\n this.context.debugItems.push(`[filter-plugin] query: filter(${query})`);\n this.context.debugItems.push(`[filter-plugin] input: ${JSON.stringify(queryContext)}`);\n }\n\n const output = jsonquery([queryContext], `filter(${query})`) as Array<any>;\n\n if (this.config.debug) {\n this.context.debugItems.push(`[filter-plugin] query match: ${output.length > 0}`);\n }\n\n return output.length > 0;\n } catch (e) {\n console.error(`[filter-plugin] Error: ${e}`);\n console.log(`[filter-plugin] query: filter(${query})`);\n console.log(`[filter-plugin] input: ${JSON.stringify(queryContext)}`);\n return false;\n }\n }\n\n /**\n * Checks if a log message should be allowed based on the configured filters.\n *\n * The filtering logic is:\n * 1. If no filters defined, allow all logs\n * 2. If message patterns match, allow the log\n * 3. If queries match, allow the log\n * 4. Otherwise, filter out the log\n *\n * @param params - The log message parameters\n * @returns true if the log should be allowed, false otherwise\n */\n check(params: PluginShouldSendToLoggerParams): boolean {\n this.resetContext(params);\n\n // If no filters defined at all, allow everything\n if (!this.config.messages?.length && !this.config.queries?.length) {\n this.context.debugItems.push(\"[filter-plugin] no filters defined, allowing message\");\n this.printDebugItems();\n return true;\n }\n\n // Check message patterns first\n if (this.checkMessagePatterns()) {\n this.printDebugItems();\n return true;\n }\n\n // Then check queries if message patterns didn't match\n const queryResult = this.checkQueries();\n this.printDebugItems();\n return queryResult;\n }\n}\n","import type { LogLayerPlugin, PluginShouldSendToLoggerParams } from \"@loglayer/plugin\";\nimport { FilterExecutor } from \"./FilterExecutor.js\";\nimport type { filterPluginParams } from \"./types.js\";\n\n/**\n * Creates a new filter plugin instance.\n *\n * The filter plugin allows filtering log messages based on:\n * - String patterns\n * - Regular expressions\n * - JSON queries\n *\n * @example\n * ```typescript\n * // Filter error messages\n * const filter = filterPlugin({\n * messages: ['error'],\n * });\n *\n * // Filter by log level\n * const levelFilter = filterPlugin({\n * queries: ['.level == \"error\" or .level == \"warn\"'],\n * });\n * ```\n *\n * @param config - The filter plugin configuration\n * @returns A LogLayer plugin instance\n */\nexport function filterPlugin(config: filterPluginParams): LogLayerPlugin {\n const executor = new FilterExecutor(config);\n\n return {\n id: config.id,\n disabled: config.disabled,\n shouldSendToLogger: (params: PluginShouldSendToLoggerParams) => {\n return executor.check(params);\n },\n };\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@loglayer/plugin-filter",
3
+ "description": "Filter logs with LogLayer using string patterns, regular expressions, or JSON Queries.",
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "exports": {
9
+ "import": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "require": {
14
+ "types": "./dist/index.d.cts",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "types": "./dist/index.d.ts",
19
+ "license": "MIT",
20
+ "repository": "loglayer/loglayer.git",
21
+ "author": "Theo Gravity <theo@suteki.nu>",
22
+ "keywords": [
23
+ "logging",
24
+ "log",
25
+ "loglayer",
26
+ "filter",
27
+ "filtering",
28
+ "plugin"
29
+ ],
30
+ "dependencies": {
31
+ "@jsonquerylang/jsonquery": "4.1.1",
32
+ "@loglayer/plugin": "1.1.1",
33
+ "@loglayer/shared": "1.0.5"
34
+ },
35
+ "devDependencies": {
36
+ "hash-runner": "2.0.1",
37
+ "@types/node": "22.10.4",
38
+ "tsup": "8.3.5",
39
+ "typescript": "5.7.2",
40
+ "vitest": "2.1.8",
41
+ "loglayer": "5.1.4",
42
+ "@internal/tsconfig": "1.0.0"
43
+ },
44
+ "bugs": "https://github.com/loglayer/loglayer/issues",
45
+ "engines": {
46
+ "node": ">=18"
47
+ },
48
+ "files": [
49
+ "dist"
50
+ ],
51
+ "homepage": "https://loglayer.dev",
52
+ "scripts": {
53
+ "build": "tsup src/index.ts",
54
+ "test": "vitest --run",
55
+ "build:dev": "hash-runner",
56
+ "clean": "rm -rf .turbo node_modules dist",
57
+ "lint": "biome check --write --unsafe src && biome format src --write && biome lint src --fix",
58
+ "verify-types": "tsc --noEmit"
59
+ }
60
+ }