Sunday, February 15, 2015

A Simple Perl Logger with Log Timestamp in Milliseconds

A simple Per Logger with log timestamp in milliseconds.
#!/usr/bin/perl
use strict;
use Time::HiRes qw/gettimeofday/;
use POSIX qw/strftime/;

log_error("This is error");
log_info("This is information");

############################################################
# Logger - Error
############################################################
sub log_error {
    my $message = shift;
    log_it( $message, "ERROR" );
}

############################################################
# Logger - Info
############################################################
sub log_info {
    my $message = shift;
    log_it( $message, "INFO" );
}

############################################################
# Logger - Info
############################################################
sub log_it {
    my $message = shift;
    my $level   = shift;
    my ( $time, $ms ) = gettimeofday();
    my $logtimestamp = 
         strftime( "%Y-%m-%d %H:%M:%S", localtime($time) );

    $ms = sprintf( "%03d", $ms / 1000 );
    printf( "%s.%03d %-5s %s\n", 
         $logtimestamp, $ms, $level, $message );
}

Sample Output

2015-02-15 16:27:34.822 ERROR This is error
2015-02-15 16:27:34.823 INFO  This is information

Thursday, January 22, 2015

How to Find UTC Time Offset from your Local Time?

This is the sample script to find the offset in seconds of your local time from GMT.
#!/usr/bin/perl
use strict;

use POSIX;

my @local = localtime();
my @utc   = gmtime();

my $timezone_offset = mktime(@local) - mktime(@utc);

print "Offset in second(s) from GMT = $timezone_offset\n";
print "Offset in hour(s) from GMT = " . ($timezone_offset / 3600) . "\n";

Output:
Offset in second(s) from GMT = 28800
Offset in hour(s) from GMT = 8

Saturday, January 12, 2013

Use Notepad++ as Perl IDE

If you are looking for a fast and small in size text editor as Perl IDE, Notepad++ probably one of the best choice. By default, Notepad++ will not run Perl script within the editor, you need install and configure "NppExec" plugin to allow this.
Follows the below steps:
  1. Install "NppExec" from "Plugin Manager".
  2. After installed the plugin, from "Plugins" menu, choose "NppExec" -> "Execute...".
  3. Copy the following script and click "Save..." to save it as "Run Perl". Click "OK" to close the dialog.
    NPP_SAVE
    cd "$(CURRENT_DIRECTORY)"
    C:\perl\bin\perl "$(FILE_NAME)" 
  4. From "Plugins" menu, choose "NppExec" -> "Advanced Options...".
  5. Check "Place to the Macros submenu" which allows us to execute the script from "Macros" menu.
  6. Type "Run Perl" in "Item name" text box and associate it with "Run Perl" script, and click "Add/Modify" to create it. menu item. Click "OK" to save the settings.
  7. Now, assign a shortcut key e.g. "Ctrl+Shift+B" to execute the script. From "Settings" menu, choose "Shortcut Mapper" -> "Plugin commands" tab, select "Run Perl" and click "Modify" to assign "Ctrl+Shift+B" or any preferable key. Click "OK" to save it.
  8. Restart Notepad++.
  9. After restarted, you can execute an opened Perl script by pressing "Ctrl+Shift+B". 

Friday, April 13, 2012

How to Convert a file from Windows format to Linux format

