@@ -7,17 +7,32 @@ use utf8;
77use feature ' say' ;
88use POSIX;
99use FindBin;
10+ use Time::HiRes;
11+ use Config;
1012
1113use Encode;
1214use File::ReadBackwards;
1315use Compress::Zlib;
16+ use Mojo::Log;
1417use LANraragi::Model::Config;
18+ use LANraragi::Utils::RotatingLog;
1519use LANraragi::Utils::Redis qw( redis_decode) ;
20+ use LANraragi::Utils::TempFolder qw( get_temp) ;
21+
22+ use constant IS_UNIX => ( $Config {osname } ne ' MSWin32' );
1623
1724# Contains all functions related to logging.
1825use Exporter ' import' ;
1926our @EXPORT_OK = qw( get_logger get_plugin_logger get_logdir get_lines_from_file) ;
2027
28+ BEGIN {
29+ if ( !IS_UNIX ) {
30+ require Win32API::File;
31+ }
32+ }
33+
34+ our %LOGGER_CACHE ;
35+
2136# Get the Log folder.
2237sub get_logdir {
2338
@@ -38,80 +53,80 @@ sub get_logger {
3853 my $pgname = $_ [0];
3954 my $logfile = $_ [1];
4055
41- my $logpath = get_logdir . " /$logfile .log" ;
42-
43- if ( -e $logpath && -s $logpath > 1048576 ) {
44-
45- # Rotate log if it's > 1MB
46- say " Rotating logfile $logfile " ;
47-
48- # Based on Logfile::Rotate
49- # Rotate existing logs
50- for ( my $i = 7; $i > 1; $i -- ) {
51- my $j = $i - 1;
52- my $next = " $logpath .$i .gz" ;
53- my $prev = " $logpath .$j .gz" ;
54- if ( -r $prev && -f $prev ) {
55- rename ( $prev , $next ) or die " error: rename failed: ($prev ,$next )" ;
56- }
56+ my $logpath = get_logdir . " /$logfile .log" ;
57+ my $cache_key = " $logfile |$pgname " ;
58+ my $log ;
59+
60+ # Reuse cached logger if exists, otherwise clean cache and recreate
61+ my $cache_refresh_error ;
62+ if ( exists $LOGGER_CACHE {$cache_key } && -e $logpath ) {
63+ $log = $LOGGER_CACHE {$cache_key };
64+
65+ my $ok ;
66+ {
67+ local $@ ;
68+ $ok = eval {
69+ LANraragi::Utils::RotatingLog::refresh_logger_handle($log );
70+ 1;
71+ };
72+ $cache_refresh_error = $@ unless $ok ;
5773 }
5874
59- # Rotate current log and Gzip-it
60- my $gz = gzopen( " $logpath .1.gz" , " wb" ) or die " error: could not gzopen $logpath : $! " ;
61-
62- open ( my $handle , ' <' , $logpath ) or die " Couldn't open $logpath :" . $! ;
63- my $buffer ;
64- $gz -> gzwrite($buffer ) while read ( $handle , $buffer , 4096 ) > 0;
65- $gz -> gzclose();
66- close $handle ;
67-
68- unlink $logpath or die " error: could not delete $logpath : $! " ;
75+ if ($ok ) {
76+ return $log ;
77+ } else {
78+ delete $LOGGER_CACHE {$cache_key };
79+ undef $log ; # avoid returning a stale handle
80+ }
6981 }
7082
71- my $log = Mojo::Log-> new(
72- path => $logpath ,
73- level => ' info'
74- );
75-
76- my $devmode = LANraragi::Model::Config-> enable_devmode;
77-
78- # Tell logger to store debug logs as well in debug mode
79- if ($devmode ) {
80- $log -> level(' debug' );
83+ # Create and cache logger with retry + backoff + jitter
84+ # Report the first logger init failure if exists
85+ my $tries = 0;
86+ my $first_error ;
87+ while ( $tries < 3 ) {
88+ my $ok ;
89+ my $err ;
90+ {
91+ local $@ ;
92+ $ok = eval {
93+ $log = LANraragi::Utils::RotatingLog-> new(
94+ path => $logpath ,
95+ level => ' info' ,
96+ logfile => $logfile ,
97+ tempdir => get_temp()
98+ );
99+ 1;
100+ };
101+ $err = $@ unless $ok ;
102+ }
103+ if ($ok ) {
104+ configure_logger($log , $pgname );
105+ $LOGGER_CACHE {$cache_key } = $log ;
106+ last ;
107+ } else {
108+ $first_error //= $err ;
109+ Time::HiRes::sleep (rand ());
110+ $tries ++;
111+ }
81112 }
82113
83- # Step down into trace if we're launched from npm run dev-server-verbose
84- if ( $ENV {LRR_DEVSERVER } ) {
85- $log -> level(' trace' );
114+ # Fall back to Mojo::Log if retry doesn't work
115+ if ( !$log ) {
116+ $log = Mojo::Log-> new(
117+ path => $logpath ,
118+ level => ' info'
119+ );
120+ configure_logger( $log , $pgname );
121+ $log -> error(" RotatingLog init failed, falling back to Mojo::Log. First error: $first_error " );
122+ } elsif ( $tries > 0 ) {
123+ $log -> warn (" RotatingLog initialized after $tries failures. First error: $first_error " );
86124 }
87125
88- # Copy logged messages to STDOUT with the matching name
89- $log -> on(
90- message => sub {
91- my ( $time , $level , @lines ) = @_ ;
92-
93- # Like with logging to file, debug logs are only printed in debug mode
94- unless ( $devmode == 0 && ( $level eq ' debug' || $level eq ' trace' ) ) {
95- print " [$pgname ] [$level ] " ;
96- say $lines [0];
97- }
98- }
99- );
100-
101- $log -> format(
102- sub {
103- my ( $time , $level , @lines ) = @_ ;
104- my $time2 = strftime( " %Y -%m -%d %H :%M :%S " , localtime ($time ) );
105-
106- my $logstring = join ( " \n " , @lines );
107-
108- # We'd like to make sure we always show proper UTF-8.
109- # redis_decode, while not initially designed for this, does the job.
110- $logstring = redis_decode($logstring );
111-
112- return " [$time2 ] [$pgname ] [$level ] $logstring \n " ;
113- }
114- );
126+ # Report cache refresh error if exists
127+ if ( $cache_refresh_error ) {
128+ $log -> error($cache_refresh_error );
129+ }
115130
116131 return $log ;
117132}
@@ -151,4 +166,51 @@ sub get_lines_from_file {
151166
152167}
153168
169+ # Provide logger with the required configs and formatting
170+ sub configure_logger {
171+
172+ my $logger = shift ;
173+ my $pgname = shift ;
174+
175+ my $devmode = LANraragi::Model::Config-> enable_devmode;
176+
177+ # Tell logger to store debug logs as well in debug mode
178+ if ($devmode ) {
179+ $logger -> level(' debug' );
180+ }
181+
182+ # Step down into trace if we're launched from npm run dev-server-verbose
183+ if ( $ENV {LRR_DEVSERVER } ) {
184+ $logger -> level(' trace' );
185+ }
186+
187+ # Copy logged messages to STDOUT with the matching name
188+ $logger -> on(
189+ message => sub {
190+ my ( $log , $level , @lines ) = @_ ;
191+
192+ # Like with logging to file, debug logs are only printed in debug mode
193+ unless ( $devmode == 0 && ( $level eq ' debug' || $level eq ' trace' ) ) {
194+ print " [$pgname ] [$level ] " ;
195+ say $lines [0];
196+ }
197+ }
198+ );
199+
200+ $logger -> format(
201+ sub {
202+ my ( $time , $level , @lines ) = @_ ;
203+ my $time2 = strftime( " %Y -%m -%d %H :%M :%S " , localtime ($time ) );
204+
205+ my $logstring = join ( " \n " , @lines );
206+
207+ # We'd like to make sure we always show proper UTF-8.
208+ # redis_decode, while not initially designed for this, does the job.
209+ $logstring = redis_decode($logstring );
210+
211+ return " [$time2 ] [$pgname ] [$level ] $logstring \n " ;
212+ }
213+ );
214+ }
215+
1542161;
0 commit comments