2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-16 10:46:03 +00:00

98_freezemon.pm: added attribute fm_logKeep to delete logfiles

git-svn-id: https://svn.fhem.de/fhem/trunk@16330 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
KernSani 2018-03-04 22:01:08 +00:00
parent 7b336dedea
commit ae49390cd2

View File

@ -22,6 +22,8 @@
# #
############################################################################## ##############################################################################
# Changelog: # Changelog:
# 0.0.16: Minor Logging changes
# AUto-delete Logfiles via fm_logKeep
# 0.0.15: New InternalTimer Handling (#81365) - Thanks to Ansgar (noansi) # 0.0.15: New InternalTimer Handling (#81365) - Thanks to Ansgar (noansi)
# New logging function (fm_logFile, fm_logExtraSeconds) incl. get function - Thanks Andy (gandy) # New logging function (fm_logFile, fm_logExtraSeconds) incl. get function - Thanks Andy (gandy)
# Fixed unescaped characters in commandref (helmut, #84992) # Fixed unescaped characters in commandref (helmut, #84992)
@ -75,9 +77,10 @@ use warnings;
use Data::Dumper; use Data::Dumper;
use POSIX; use POSIX;
use Time::HiRes qw(gettimeofday); use Time::HiRes qw(gettimeofday);
use Time::HiRes qw(tv_interval);
use B qw(svref_2object); use B qw(svref_2object);
my $version = "0.0.15"; my $version = "0.0.16";
my @logqueue = (); my @logqueue = ();
################################### ###################################
@ -88,7 +91,7 @@ sub freezemon_Initialize($) {
# Module specific attributes # Module specific attributes
my @freezemon_attr = my @freezemon_attr =
( (
"fm_forceApptime:0,1 fm_freezeThreshold disable:0,1 fm_log fm_ignoreDev fm_ignoreMode:off,single,all fm_extDetail:0,1 fm_logExtraSeconds fm_logFile" "fm_forceApptime:0,1 fm_freezeThreshold disable:0,1 fm_log fm_ignoreDev fm_ignoreMode:off,single,all fm_extDetail:0,1 fm_logExtraSeconds fm_logFile fm_logKeep"
); );
$hash->{GetFn} = "freezemon_Get"; $hash->{GetFn} = "freezemon_Get";
@ -175,6 +178,16 @@ sub freezemon_ProcessTimer($) {
#Check Freezes #Check Freezes
if ( $freeze > AttrVal( $name, "fm_freezeThreshold", 1 ) ) { if ( $freeze > AttrVal( $name, "fm_freezeThreshold", 1 ) ) {
my ( $seconds, $microseconds ) = gettimeofday();
my $t0 = [gettimeofday];
my @t = localtime($seconds);
my $tim = sprintf(
"%04d.%02d.%02d %02d:%02d:%02d.%03d",
$t[5] + 1900,
$t[4] + 1,
$t[3], $t[2], $t[1], $t[0], $microseconds / 1000
);
Log3 $name, 5, "[Freezemon] $name: ----------- Starting Freeze handling at $tim ---------------------";
my $dev = $hash->{helper}{apptime}; my $dev = $hash->{helper}{apptime};
my $guys = ""; my $guys = "";
@ -223,7 +236,7 @@ sub freezemon_ProcessTimer($) {
if ( $imode eq "all" ) { if ( $imode eq "all" ) {
foreach my $d ( values %devs ) { foreach my $d ( values %devs ) {
if ( !exists( $id{$d} ) ) { if ( !exists( $id{$d} ) ) {
Log3 $name, 5, "[Freezemon] $name logging $dev in $imode mode, because $d is not ignored"; Log3 $name, 5, "[Freezemon] $name logging: $dev in $imode mode, because $d is not ignored";
$exists = 1; $exists = 1;
last; last;
} }
@ -265,7 +278,7 @@ sub freezemon_ProcessTimer($) {
# Create Log( # Create Log(
my $msg = strftime( my $msg = strftime(
"[Freezemon] $name possible freeze starting at %H:%M:%S, delay is $freeze possibly caused by $dev", "[Freezemon] $name: possible freeze starting at %H:%M:%S, delay is $freeze possibly caused by $dev",
localtime( $hash->{helper}{TIMER} ) ); localtime( $hash->{helper}{TIMER} ) );
my $log = freezemon_dump_log( $hash, $hash->{helper}{TIMER}, $msg ); my $log = freezemon_dump_log( $hash, $hash->{helper}{TIMER}, $msg );
@ -300,6 +313,17 @@ sub freezemon_ProcessTimer($) {
else { else {
Log3 $name, 5, "[Freezemon] $name - $dev was ignored"; Log3 $name, 5, "[Freezemon] $name - $dev was ignored";
} }
( $seconds, $microseconds ) = gettimeofday();
@t = localtime($seconds);
$tim = sprintf(
"%04d.%02d.%02d %02d:%02d:%02d.%03d",
$t[5] + 1900,
$t[4] + 1,
$t[3], $t[2], $t[1], $t[0], $microseconds / 1000
);
my $ms = tv_interval($t0);
Log3 $name, 5, "[Freezemon] $name: ----------- Ending Freeze handling at $tim after $ms --------";
} }
freezemon_purge_log_before( $hash, $hash->{helper}{TIMER} - AttrVal( $name, "fm_logExtraSeconds", 0 ) ) freezemon_purge_log_before( $hash, $hash->{helper}{TIMER} - AttrVal( $name, "fm_logExtraSeconds", 0 ) )
if ( AttrVal( $name, "fm_logFile", "" ) ne "" ); if ( AttrVal( $name, "fm_logFile", "" ) ne "" );
@ -333,6 +357,18 @@ sub freezemon_ProcessTimer($) {
{ {
fhem( "apptime", 1 ); fhem( "apptime", 1 );
} }
# let's get rid of old logs
if ( my $keep = AttrVal( $name, "fm_logKeep", undef ) ) {
my @fl = freezemon_getLogFiles( $name, 1 );
my $path = freezemon_getLogPath($name);
my $max = scalar(@fl) - $keep;
for ( my $i = 0 ; $i < $max ; $i++ ) {
Log3 $name, 3, "[Freezemon] $name: Deleting $fl[$i]";
unlink("$path/$fl[$i]");
}
}
} }
# start next timer # start next timer
@ -344,11 +380,8 @@ sub freezemon_ProcessTimer($) {
################################### ###################################
sub freezemon_Set($@) { sub freezemon_Set($@) {
my ( $hash, $name, $cmd, @args ) = @_; my ( $hash, $name, $cmd, @args ) = @_;
my $usage = "Unknown argument $cmd, choose one of active:noArg inactive:noArg clear:noArg";
#my $usage = 'Unknown argument $args[1], choose one of freeze:noArg log:abc,cde';
#return $usage if ( !defined( $args[1] ) );
return "\"set $name\" needs at least one argument" unless ( defined($cmd) ); return "\"set $name\" needs at least one argument" unless ( defined($cmd) );
if ( $cmd eq "inactive" ) { if ( $cmd eq "inactive" ) {
@ -380,7 +413,7 @@ sub freezemon_Set($@) {
readingsEndUpdate( $hash, 1 ); readingsEndUpdate( $hash, 1 );
} }
else { else {
return "Unknown argument $cmd, choose one of active:noArg inactive:noArg clear:noArg"; return $usage;
} }
return undef; return undef;
} }
@ -393,29 +426,17 @@ sub freezemon_Get($@) {
my $ret = ""; my $ret = "";
my $usage = 'Unknown argument $a[1], choose one of freeze:noArg log:'; my $usage = 'Unknown argument $a[1], choose one of freeze:noArg log:';
return $usage if ( !defined( $a[1] ) ); return "\"get $name\" needs at least one argument" unless ( defined($a[1]) );
#get the logfiles #get the logfiles
my $lf = AttrVal( $name, "fm_logFile", "" ); my @fl = freezemon_getLogFiles($name);
$lf =~ m,^(.*)/([^/%]*).*$,;
my $path = $1;
my $pattern = $2;
$path =~ s/%L/$attr{global}{logdir}/g if ( $path =~ m/%/ && $attr{global}{logdir} );
my @fl;
my $sfl;
if ( opendir( DH, $path ) ) {
while ( my $f = readdir(DH) ) {
push( @fl, $f ) if ( $f =~ /$pattern.*/ );
}
closedir(DH);
@fl = sort { ( CORE::stat("$path/$b") )[9] <=> ( CORE::stat("$path/$a") )[9] } @fl;
}
my $sfl = join( ",", @fl ); my $sfl = join( ",", @fl );
$usage .= $sfl; $usage .= $sfl;
# Get freeze entries # Get freeze entries
if ( $a[1] eq "freeze" ) { if ( $a[1] eq "freeze" ) {
my @colors = ( "red", "yellow", "green", "white", "gray" ); my @colors = ( "red", "yellow", "green", "white", "gray" );
my @freezes = split( ",", ReadingsVal( $name, ".fm_freezes", "" ) ); my @freezes = split( ",", ReadingsVal( $name, ".fm_freezes", "" ) );
foreach (@freezes) { foreach (@freezes) {
@ -436,7 +457,7 @@ sub freezemon_Get($@) {
$ret .= "<font color='$colors[$loglevel-1]'><b>" . $loglevel . "</b></font> - " . $_ . "<br>"; $ret .= "<font color='$colors[$loglevel-1]'><b>" . $loglevel . "</b></font> - " . $_ . "<br>";
} }
Log3 $name, 5, "Get entries: $ret";
return "<html>" . $ret . "</html>"; return "<html>" . $ret . "</html>";
} }
elsif ( $a[1] eq "log" ) { elsif ( $a[1] eq "log" ) {
@ -447,6 +468,7 @@ sub freezemon_Get($@) {
if ( $gf =~ m,^(.*)/([^/]*)$, ) { if ( $gf =~ m,^(.*)/([^/]*)$, ) {
$gf = $2; $gf = $2;
} }
my $path = freezemon_getLogPath($name);
# Build the complete path (using global logfile parameter if necessary) # Build the complete path (using global logfile parameter if necessary)
$path = "$path/$gf"; $path = "$path/$gf";
@ -489,15 +511,18 @@ sub freezemon_Attr($) {
return "Attribute " . $aName . " is either 0 or 1"; return "Attribute " . $aName . " is either 0 or 1";
} }
} }
if ( $aName eq "fm_freezeThreshold" ) { elsif ( $aName eq "fm_freezeThreshold" ) {
if ( !looks_like_number($aVal) ) { if ( !looks_like_number($aVal) ) {
return "Attribute " . $aName . " has to be a number (seconds) "; return "Attribute " . $aName . " has to be a number (seconds) ";
} }
} }
if ( $aName eq "fm_freezeThreshold" ) { elsif ( $aName eq "fm_logKeep" ) {
if ( !looks_like_number($aVal) or $aVal <= 0) {
return "Attribute " . $aName . " has to be a number > 0";
}
} }
if ( $aName eq "fm_logFile" ) { elsif ( $aName eq "fm_logFile" ) {
if ( $aVal ne "" ) { if ( $aVal ne "" ) {
$aVal =~ m,^(.*)/([^/]*)$,; $aVal =~ m,^(.*)/([^/]*)$,;
my $path = $1; my $path = $1;
@ -514,8 +539,7 @@ sub freezemon_Attr($) {
return "Attribute " . $aName . ": Enter a valid path or delete the attribute to disable."; return "Attribute " . $aName . ": Enter a valid path or delete the attribute to disable.";
} }
} }
elsif ( $aName eq "disable" ) {
if ( $aName eq "disable" ) {
if ( $aVal == 1 ) { if ( $aVal == 1 ) {
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
readingsSingleUpdate( $hash, "state", "inactive", 1 ); readingsSingleUpdate( $hash, "state", "inactive", 1 );
@ -531,9 +555,9 @@ sub freezemon_Attr($) {
if ( $aName eq "disable" ) { if ( $aName eq "disable" ) {
freezemon_start($hash); freezemon_start($hash);
} }
if ( $aName eq "fm_logFile" ) { elsif ( $aName eq "fm_logFile" ) {
my $status = Log3( "", 100, "" ); my $status = Log3( "", 100, "" );
Log3( "", 0, "[FREEZEMON] $name: Unwrapping Log3" ); Log3( "", 0, "[Freezemon] $name: Unwrapping Log3" );
*main::Log3 = $hash->{helper}{Log3}; *main::Log3 = $hash->{helper}{Log3};
} }
@ -634,6 +658,9 @@ sub freezemon_apptime($) {
elsif ( $fn eq "HttpUtils_Err" ) { elsif ( $fn eq "HttpUtils_Err" ) {
$shortarg = $shortarg->{hash}{hash}{NAME}; $shortarg = $shortarg->{hash}{hash}{NAME};
} }
elsif ( $fn="FileLog_dailySwitch") {
$shortarg = $shortarg->{NotifyFn};
}
else { else {
Log3 $name, 5, "[Freezemon] $name found something without a name $fn" . Dumper($shortarg); Log3 $name, 5, "[Freezemon] $name found something without a name $fn" . Dumper($shortarg);
$shortarg = "N/A"; $shortarg = "N/A";
@ -736,13 +763,13 @@ sub freezemon_install_log_wrapper($) {
$name = "FreezeMon" unless defined($name); $name = "FreezeMon" unless defined($name);
my $status = Log3( "", 99, "" ); my $status = Log3( "", 99, "" );
if ( !defined($status) || $status ne "already wrapped" ) { if ( !defined($status) || $status ne "already wrapped" ) {
Log3( "", 0, "[FREEZEMON] $name: Wrapping Log3" ); Log3( "", 0, "[Freezemon] $name: Wrapping Log3" );
$hash->{helper}{Log3} = \&Log3; $hash->{helper}{Log3} = \&Log3;
*main::Log3 = freezemon_wrap_Log3( \&Log3 ); *main::Log3 = freezemon_wrap_Log3( \&Log3 );
} }
else { else {
Log3( "", 0, "[FREEZEMON] $name: Log3 is already wrapped" ); Log3( "", 0, "[Freezemon] $name: Log3 is already wrapped" );
Log3( "", 0, "[FREEZEMON] $name: status=$status" ); Log3( "", 0, "[Freezemon] $name: status=$status" );
} }
} }
################################### ###################################
@ -752,14 +779,14 @@ sub freezemon_purge_log_before($$) {
my @t = localtime($before); my @t = localtime($before);
my $tim = sprintf( "%04d.%02d.%02d %02d:%02d:%02d.%03d", $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0], 0 ); my $tim = sprintf( "%04d.%02d.%02d %02d:%02d:%02d.%03d", $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0], 0 );
#Log3 $hash, 5, "[FREEZEMON] $name: purging log entries before $tim."; #Log3 $hash, 5, "[Freezemon] $name: purging log entries before $tim.";
my $cnt = 0; my $cnt = 0;
while ( scalar @logqueue > 0 && $logqueue[0]->[0] < $before ) { while ( scalar @logqueue > 0 && $logqueue[0]->[0] < $before ) {
shift @logqueue; shift @logqueue;
$cnt += 1; $cnt += 1;
} }
#Log3 $hash, 5, "[FREEZEMON] $name: $cnt entries purged from logqueue, size is now ".(scalar @logqueue); #Log3 $hash, 5, "[Freezemon] $name: $cnt entries purged from logqueue, size is now ".(scalar @logqueue);
} }
################################### ###################################
sub freezemon_dump_log($$$) { sub freezemon_dump_log($$$) {
@ -772,7 +799,7 @@ sub freezemon_dump_log($$$) {
my $currlogfile = ResolveDateWildcards( AttrVal( $name, "fm_logFile", undef ), @t ); my $currlogfile = ResolveDateWildcards( AttrVal( $name, "fm_logFile", undef ), @t );
return unless defined($currlogfile) && $currlogfile ne ""; return unless defined($currlogfile) && $currlogfile ne "";
Log3 $hash, 3, "[FREEZEMON] $name: dumping " . ( scalar @logqueue ) . " log entries to $currlogfile"; Log3 $hash, 3, "[Freezemon] $name: dumping " . ( scalar @logqueue ) . " log entries to $currlogfile";
open( fm_LOG, ">>$currlogfile" ) || return ("Can't open $currlogfile: $!"); open( fm_LOG, ">>$currlogfile" ) || return ("Can't open $currlogfile: $!");
@ -798,8 +825,10 @@ sub freezemon_dump_log($$$) {
print fm_LOG "$tim $loglevel: $text\n"; print fm_LOG "$tim $loglevel: $text\n";
$last_ts = $ts; $last_ts = $ts;
} }
print fm_LOG $msg . "\n"; print fm_LOG $msg . "\n";
close(fm_LOG); close(fm_LOG);
return $currlogfile; return $currlogfile;
} }
################################### ###################################
@ -808,6 +837,41 @@ sub freezemon_logLink($$) {
my $ret = "<a href='$FW_ME?cmd=" . urlEncode("get $name log $link") . "&%%CSRF%%'> [Log]</a>"; my $ret = "<a href='$FW_ME?cmd=" . urlEncode("get $name log $link") . "&%%CSRF%%'> [Log]</a>";
return $ret; return $ret;
} }
###################################
sub freezemon_getLogFiles($;$) {
my ( $name, $reverse ) = @_;
my @fl;
my $path = freezemon_getLogPath($name);
return @fl if ( !$path );
my $lf = AttrVal( $name, "fm_logFile", "" );
$lf =~ m,^(.*)/([^/%]*).*$,;
my $pattern = $2;
if ( opendir( DH, $path ) ) {
while ( my $f = readdir(DH) ) {
push( @fl, $f ) if ( $f =~ /$pattern.*/ );
}
closedir(DH);
if ( !$reverse ) {
@fl = sort { ( CORE::stat("$path/$b") )[9] <=> ( CORE::stat("$path/$a") )[9] } @fl;
}
else {
@fl = sort { ( CORE::stat("$path/$a") )[9] <=> ( CORE::stat("$path/$b") )[9] } @fl;
}
}
return @fl;
}
###################################
sub freezemon_getLogPath($) {
my ($name) = @_;
my $lf = AttrVal( $name, "fm_logFile", "" );
return undef if $lf eq "";
$lf =~ m,^(.*)/([^/%]*).*$,;
my $path = $1;
$path =~ s/%L/$attr{global}{logdir}/g if ( $path =~ m/%/ && $attr{global}{logdir} );
return $path;
}
1; 1;
@ -885,6 +949,7 @@ sub freezemon_logLink($$) {
<li>fm_log: dynamic loglevel, takes a string like 10:1 5:2 1:3 , which means: freezes > 10 seconds will be logged with loglevel 1 , >5 seconds with loglevel 2 etc...</li> <li>fm_log: dynamic loglevel, takes a string like 10:1 5:2 1:3 , which means: freezes > 10 seconds will be logged with loglevel 1 , >5 seconds with loglevel 2 etc...</li>
<li>fm_logFile: takes a valid file name (like e.g. ./log/freeze-%Y%m%d-%H%M%S.log). If set, logs messages of loglevel 5 (even if global loglevel is < 5) before a freeze in separate file.</li> <li>fm_logFile: takes a valid file name (like e.g. ./log/freeze-%Y%m%d-%H%M%S.log). If set, logs messages of loglevel 5 (even if global loglevel is < 5) before a freeze in separate file.</li>
<li>fm_logExtraSeconds: defines how much seconds before the freeze are logged (if fm_logFile is set)</li> <li>fm_logExtraSeconds: defines how much seconds before the freeze are logged (if fm_logFile is set)</li>
<li>fm_logKeep: A number that defines how many logFiles should be kept. If set all logfiles except the latest n freezemon logfiles will be deleted regularly.</li>
<li>disable: activate/deactivate freeze detection</li> <li>disable: activate/deactivate freeze detection</li>
</ul> </ul>
</ul> </ul>
@ -969,7 +1034,7 @@ sub freezemon_logLink($$) {
<li>fm_log: dynamischer Loglevel, nimmt einen String der Form 10:1 5:2 1:3 entgegen, was bedeutet: Freezes > 10 Sekunden werden mit Loglevel 1 geloggt, >5 Sekunden mit Loglevel 2 usw...</li> <li>fm_log: dynamischer Loglevel, nimmt einen String der Form 10:1 5:2 1:3 entgegen, was bedeutet: Freezes > 10 Sekunden werden mit Loglevel 1 geloggt, >5 Sekunden mit Loglevel 2 usw...</li>
<li>fm_logFile: ist ein gültiger Filename (wie z.B. ./log/freeze-%Y%m%d-%H%M%S.log). Wenn gesetzt, werdn Meldungen auf Loglevel 5 (auch wenn global Loglevel < 5 ist) vor einem Freeze in einem seperaten File geloggt.</li> <li>fm_logFile: ist ein gültiger Filename (wie z.B. ./log/freeze-%Y%m%d-%H%M%S.log). Wenn gesetzt, werdn Meldungen auf Loglevel 5 (auch wenn global Loglevel < 5 ist) vor einem Freeze in einem seperaten File geloggt.</li>
<li>fm_logExtraSeconds: definiert wieviele Sekunden vor dem Freeze geloggt werden (wenn fm logFile gesetzt ist)</li> <li>fm_logExtraSeconds: definiert wieviele Sekunden vor dem Freeze geloggt werden (wenn fm logFile gesetzt ist)</li>
<li>fm_logKeep: Eine Zahl, die angibt wieviele Logfiles behalten werden sollen. Wenn gesetzt, werden alle Logfiles ausser den letzten n Freezemon Logfiles regelmäßig gelöscht.</li>
<li>disable: aktivieren/deaktivieren der Freeze-Erkennung</li> <li>disable: aktivieren/deaktivieren der Freeze-Erkennung</li>
</ul> </ul>
</ul> </ul>