@logickernel/logger 0.10.3 → 0.10.4

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.
Files changed (2) hide show
  1. package/README.md +88 -3
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -22,7 +22,7 @@ log.warning("disk space low", { used: "92%", mount: "/data" });
22
22
  - In **GCP** (or when `LOGGER_TARGET=gcp`): writes to Google Cloud Logging with proper severities and structured `jsonPayload` when a payload object is provided.
23
23
  - On the **console**: writes with emoji prefixes, a local timestamp, and the payload inlined as compact JSON.
24
24
  - **Both at once**: set `LOGGER_TARGET=gcp,console` to fan out to both.
25
- - **Why it exists**: To avoid sprinkling environment-specific logging logic across your codebase. You import one factory and use it everywhere.
25
+ - **Why it exists**: To make it easy to produce structured, queryable telemetry from any Node.js service without wiring up a separate metrics stack. Log entries are first-class data points: their `jsonPayload` fields and labels feed directly into Cloud Monitoring log-based metrics and dashboards.
26
26
 
27
27
  **Key features**
28
28
 
@@ -187,7 +187,92 @@ const log: Logger = logger("my-scope");
187
187
 
188
188
  ---
189
189
 
190
- ## 3. Local Setup (Development)
190
+ ## 3. Best Practices
191
+
192
+ ### Purpose: structured telemetry, not just log lines
193
+
194
+ Every log entry written to Cloud Logging is a queryable data point. The goal is to make those entries useful beyond text search: payload fields become extractable metric values (latency, counts, sizes), and labels become the dimensions you filter and group by in Cloud Monitoring dashboards and alerting policies.
195
+
196
+ ### Payload carries values; labels carry categories
197
+
198
+ The two data arguments serve distinct roles and should not be mixed:
199
+
200
+ | | `payload` — 2nd arg | `labels` — scope + 3rd arg |
201
+ |---|---|---|
202
+ | Type | `Record<string, unknown>` | `Record<string, string>` — strings only |
203
+ | Purpose | Measurements and event data | Categorization and filtering |
204
+ | GCP storage | Indexed as `jsonPayload` fields | Stored as entry labels |
205
+ | Metrics use | Field values extracted into metric data points | Dimensions for aggregation and segmentation |
206
+ | Cardinality | Can be high (IDs, URLs, queries) | Must be low (bounded enums and categories) |
207
+
208
+ **Put measurements and context in payload:**
209
+
210
+ ```ts
211
+ log.info("request handled", { ms: 42, status: 200, bytes: 1024 });
212
+ log.info("cache result", { hit: true, ttl: 300 });
213
+ log.warning("slow query", { ms: 850, rowsScanned: 12000 });
214
+ log.info("job complete", { processed: 142, failed: 3, durationMs: 5400 });
215
+ ```
216
+
217
+ **Put grouping dimensions in labels:**
218
+
219
+ ```ts
220
+ // scope sets a label on every entry from this logger
221
+ const log = logger("payments");
222
+
223
+ // per-call labels add event-specific dimensions
224
+ log.info("charge processed", { amount: 99.95 }, { provider: "stripe", currency: "usd" });
225
+ log.error("charge failed", { code: "card_declined" }, { provider: "stripe" });
226
+ ```
227
+
228
+ ### Keep label cardinality low
229
+
230
+ Labels become metric dimensions. High-cardinality values — user IDs, request IDs, raw URLs with path parameters — will explode the cardinality of any metric built on them and will be rejected or silently dropped by Cloud Monitoring. Put those values in the payload instead.
231
+
232
+ ```ts
233
+ // Good — labels are bounded, payload carries the variable data
234
+ log.info("payment processed", { amount: 99.95, userId: "u-9182", orderId: "o-4421" }, { provider: "stripe" });
235
+
236
+ // Avoid — userId in labels has unbounded cardinality
237
+ log.info("payment processed", { amount: 99.95 }, { provider: "stripe", userId: "u-9182" });
238
+ ```
239
+
240
+ ### Instantiate once per module or service boundary
241
+
242
+ Create the logger at module scope, not inside request handlers or loops. The factory is lightweight, but calling it repeatedly is unnecessary and loses the benefit of a stable scope label.
243
+
244
+ ```ts
245
+ // Good — created once, reused everywhere in this module
246
+ const log = logger("orders");
247
+
248
+ export async function createOrder(data: OrderData) {
249
+ log.info("order created", { orderId: data.id, total: data.total });
250
+ }
251
+
252
+ // Avoid — recreated on every call
253
+ export async function createOrder(data: OrderData) {
254
+ logger("orders").info("order created", { orderId: data.id, total: data.total });
255
+ }
256
+ ```
257
+
258
+ ### Building log-based metrics in Cloud Monitoring
259
+
260
+ Once entries flow into Cloud Logging you can create log-based metrics in a few steps:
261
+
262
+ 1. Open **Cloud Logging → Log-based Metrics → Create metric**.
263
+ 2. Set a filter to scope the metric, e.g.:
264
+ ```
265
+ logName="projects/MY_PROJECT/logs/MY_LOG"
266
+ severity="INFO"
267
+ jsonPayload.ms > 0
268
+ ```
269
+ 3. For a **distribution metric** (e.g. request latency), set the **field extractor** to `jsonPayload.ms`.
270
+ 4. Add **label extractors** for the dimensions you want to slice by, e.g. `labels.scope`, `labels."service_id"`.
271
+ 5. Chart the metric in **Cloud Monitoring** or attach an alerting policy (e.g. p99 latency > 500 ms).
272
+
273
+ ---
274
+
275
+ ## 4. Local Setup (Development)
191
276
 
192
277
  ### Prerequisites
193
278
 
@@ -217,7 +302,7 @@ npm run build
217
302
 
218
303
  ---
219
304
 
220
- ## 4. Additional Resources
305
+ ## 5. Additional Resources
221
306
 
222
307
  - **Package**: `@logickernel/logger` on npm.
223
308
  - **License**: MIT (see `LICENSE` in this repository).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logickernel/logger",
3
- "version": "0.10.3",
3
+ "version": "0.10.4",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",