[READ-ONLY] Mirror of https://github.com/FoxxMD/logging. A typed, opinionated, batteries-included, Pino-based logging solution for backend TS/JS projects foxxmd.github.io/logging
child-logger logging logging-library nodejs pinojs typescript-library
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: Allow evaluating labels as functions

FoxxMD (Dec 3, 2025, 4:23 PM UTC) 21ca7705 5fde5b7f

+42 -6
+4 -2
README.md
··· 147 147 148 148 **Labels** are inserted between the log level and message contents of a log. The child logger inherits **all** labels from **all** its parent loggers. 149 149 150 - `childLogger` accepts a single string label or an array of string labels. 150 + `childLogger` accepts a single string/function label or an array of string/function labels. 151 151 152 152 ```ts 153 153 import {loggerApp, childLogger} from '@foxxmd/logging'; ··· 160 160 nestedChild1.debug('I am nested one level'); 161 161 // [2024-03-07 11:27:41.945 -0500] DEBUG: [First] I am nested one level 162 162 163 - const nestedChild2 = childLogger(nestedChild1, ['Second', 'Third']); 163 + const nestedChild2 = childLogger(nestedChild1, ['Second', () => 'Third']); 164 164 nestedChild2.warn('I am nested two levels but with more labels'); 165 165 // [2024-03-07 11:27:41.945 -0500] WARN: [First] [Second] [Third] I am nested two levels but with more labels 166 166 ··· 174 174 ```ts 175 175 logger.debug({labels: ['MyLabel']}, 'My log message'); 176 176 ``` 177 + 178 + **NOTE:** If a label *function* throws an error then the label will be the error's message. Make sure your labels don't throw! 177 179 178 180 ### Serializing Objects and Errors 179 181
+18 -1
src/loggers.ts
··· 9 9 LogOptions 10 10 } from "./types.js"; 11 11 import {buildDestinationFile, buildDestinationRollingFile, buildDestinationStdout} from "./destinations.js"; 12 - import {pino, levels} from "pino"; 12 + import {pino, levels, stdSerializers} from "pino"; 13 13 14 14 /** 15 15 * Builds a Logger object for use in your application ··· 41 41 level: defaultLevel, 42 42 customLevels: CUSTOM_LEVELS, 43 43 useOnlyCustomLevels: false, 44 + serializers: { 45 + err: stdSerializers.err, 46 + labels: (labels: any[]) => { 47 + // allow dynamic labels 48 + return labels.map(x => { 49 + if(typeof x === 'function') { 50 + try { 51 + return x() 52 + } catch (e) { 53 + return e.message; 54 + } 55 + } 56 + return x; 57 + 58 + }).filter(x => x !== null && x !== undefined); 59 + }, 60 + }, 44 61 ...extras 45 62 }, pino.multistream(streams, {levels: {...levels.values, ...CUSTOM_LEVELS}})) as Logger; 46 63 plogger.labels = [];
+20 -3
tests/index.test.ts
··· 622 622 expect(raw.labels[1]).eq('Test2'); 623 623 }); 624 624 625 + it('outputs labels from functions', async function () { 626 + const [logger, testStream, rawStream] = testConsoleLogger(); 627 + const formattedBuff = pEvent(testStream, 'data'); 628 + const rawBuff = pEvent(rawStream, 'data'); 629 + const child = childLogger(logger, ['Test1', () => 'Dynamic']); 630 + child.debug('log something'); 631 + await sleep(10); 632 + const formatted = (await formattedBuff).toString(); 633 + const raw = JSON.parse((await rawBuff).toString()) as LogData; 634 + expect(formatted).includes(' [Test1] [Dynamic] '); 635 + expect(raw.labels).is.not.undefined; 636 + expect(raw.labels.length).eq(2); 637 + expect(raw.labels[0]).eq('Test1'); 638 + expect(raw.labels[1]).eq('Dynamic'); 639 + }); 640 + 625 641 it('merges labels from nested children', async function () { 626 642 const [logger, testStream, rawStream] = testConsoleLogger(); 627 643 const formattedBuff = pEvent(testStream, 'data'); 628 644 const rawBuff = pEvent(rawStream, 'data'); 629 645 const child = childLogger(logger, ['Test1', 'Test2']); 630 - const child2 = childLogger(child, ['Test3', 'Test4']); 646 + const child2 = childLogger(child, ['Test3', () => 'Test4']); 631 647 const child3 = childLogger(child2, ['Test5']); 632 648 child3.debug('log something'); 633 649 await sleep(10); ··· 649 665 const rawBuff = pEvent(rawStream, 'data'); 650 666 651 667 logger.addLabel('Parent'); 652 - const child = childLogger(logger, ['Test1']); 668 + const child = childLogger(logger, [() => 'Test1']); 653 669 child.debug({labels: ['Runtime']},'log something'); 654 670 await sleep(10); 655 671 const formatted = (await formattedBuff).toString(); ··· 679 695 const rawBuff = pEvent(rawStream, 'data'); 680 696 681 697 logger.addLabel('Parent'); 682 - logger.debug({labels: ['Runtime']},'log something'); 698 + logger.debug({labels: ['Runtime', () => 'Runtime2']},'log something'); 683 699 await sleep(10); 684 700 const formatted = (await formattedBuff).toString(); 685 701 expect(formatted).includes(' [Parent] '); 686 702 expect(formatted).includes(' [Runtime] '); 703 + expect(formatted).includes(' [Runtime2] '); 687 704 }); 688 705 }); 689 706 });