@@ -52,6 +52,8 @@ const path = require('path');
5252// Test msg field is required
5353{
5454 const logger = createLogger ( ) ;
55+
56+ // Object without msg should throw
5557 assert . throws ( ( ) => {
5658 logger . info ( { userId : 123 } ) ; // Missing msg
5759 } , {
@@ -63,9 +65,10 @@ const path = require('path');
6365 } , {
6466 code : 'ERR_INVALID_ARG_TYPE' ,
6567 } ) ;
66- }
6768
68- // Test child logger context inheritance
69+ // String without second arg should work (no assertion needed, just shouldn't throw)
70+ logger . info ( 'just a message' ) ;
71+ } // Test child logger context inheritance
6972{
7073 const logger = createLogger ( { level : 'info' } ) ;
7174 const childLogger = logger . child ( { requestId : 'abc-123' } ) ;
@@ -182,7 +185,8 @@ const path = require('path');
182185 assert . throws ( ( ) => {
183186 handler . handle ( { } ) ;
184187 } , {
185- message : / m u s t b e i m p l e m e n t e d / ,
188+ code : 'ERR_METHOD_NOT_IMPLEMENTED' ,
189+ message : / H a n d l e r \. h a n d l e \( \) m e t h o d i s n o t i m p l e m e n t e d / ,
186190 } ) ;
187191}
188192
@@ -208,3 +212,162 @@ const path = require('path');
208212 logger . error ( { msg : 'processed' } ) ;
209213 assert . strictEqual ( handleCalled , true ) ;
210214}
215+
216+ // Test invalid fields argument
217+ {
218+ const logger = createLogger ( ) ;
219+ assert . throws ( ( ) => {
220+ logger . info ( 'message' , 'not an object' ) ; // Second arg must be object
221+ } , {
222+ code : 'ERR_INVALID_ARG_TYPE' ,
223+ } ) ;
224+ }
225+
226+ // Test string message signature
227+ {
228+ const tmpfile = path . join ( os . tmpdir ( ) , `test-log-${ process . pid } -string.json` ) ;
229+ const handler = new JSONHandler ( {
230+ stream : fs . openSync ( tmpfile , 'w' ) ,
231+ level : 'info' ,
232+ } ) ;
233+ const logger = new Logger ( { handler } ) ;
234+
235+ logger . info ( 'simple message' ) ;
236+
237+ handler . flushSync ( ) ;
238+ handler . end ( ) ;
239+ const output = fs . readFileSync ( tmpfile , 'utf8' ) ;
240+ const parsed = JSON . parse ( output . trim ( ) ) ;
241+
242+ assert . strictEqual ( parsed . msg , 'simple message' ) ;
243+ assert . strictEqual ( parsed . level , 'info' ) ;
244+
245+ fs . unlinkSync ( tmpfile ) ;
246+ }
247+
248+ // Test string message with fields
249+ {
250+ const tmpfile = path . join ( os . tmpdir ( ) , `test-log-${ process . pid } -string-fields.json` ) ;
251+ const handler = new JSONHandler ( {
252+ stream : fs . openSync ( tmpfile , 'w' ) ,
253+ level : 'info' ,
254+ } ) ;
255+ const logger = new Logger ( { handler } ) ;
256+
257+ logger . info ( 'user login' , { userId : 123 , ip : '127.0.0.1' } ) ;
258+
259+ handler . flushSync ( ) ;
260+ handler . end ( ) ;
261+ const output = fs . readFileSync ( tmpfile , 'utf8' ) ;
262+ const parsed = JSON . parse ( output . trim ( ) ) ;
263+
264+ assert . strictEqual ( parsed . msg , 'user login' ) ;
265+ assert . strictEqual ( parsed . userId , 123 ) ;
266+ assert . strictEqual ( parsed . ip , '127.0.0.1' ) ;
267+
268+ fs . unlinkSync ( tmpfile ) ;
269+ }
270+
271+ // Test Error object serialization
272+ {
273+ const tmpfile = path . join ( os . tmpdir ( ) , `test-log-${ process . pid } -error.json` ) ;
274+ const handler = new JSONHandler ( {
275+ stream : fs . openSync ( tmpfile , 'w' ) ,
276+ level : 'info' ,
277+ } ) ;
278+ const logger = new Logger ( { handler } ) ;
279+
280+ const err = new Error ( 'test error' ) ;
281+ err . code = 'TEST_ERROR' ;
282+ logger . error ( { msg : 'operation failed' , err } ) ;
283+
284+ handler . flushSync ( ) ;
285+ handler . end ( ) ;
286+ const output = fs . readFileSync ( tmpfile , 'utf8' ) ;
287+ const parsed = JSON . parse ( output . trim ( ) ) ;
288+
289+ // Error should be serialized with stack trace
290+ assert . strictEqual ( parsed . msg , 'operation failed' ) ;
291+ assert . strictEqual ( typeof parsed . err , 'object' ) ;
292+ assert . strictEqual ( parsed . err . message , 'test error' ) ;
293+ assert . strictEqual ( parsed . err . code , 'TEST_ERROR' ) ;
294+ assert ( parsed . err . stack ) ;
295+
296+ fs . unlinkSync ( tmpfile ) ;
297+ }
298+
299+ // Test Error as first argument
300+ {
301+ const tmpfile = path . join ( os . tmpdir ( ) , `test-log-${ process . pid } -error-first.json` ) ;
302+ const handler = new JSONHandler ( {
303+ stream : fs . openSync ( tmpfile , 'w' ) ,
304+ level : 'info' ,
305+ } ) ;
306+ const logger = new Logger ( { handler } ) ;
307+
308+ const err = new Error ( 'boom' ) ;
309+ logger . error ( err ) ; // Error as first arg
310+
311+ handler . flushSync ( ) ;
312+ handler . end ( ) ;
313+ const output = fs . readFileSync ( tmpfile , 'utf8' ) ;
314+ const parsed = JSON . parse ( output . trim ( ) ) ;
315+
316+ assert . strictEqual ( parsed . msg , 'boom' ) ; // message from error
317+ assert . strictEqual ( typeof parsed . err , 'object' ) ;
318+ assert ( parsed . err . stack ) ;
319+
320+ fs . unlinkSync ( tmpfile ) ;
321+ }
322+
323+ // Test child logger with parent fields merge
324+ {
325+ const tmpfile = path . join ( os . tmpdir ( ) , `test-log-${ process . pid } -child-merge.json` ) ;
326+ const handler = new JSONHandler ( {
327+ stream : fs . openSync ( tmpfile , 'w' ) ,
328+ level : 'info' ,
329+ fields : { service : 'api' } , // handler fields
330+ } ) ;
331+ const logger = new Logger ( { handler } ) ;
332+ const childLogger = logger . child ( { requestId : '123' } ) ; // child bindings
333+
334+ childLogger . info ( 'request processed' , { duration : 150 } ) ; // log fields
335+
336+ handler . flushSync ( ) ;
337+ handler . end ( ) ;
338+ const output = fs . readFileSync ( tmpfile , 'utf8' ) ;
339+ const parsed = JSON . parse ( output . trim ( ) ) ;
340+
341+ // Merge order: handler fields < bindings < log fields
342+ assert . strictEqual ( parsed . service , 'api' ) ;
343+ assert . strictEqual ( parsed . requestId , '123' ) ;
344+ assert . strictEqual ( parsed . duration , 150 ) ;
345+ assert . strictEqual ( parsed . msg , 'request processed' ) ;
346+
347+ fs . unlinkSync ( tmpfile ) ;
348+ }
349+
350+ // Test field override priority
351+ {
352+ const tmpfile = path . join ( os . tmpdir ( ) , `test-log-${ process . pid } -override.json` ) ;
353+ const handler = new JSONHandler ( {
354+ stream : fs . openSync ( tmpfile , 'w' ) ,
355+ level : 'info' ,
356+ fields : { env : 'dev' , version : '1.0' } ,
357+ } ) ;
358+ const logger = new Logger ( { handler } ) ;
359+ const childLogger = logger . child ( { env : 'staging' } ) ;
360+
361+ childLogger . info ( 'test' , { env : 'production' } ) ;
362+
363+ handler . flushSync ( ) ;
364+ handler . end ( ) ;
365+ const output = fs . readFileSync ( tmpfile , 'utf8' ) ;
366+ const parsed = JSON . parse ( output . trim ( ) ) ;
367+
368+ // Log fields should override everything
369+ assert . strictEqual ( parsed . env , 'production' ) ;
370+ assert . strictEqual ( parsed . version , '1.0' ) ;
371+
372+ fs . unlinkSync ( tmpfile ) ;
373+ }
0 commit comments