This is a Perl implementation of "dos2unix" (a utility to convert Windows/DOS file format to Linux/UNIX file format.

#!/usr/bin/perl
use strict;

use File::Copy;
use File::Temp;

my $FILENAME = "MY_FILE.txt";

dos2ux($FILENAME);

sub dos2ux {
    my $infile = shift;
    my $is_converted = 0;

    # Make a temp file which will self delete automatically.
    my $tmp = File::Temp->new(
        UNLINK => 1);

    if (open(INFILE, $infile)) {
        if (open (OUTFILE, ">", $tmp->filename)) {
            binmode(OUTFILE);
            while () {
                s/\r\n$/\n/;
                print OUTFILE $_;
            }
            close(OUTFILE);
            $is_converted = 1;
        }

        close(INFILE);
    } else {
        print "Fail to open $infile\n";
    }

    if ($is_converted) {
        copy($tmp->filename, $infile);
    }
}

Sunday, April 8, 2012

Remove Old Log Files with Keeping Minimum Number of Copies

It is very common that not all the application will purge their old log files. Hence, you need to purge the old log files (backup ZIP files) before the system runs out of space.

Normally, we will purge the old files based on date. If the application does not run for long time, all the log files will be purged. Sometime, we would like to keep the minimum number of old log files to find out when it was run.


The script will be based on the following conditions:

1. Keep Minimum Number of Copies - If the application does not run, we will keep the log files even they are few months old. It will not proceed to check the keep days if failed to meet this condition.

2. Keep Days - It will remove all the logs that older than the specify days. To keep up to today log, specify zero day.


#!/usr/bin/perl
# $Id: purge_files.pl,v 1.4 2012/04/19 13:38:55 chinsiang Exp chinsiang $
#
use strict;
use Getopt::Long;
use POSIX;
use File::Spec;
use File::Glob;

my $VERSION = "1.0";
# 0 = keep today, 1 = keep today & yesterday, etc
my $BACKUP_DAYS_TO_KEEP = 14;
# Specify number of copies to keep. If less the this number, 
# housekeeping will be aborted.
my $BACKUP_COPY_TO_KEEP = 10;

my $FILE_PATTERN = '*.log';

my %option;
die unless GetOptions(
    "file-pattern=s" => \$option{'file-pattern'},
    "backup-copy=i"  => \$option{'backup-copy'},
    "keep-days=i"    => \$option{'keep-days'},
    "test"           => \$option{'test'},
    "verbose"        => \$option{'verbose'},
    "help"           => \$option{'help'},
);

if (defined( $option{'help'} )) {
    print_usage();
}

if (!defined($option{'file-pattern'})) {
    $option{'file-pattern'} = $FILE_PATTERN;
}


if (!defined($option{'backup-copy'})) {
    $option{'backup-copy'} = $BACKUP_COPY_TO_KEEP;
}

if (!defined($option{'keep-days'})) {
    $option{'keep-days'} = $BACKUP_DAYS_TO_KEEP;
}

if (!defined($option{'test'})) {
    $option{'test'} = 0;
}


purge_files($option{'file-pattern'}, 
            $option{'keep-days'}, 
            $option{'backup-copy'},
            $option{'test'},
            $option{'verbose'});

##############################################################################
# Purge the files
sub purge_files() {
    my $file_pattern   = shift;
    my $keep_days      = shift;
    my $backup_to_keep = shift;
    my $test           = shift;
    my $verbose        = shift;
    
    my $DAY_IN_SECONDS = 86400;
    
    my %files = get_sorted_files($file_pattern);
        
    my $file_count = scalar(keys(%files));                
    my $now = time();
    my $delete_time = $now - ($DAY_IN_SECONDS * $keep_days);
    
    # Adjust to localtime 12am    
    $delete_time -= (($delete_time % $DAY_IN_SECONDS ) + tzoffset()); 
    
    if ($verbose) {   
        print "Delete file older than " 
              . strftime("%Y-%m-%d %H:%M:%S", localtime($delete_time)) . "\n";
    }
            
    
    if ($verbose) {
        print "Found files\n";
        my $index = 0;
        foreach my $file (sort { $files{$a} cmp $files{$b} } keys %files) {
            $index++;
            printf("%2d. %s %s\n", 
                    $index,
                    $file,
                    strftime("%Y-%m-%d %H:%M:%S", localtime( $files{$file} ))
                    );
        }
    }
            
    foreach my $file (sort { $files{$a} cmp $files{$b} } keys %files) {                
        my $file_modified = $files{$file};


        if ($file_count > $backup_to_keep) {              
            if ($file_modified < $delete_time) {
                $file_count--;                

                if ($test) {
                    print "Test: Delete $file, "
                          . "Modified: " 
                          . strftime("%Y-%m-%d %H:%M:%S", localtime($file_modified)) 
                          . "\n";
                }
                else {
                    if (unlink($file) > 0) {
                        print "Deleted: $file, "
                              . "Modified: " 
                              . strftime("%Y-%m-%d %H:%M:%S", localtime($file_modified)) 
                              . "\n";
                    }
                }

            }
        }     
        else {
            # Exit the loop
            last;
        }            
    }
}

##############################################################################
# Get sorted files into a hash table.
sub get_sorted_files {
    my $path  = shift;
    my $regex = shift;

    my @files = glob($path);
    my %hash = ();

    foreach my $file (@files) {
        $hash{$file} = (stat($file))[9];
    }

    return %hash;
}

##############################################################################
# Find the timezone in seconds.
sub tzoffset {
    my $t = time();
    my $utc = mktime(gmtime($t));
    my $local = mktime(localtime($t));

    return ($local - $utc);
}

##############################################################################
# Print usage
sub print_usage {
    my $usage =<<EOF
DESCRIPTION:
    Purge old records    

OPTIONS:
    --[f]ile-pattern File pattern in regex to purge. (Default: $FILE_PATTERN)
    --[b]ackup-copy  Minimum backup copy to keep. (Default: $BACKUP_COPY_TO_KEEP copies)
    --[k]eep-days    Number of days to keep. 
                     0 = Today, 1 = Today & Yesterday, etc.
                     (Default: $BACKUP_DAYS_TO_KEEP days)
    --[t]est         Test run without purge the files.
    --[v]erbose      Increase verbosity.
    --[h]elp         Show this help text.
   

EXAMPLE:
    \$ $0 --keep-days 10 --backup-copy 1 --file-pattern "~/tmp/*log" --v
EOF
    die($usage);
}

__END__


Wednesday, November 16, 2011

Perl Quick References

Posting my personal Perl quick reference.

Monday, October 31, 2011

Perl Quick Start Template

When starting a new script, you can use this template as a base which consists of:
  1. RCS, CVS or SVN "Id" keyword to keep the version information.
  2. Support script arguments.
  3. Show usage function.
  4. A trim function.

#!/usr/bin/perl -w
# $Id: $
#
use strict;
use Getopt::Long;

my $VERSION = "1.0";

my %option;
die unless GetOptions(
    "help"         => \$option{'help'},
);

if (defined( $option{'help'} )) {
    print_usage();
}

...


# Perl trim function to remove whitespace from the start and end of the string
sub trim {
    my $string = shift;
    $string =~ s/^\s+//;
    $string =~ s/\s+$//;
    return $string;
}

# Print usage
sub print_usage {
    my $usage = <<EOF;
DESCRIPTION:
    

OPTIONS:
    --[h]elp    Show this help text.

EXAMPLE:
    \$ $0
EOF
    die($usage);
}