@@ -278,7 +278,7 @@ pub const Logger = struct {
278278
279279 fn rotateLogFileIfNeeded (self : * Logger ) ! void {
280280 const now = std .time .timestamp ();
281- const current_date = now / ( 24 * 60 * 60 ); // Days since epoch
281+ const current_date = @divTrunc ( now , 24 * 60 * 60 ); // Days since epoch
282282
283283 if (self .current_date == current_date and self .current_file != null ) {
284284 // Check file size
@@ -297,7 +297,7 @@ pub const Logger = struct {
297297
298298 // Open new log file: tri-YYYY-MM-DD.jsonl
299299 var buffer : [32 ]u8 = undefined ;
300- const date_str = std .fmt .formatIntBuf (buffer [0.. ], current_date , 10 , .lower , .{});
300+ const date_str = std .fmt .formatIntBuf (buffer [0.. ], @intCast ( current_date ) , 10 , .lower , .{});
301301
302302 const filename = try std .fmt .allocPrint (self .allocator , "tri-{s}.jsonl" , .{date_str });
303303 defer self .allocator .free (filename );
@@ -364,17 +364,80 @@ pub fn getGlobalLogger() ?*Logger {
364364 return if (global_logger ) | * l | & l else null ;
365365}
366366
367+ // Observer role prefixes for structured logging
368+ pub const ObserverRole = enum {
369+ oracle ,
370+ sentinel ,
371+ muse ,
372+ scholar ,
373+ chronos ,
374+ cortex ,
375+ salience ,
376+ pathology ,
377+ voice ,
378+ thalamus ,
379+ hippocampus ,
380+ phoenix ,
381+ basal_ganglia ,
382+ evolution ,
383+ farm ,
384+ arena ,
385+ };
386+
387+ /// Convert observer role to bracketed prefix string
388+ fn rolePrefixToString (role : ? ObserverRole ) []const u8 {
389+ if (role ) | r | {
390+ return switch (r ) {
391+ .oracle = > "[ORACLE] " ,
392+ .sentinel = > "[SENTINEL] " ,
393+ .muse = > "[MUSE] " ,
394+ .scholar = > "[SCHOLAR] " ,
395+ .chronos = > "[CHRONOS] " ,
396+ .cortex = > "[CORTEX] " ,
397+ .salience = > "[SALIENCE] " ,
398+ .pathology = > "[PATHOLOGY] " ,
399+ .voice = > "[VOICE] " ,
400+ .thalamus = > "[THALAMUS] " ,
401+ .hippocampus = > "[HIPPOCAMPUS] " ,
402+ .phoenix = > "[PHOENIX] " ,
403+ .basal_ganglia = > "[BASAL_GANGLIA] " ,
404+ .evolution = > "[EVOLUTION] " ,
405+ .farm = > "[FARM] " ,
406+ .arena = > "[ARENA] " ,
407+ };
408+ }
409+ return "" ;
410+ }
411+
367412/// Convenience functions using global logger
368- pub fn log (level : Level , message : []const u8 ) void {
413+ /// logNarrate accepts optional role prefix for observer identification
414+ pub fn logNarrate (level : Level , role : ? ObserverRole , message : []const u8 ) void {
369415 if (getGlobalLogger ()) | logger | {
370- var entry = LogEntry .init (logger .allocator , level , message );
416+ // Prepend role prefix if provided
417+ const prefix = rolePrefixToString (role );
418+ const full_message = std .fmt .allocPrint (std .heap .page_allocator , "{s}{s}" , .{ prefix , message }) catch {
419+ // Fallback to message alone if formatting fails
420+ var entry = LogEntry .init (logger .allocator , level , message );
421+ defer entry .deinit ();
422+ logger .log (entry ) catch | err | {
423+ std .log .debug ("structured_log: failed to write log entry: {}" , .{err });
424+ };
425+ return ;
426+ };
427+ defer std .heap .page_allocator .free (full_message );
428+
429+ var entry = LogEntry .init (logger .allocator , level , full_message );
371430 defer entry .deinit ();
372431 logger .log (entry ) catch | err | {
373432 std .log .debug ("structured_log: failed to write log entry: {}" , .{err });
374433 };
375434 }
376435}
377436
437+ pub fn log (level : Level , message : []const u8 ) void {
438+ logNarrate (level , null , message );
439+ }
440+
378441pub fn debugFmt (comptime fmt : []const u8 , args : anytype ) void {
379442 const msg = std .fmt .allocPrint (std .heap .page_allocator , fmt , args ) catch {
380443 log (.debug , "unformattable" );
@@ -384,6 +447,16 @@ pub fn debugFmt(comptime fmt: []const u8, args: anytype) void {
384447 log (.debug , msg );
385448}
386449
450+ /// debugNarrate with optional role prefix
451+ pub fn debugNarrate (role : ? ObserverRole , comptime fmt : []const u8 , args : anytype ) void {
452+ const msg = std .fmt .allocPrint (std .heap .page_allocator , fmt , args ) catch {
453+ logNarrate (.debug , role , "unformattable" );
454+ return ;
455+ };
456+ defer std .heap .page_allocator .free (msg );
457+ logNarrate (.debug , role , msg );
458+ }
459+
387460pub fn infoFmt (comptime fmt : []const u8 , args : anytype ) void {
388461 const msg = std .fmt .allocPrint (std .heap .page_allocator , fmt , args ) catch {
389462 log (.info , "unformattable" );
@@ -393,6 +466,16 @@ pub fn infoFmt(comptime fmt: []const u8, args: anytype) void {
393466 log (.info , msg );
394467}
395468
469+ /// infoNarrate with optional role prefix
470+ pub fn infoNarrate (role : ? ObserverRole , comptime fmt : []const u8 , args : anytype ) void {
471+ const msg = std .fmt .allocPrint (std .heap .page_allocator , fmt , args ) catch {
472+ logNarrate (.info , role , "unformattable" );
473+ return ;
474+ };
475+ defer std .heap .page_allocator .free (msg );
476+ logNarrate (.info , role , msg );
477+ }
478+
396479pub fn warnFmt (comptime fmt : []const u8 , args : anytype ) void {
397480 const msg = std .fmt .allocPrint (std .heap .page_allocator , fmt , args ) catch {
398481 log (.warn , "unformattable" );
@@ -402,6 +485,16 @@ pub fn warnFmt(comptime fmt: []const u8, args: anytype) void {
402485 log (.warn , msg );
403486}
404487
488+ /// warnNarrate with optional role prefix
489+ pub fn warnNarrate (role : ? ObserverRole , comptime fmt : []const u8 , args : anytype ) void {
490+ const msg = std .fmt .allocPrint (std .heap .page_allocator , fmt , args ) catch {
491+ logNarrate (.warn , role , "unformattable" );
492+ return ;
493+ };
494+ defer std .heap .page_allocator .free (msg );
495+ logNarrate (.warn , role , msg );
496+ }
497+
405498pub fn errorFmt (comptime fmt : []const u8 , args : anytype ) void {
406499 const msg = std .fmt .allocPrint (std .heap .page_allocator , fmt , args ) catch {
407500 log (.err , "unformattable" );
@@ -411,6 +504,16 @@ pub fn errorFmt(comptime fmt: []const u8, args: anytype) void {
411504 log (.err , msg );
412505}
413506
507+ /// errorNarrate with optional role prefix
508+ pub fn errorNarrate (role : ? ObserverRole , comptime fmt : []const u8 , args : anytype ) void {
509+ const msg = std .fmt .allocPrint (std .heap .page_allocator , fmt , args ) catch {
510+ logNarrate (.err , role , "unformattable" );
511+ return ;
512+ };
513+ defer std .heap .page_allocator .free (msg );
514+ logNarrate (.err , role , msg );
515+ }
516+
414517pub fn criticalFmt (comptime fmt : []const u8 , args : anytype ) void {
415518 const msg = std .fmt .allocPrint (std .heap .page_allocator , fmt , args ) catch {
416519 log (.critical , "unformattable" );
@@ -420,6 +523,16 @@ pub fn criticalFmt(comptime fmt: []const u8, args: anytype) void {
420523 log (.critical , msg );
421524}
422525
526+ /// criticalNarrate with optional role prefix
527+ pub fn criticalNarrate (role : ? ObserverRole , comptime fmt : []const u8 , args : anytype ) void {
528+ const msg = std .fmt .allocPrint (std .heap .page_allocator , fmt , args ) catch {
529+ logNarrate (.critical , role , "unformattable" );
530+ return ;
531+ };
532+ defer std .heap .page_allocator .free (msg );
533+ logNarrate (.critical , role , msg );
534+ }
535+
423536/// Write JSON string with proper escaping
424537fn writeJsonString (allocator : std.mem.Allocator , writer : anytype , str : []const u8 ) ! void {
425538 _ = allocator ;
@@ -485,3 +598,53 @@ test "writeJsonString escapes special characters" {
485598
486599 try std .testing .expectEqualStrings ("\" Hello\\ nWorld\\ \"\" " , result );
487600}
601+
602+ test "rolePrefixToString returns correct prefixes" {
603+ try std .testing .expectEqualStrings ("[ORACLE] " , rolePrefixToString (.oracle ));
604+ try std .testing .expectEqualStrings ("[SENTINEL] " , rolePrefixToString (.sentinel ));
605+ try std .testing .expectEqualStrings ("[MUSE] " , rolePrefixToString (.muse ));
606+ try std .testing .expectEqualStrings ("[SCHOLAR] " , rolePrefixToString (.scholar ));
607+ try std .testing .expectEqualStrings ("[CHRONOS] " , rolePrefixToString (.chronos ));
608+ try std .testing .expectEqualStrings ("[CORTEX] " , rolePrefixToString (.cortex ));
609+ try std .testing .expectEqualStrings ("[SALIENCE] " , rolePrefixToString (.salience ));
610+ try std .testing .expectEqualStrings ("[PATHOLOGY] " , rolePrefixToString (.pathology ));
611+ try std .testing .expectEqualStrings ("[VOICE] " , rolePrefixToString (.voice ));
612+ try std .testing .expectEqualStrings ("[THALAMUS] " , rolePrefixToString (.thalamus ));
613+ try std .testing .expectEqualStrings ("[HIPPOCAMPUS] " , rolePrefixToString (.hippocampus ));
614+ try std .testing .expectEqualStrings ("[PHOENIX] " , rolePrefixToString (.phoenix ));
615+ try std .testing .expectEqualStrings ("[BASAL_GANGLIA] " , rolePrefixToString (.basal_ganglia ));
616+ try std .testing .expectEqualStrings ("[EVOLUTION] " , rolePrefixToString (.evolution ));
617+ try std .testing .expectEqualStrings ("[FARM] " , rolePrefixToString (.farm ));
618+ try std .testing .expectEqualStrings ("[ARENA] " , rolePrefixToString (.arena ));
619+ try std .testing .expectEqualStrings ("" , rolePrefixToString (null ));
620+ }
621+
622+ test "logNarrate with role prefixes message" {
623+ // Just verify the function compiles and doesn't crash
624+ // Actual file writing requires global logger to be initialized
625+ logNarrate (.info , .oracle , "System state updated" );
626+ logNarrate (.info , .cortex , "Neural pattern detected" );
627+ logNarrate (.info , .hippocampus , "Memory consolidation complete" );
628+ logNarrate (.info , null , "Regular message without prefix" );
629+ }
630+
631+ test "infoNarrate with role prefixes message" {
632+ infoNarrate (.oracle , "Deep review complete" , .{});
633+ infoNarrate (.scholar , "Research findings recorded" , .{});
634+ infoNarrate (.phoenix , "Cell regeneration triggered" , .{});
635+ infoNarrate (null , "Standard info message" , .{});
636+ }
637+
638+ test "warnNarrate with role prefixes message" {
639+ warnNarrate (.sentinel , "Risk threshold exceeded" , .{});
640+ warnNarrate (.pathology , "Anomaly detected in patterns" , .{});
641+ warnNarrate (.evolution , "Kill event logged" , .{});
642+ warnNarrate (null , "Standard warning" , .{});
643+ }
644+
645+ test "errorNarrate with role prefixes message" {
646+ errorNarrate (.farm , "Training service crashed" , .{});
647+ errorNarrate (.arena , "Battle evaluation failed" , .{});
648+ errorNarrate (.basal_ganglia , "Pattern matching error" , .{});
649+ errorNarrate (null , "Standard error" , .{});
650+ }
0 commit comments