syntropylog 0.12.1 → 0.12.2
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/CHANGELOG.md +8 -0
- package/README.md +78 -14
- package/dist/types/config/loadLoggerConfig.d.ts +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- **Socket / security:** Addressed Socket.dev alerts and clarified behavior in docs. Native addon no longer uses shell (`execSync`): resolves `ldd` path via `PATH` and `fs.existsSync` only. Documented filesystem access (native loader + `loadLoggerConfig`), environment variables (only `PATH` in optional native addon), dynamic require (static paths only), and URL/network (no runtime URLs). SECURITY.md now lists the single env var read (`PATH`) and README Security & Compliance section covers network, env, dynamic require, and filesystem.
|
|
8
|
+
|
|
9
|
+
**Docs - Universal Adapter:** README section 3 reworked: mapping is defined once with `UniversalLogFormatter` (outside the executor); executor receives the mapped object and can send it to multiple backends (e.g. Prisma, TypeORM, Mongoose) in one block. Single example shows one mapping → one object → three destinations with `Promise.all`.
|
|
10
|
+
|
|
3
11
|
## 0.12.0
|
|
4
12
|
|
|
5
13
|
First release after 0.11.3. Includes all framework refinements validated end-to-end with the examples repo (0.11.4 was never published to npm).
|
package/README.md
CHANGED
|
@@ -160,33 +160,86 @@ await syntropyLog.init({
|
|
|
160
160
|
|
|
161
161
|
## 3. Universal Adapter — log to any backend
|
|
162
162
|
|
|
163
|
-
**What:** Send each log to PostgreSQL, MongoDB, Elasticsearch, S3, etc. by implementing a single `executor`. No vendor lock-in;
|
|
163
|
+
**What:** Send each log to PostgreSQL, MongoDB, Elasticsearch, S3, etc. by implementing a single `executor`. No vendor lock-in. You define **once** how the log entry maps to your schema; the executor only receives that mapped object and persists it (ORM, raw client, HTTP). If `executor` throws, SyntropyLog logs the error and continues (Silent Observer).
|
|
164
164
|
|
|
165
|
-
**How:** Use `AdapterTransport` + `UniversalAdapter
|
|
165
|
+
**How:** Use `AdapterTransport` + `UniversalAdapter` + `UniversalLogFormatter`. Three steps:
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### 1. Define the mapping (outside the executor)
|
|
170
|
+
|
|
171
|
+
Define how each log entry field maps to your persistence shape. One place, no repetition in code. Use `UniversalLogFormatter` with a `mapping` object: keys = your schema (e.g. DB columns), values = path in the log entry.
|
|
172
|
+
|
|
173
|
+
| Log entry path | Meaning |
|
|
174
|
+
|----------------|--------|
|
|
175
|
+
| `level` | Log level |
|
|
176
|
+
| `message` | Message string |
|
|
177
|
+
| `serviceName` | From init config |
|
|
178
|
+
| `correlationId` | From context |
|
|
179
|
+
| `timestamp` | ISO string |
|
|
180
|
+
| `meta` | Merged metadata (use as payload/JSON column) |
|
|
181
|
+
|
|
182
|
+
Example: map to a table with columns `level`, `message`, `serviceName`, `correlationId`, `payload`, `timestamp`.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { UniversalLogFormatter } from 'syntropylog';
|
|
186
|
+
|
|
187
|
+
const logToDbMapping = {
|
|
188
|
+
level: 'level',
|
|
189
|
+
message: 'message',
|
|
190
|
+
serviceName: 'serviceName',
|
|
191
|
+
correlationId: 'correlationId',
|
|
192
|
+
payload: 'meta',
|
|
193
|
+
timestamp: 'timestamp',
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const formatter = new UniversalLogFormatter({ mapping: logToDbMapping });
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The formatter turns every log entry into an object with exactly those keys. Change the mapping here when your schema changes; the executor stays the same.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### 2. The object the executor receives
|
|
204
|
+
|
|
205
|
+
When you attach this formatter to the transport, the `executor` receives **that mapped object** (not the raw log entry). So the executor only sees something like `{ level, message, serviceName, correlationId, payload, timestamp }`. Your only job in the executor is to **persist** that object.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
### 3. Sending that object to your backend
|
|
210
|
+
|
|
211
|
+
One mapping produces one object shape. You can send **that same object** to as many backends as you want in a single executor: same `data`, different destinations (e.g. Prisma, TypeORM, Mongoose, Elasticsearch, S3). No field list — the shape comes from the mapping.
|
|
212
|
+
|
|
213
|
+
**Example: one mapped object → three destinations**
|
|
166
214
|
|
|
167
215
|
```typescript
|
|
168
216
|
import { AdapterTransport, UniversalAdapter } from 'syntropylog';
|
|
217
|
+
import { prisma } from './prisma';
|
|
218
|
+
import { getRepository } from 'typeorm';
|
|
219
|
+
import { SystemLog as TypeOrmSystemLog } from './entities/SystemLog';
|
|
220
|
+
import { SystemLogModel } from './models/SystemLog';
|
|
169
221
|
|
|
170
222
|
const dbTransport = new AdapterTransport({
|
|
171
223
|
name: 'db',
|
|
224
|
+
formatter,
|
|
172
225
|
adapter: new UniversalAdapter({
|
|
173
|
-
executor: async (
|
|
174
|
-
|
|
175
|
-
data
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
226
|
+
executor: async (data) => {
|
|
227
|
+
const row = {
|
|
228
|
+
...data,
|
|
229
|
+
timestamp: new Date(data.timestamp as string),
|
|
230
|
+
};
|
|
231
|
+
// Same object, three destinations (e.g. Postgres + TypeORM DB + MongoDB)
|
|
232
|
+
await Promise.all([
|
|
233
|
+
prisma.systemLog.create({ data: row }),
|
|
234
|
+
getRepository(TypeOrmSystemLog).save(row),
|
|
235
|
+
SystemLogModel.create(row),
|
|
236
|
+
]);
|
|
184
237
|
},
|
|
185
238
|
}),
|
|
186
239
|
});
|
|
187
240
|
```
|
|
188
241
|
|
|
189
|
-
|
|
242
|
+
Use one transport and one executor; add or remove destinations in that same block. Use this transport in your `init()` (e.g. in `logger.transports` or `logger.transportList` + `logger.env`).
|
|
190
243
|
|
|
191
244
|
---
|
|
192
245
|
|
|
@@ -569,6 +622,17 @@ Secure this route (e.g. auth, internal only). When debugging in a POD is finishe
|
|
|
569
622
|
|
|
570
623
|
## Security & Compliance
|
|
571
624
|
|
|
625
|
+
**Network & URLs:** This package does not contact any external URLs or IPs at runtime. The only network I/O is what you configure (e.g. your `executor` sending logs to your PostgreSQL, Elasticsearch, or API). URLs in this README and in `package.json` are documentation and metadata only (badges, repository links); they are not used for outbound connections.
|
|
626
|
+
|
|
627
|
+
**Environment variables:** The main package does not read any. The optional native addon (`syntropylog-native`) reads only `PATH` on Linux to locate the `ldd` binary for musl/glibc detection. No credentials or other env vars are read. See [SECURITY.md](./SECURITY.md) for the full list.
|
|
628
|
+
|
|
629
|
+
**Dynamic require:** The package does not use dynamic `require(variable)` or user-controlled paths. Module paths are static string literals (e.g. `require('syntropylog-native')`, `require('./index.js')`). The native addon loader chooses one of several fixed `.node` paths based on platform/arch; no user input is used to build require paths.
|
|
630
|
+
|
|
631
|
+
**Filesystem access:** The package only reads the files described below; it does not scan or read arbitrary paths.
|
|
632
|
+
|
|
633
|
+
- **Native addon loader** (`syntropylog-native`): Reads only (1) the presence of native `.node` binaries inside the package’s own directory (`__dirname`) to choose the correct build for the current OS/arch, and (2) on Linux only, the system `ldd` binary (e.g. `/usr/bin/ldd`) to detect musl vs glibc. No user or application files are read.
|
|
634
|
+
- **Config loader** (`loadLoggerConfig`): Reads only paths you control. If you use it, it reads at most one file: either the path you pass in `opts.configPath`, or a file under `opts.configDir` with a name derived from `opts.defaultBase` and `opts.environment` (default: `./config/logger.yaml` or `./config/logger-{env}.yaml`). You can avoid filesystem access entirely by not calling `loadLoggerConfig` and passing config directly to `init()`.
|
|
635
|
+
|
|
572
636
|
| Dynamically configurable | Fixed at init |
|
|
573
637
|
|--------------------------|---------------|
|
|
574
638
|
| Log level, additive masking rules, transports (debug: add console only for visual help in a POD; existing stay; `resetTransports()` to remove added) | Core masking config (`maskChar`, `maxDepth`), Redis/HTTP/broker |
|
|
@@ -37,7 +37,9 @@ export interface LoggerConfigLoaderOptions {
|
|
|
37
37
|
*
|
|
38
38
|
* **Security:** A restricted schema (JSON_SCHEMA) is used to avoid prototype pollution
|
|
39
39
|
* and dangerous types. Only use with configuration files under deployment team
|
|
40
|
-
* control (controlled paths and permissions).
|
|
40
|
+
* control (controlled paths and permissions). This function reads only the paths
|
|
41
|
+
* derived from opts (configPath, configDir, defaultBase, environment); no other
|
|
42
|
+
* filesystem access is performed.
|
|
41
43
|
*
|
|
42
44
|
* @param opts - Options to customize the loading behavior.
|
|
43
45
|
* @returns A partial `LoggerOptions` object, or an empty object if no file is found.
|