脚本功能:
脚本使用Perl语言写成,获取服务器系统信息,并发送html格式邮件
由于整篇文章超过了8万字符,所以代码部分没有发全,请下载附件
脚本适用场合:
不便于使用nagios等监控软件
不想安装nagios等监控软件(服务器数量比较少)
脚本适用操作系统:
仅适用于redhat和centos系列
简单使用步骤:
1.安装所需要的Perl模块(需要ROOT身份安装),详看使用脚本的准备工作部分
2.脚本上传至服务器,增加可执行权限
3.定制计划任务
如:0 9-18/1 * * 1,2,3,4,5 /root/getSystemInformation.pl
系统信息包括:
- 系统当前负载
- 总进程数量
- 当前运行的进程数量
- 系统当前远程登录用户数量
- 系统已经运行多长时间
- 系统版本、系统位数、内核版本
- 当前网卡流量
- 磁盘使用情况
- 内存使用情况
- SWAP使用情况
- 当前关键进程信息
- 当前80端口的网络连接状态
- mysql主从复制状态
- /var/log/messages中的错误信息(如果只想要该功能参考 http://linux5588.blog.51cto.com/65280/973962 上一个博文)
邮件效果如下:
使用脚本的准备工作:
需要安装以下perl模块,需要root身份来安装这些模块
Authen::SASL 用于验证邮件的用户名和密码
Filesys::Df 用于获取磁盘分区信息
DBI 用于连接mysql数据库(如果不需要检查mysql主从状态,则不需要安装,同时注释 require 'DBI.pm';)
DBD::mysql 用于连接mysql数据库(如果不需要检查mysql主从状态,则不需要安装,同时注释 require 'DBD/mysql.pm';)
安装可先检查这些模块的安装情况,使用以下命令进行检查
- perl -e "use Authen::SASL" 其他模块也是如此检查
- perl -e "use Filesys::Df"
- perl -e "use DBI"
- perl -e "use DBD::mysql"
执行完成后,没有任何输出则表示,该模块已安装,如果有报错信息,则需要安装该模块.
注意不需要检查mysql主从状态的,则不需要安装DBI DBD::mysql
如果需要安装DBI DBD::mysql 这2个模块的话,请确保PATH环境变量里面有Mysql的bin目录,不然安装过程会报错
- export PATH=$PATH:/usr/local/mysql/bin
然后再进行以下的操作。
安装方式有以下2种:
第一种:一键安装所需的模块(服务器需能上网)
- cpan Authen::SASL Filesys::Df DBI DBD::mysql
如果有像这样的询问
- Are you ready for manual configuration? [yes] no
这里选择no,因为这是询问你是否要手动配置,手工配置比较麻烦,还是让程序自动配置一些路径。
如果有像这样的询问
- Shall I follow them and prepend them to the queue
- of modules we are processing right now? [yes] 这里直接回车即可
第二种:进行手工编译安装
Authen::SASL 下载地址:
- http://search.cpan.org/CPAN/authors/id/G/GB/GBARR/Authen-SASL-2.16.tar.gz
Filesys::Df 下载地址:
- http://search.cpan.org/CPAN/authors/id/I/IG/IGUTHRIE/Filesys-Df-0.92.tar.gz
DBI 下载地址:
- http://search.cpan.org/CPAN/authors/id/T/TI/TIMB/DBI-1.622.tar.gz
DBD::mysql 下载地址:
- http://search.cpan.org/CPAN/authors/id/C/CA/CAPTTOFU/DBD-mysql-4.022.tar.gz
手工编译安装一般按照这个步骤(最好先看看目录下面的README文件),比如安装Filesys::Df这个模块
- tar zxf Filesys-Df-0.92.tar.gz
- cd Filesys-Df-0.92
- perl Makefile.PL
- make
- make test
- make install
脚本内容如下:
脚本中红色注释请注意,由于整篇文章超过了8万字符,所以代码部分没有发全,请下载附件。
- #!/usr/bin/perl
- use Cwd; #获取当前目录路径
- use Sys::Hostname; #获取主机名
- use Socket; #用于获取IP地址
- use Net::SMTP; #用于发送邮件
- use Authen::SASL; #用于验证邮件的用户名和密码
- use File::Basename; #用于取得脚本的名字
- use Filesys::Df; #用于获取磁盘分区信息
- require 'sys/ioctl.ph'; #用于获取IP地址
- require 'DBI.pm'; #用于连接mysql数据库,不需要请注释掉(前面添加#)
- require 'DBD/mysql.pm'; #用于连接mysql数据库,不需要请注释掉
- # 2012年10月22日 Leo [email protected]
- # Blog:http://linux5588.blog.51cto.com
- # 本脚本只适合Redhat和CentOS系列的Linux系统
- #以下定义的变量需要修改
- my $interface = "eth0"; #邮件内容中显示的IP地址和流量统计的接口
- my $process_pattern = "httpd|java|mysqld"; #想要查看关键进程的名称
- my @partitions = ("/","/data","/backup_svn"); #想要查看的磁盘分区
- my $logfile ="/var/log/messages"; #要读取的日志文件
- my $countfile ="/var/log/last_messages.size"; #用于存放上一次读取日志文件的位置,要有可写入权限
- my $error_pattern = "(?:Error|Warning|Invalid)";#用于匹配日志文件中的error行或者warn行或者Invalid行
- #发送邮件设置,需要修改
- my $site = '163.com'; #用于标识发送邮件时的邮件域
- my $smtp_host = 'smtp.163.com' ; #发送的邮件服务器
- my $from = '[email protected]'; #发送邮件的帐号
- my $password = 'xxxxxxx'; #帐号密码
- my $to = '[email protected]'; #收件人
- my $subject = '服务器系统信息'; #设置邮件主题
- my $from_info = '服务器监控邮件'; #设置发件人信息
- # my $to2 = '[email protected]'; #设置第二个个收件人
- # my $to3 = '[email protected]'; #设置第三个个收件人
- #mysql从服务器设置,如果不需要检查mysql主从状态请注释掉这些变量,如果需要检查请修改
- my $slave_ip = '192.168.100.253'; #从服务器的IP地址,一般写内网IP地址
- my $database_name = 'test'; #连接的数据库,默认是test数据库
- my $database_user = 'root'; #从服务器登录mysql的用户名
- my $database_password = '123456'; #从服务器登录mysql的密码
- #以下定义的变量不需要修改
- my $currentscriptpid = $$; #脚本自身的PID
- my $hostname = hostname; #得到主机名
- my $date = &getDate; #获取日期
- my $currentPwd = cwd; #得到当前目录路径
- my $proc = "/proc"; #/proc目录变量
- my $ip = &getIP($interface); #获取IP地址
- my $basename = basename $0; #取得脚本的名字
- my $temp_log = "/tmp/$basename.log"; #临时文件,用于写入系统信息,用完后会删除
- my $run_log = "/tmp/${basename}_Running_Error.log";#脚本运行时错误日志
- #调用主函数执行程序
- &main;
- #主函数调用其他函数
- sub main {
- #改变当前工作目录到/proc目录
- chdir "$proc" or die "Can't change $proc: $!\n";
- #打开错误日志文件句柄
- open RUNLOG,">>","$run_log" or warn "Can't write running log\n";
- print RUNLOG "$date\n","=" x 19,"\n";
- #调用所有的函数,把获取到的信息写入到临时文件中
- &getSystemOverview;
- &getPerSecondTraffic;
- &getDiskUsage;
- &getMemoryUsuage;
- &getSwapUsage;
- &getTotalProcess;
- &getHttpPortConnectionStatus;
- #&checkMysqlMasterSlaveStatus; #如果需要检查mysql主从状态,去掉注释即可
- &getErrorLogFromMessages;
- #调用写html函数,把获得到的信息写成html格式
- &WriteHtml;
- #关闭错误日志文件句柄
- print RUNLOG "\n";
- close RUNLOG;
- #打开临时文件,读取文件的内容并发送邮件
- open READLOG,"<","$temp_log" or die "Can't read $temp_log : $!";
- @temp_information =
; - close READLOG;
- &SendMail(@temp_information);
- #改变当前工作目录到原来的工作目录
- chdir "$currentPwd" or warn "Can't change to $currentPwd : $!\n";
- }
- #把获取到的系统信息写成html格式
- #如果不需要检查mysql主从状态,和80的端口的网络连接状态,请删除红色表示那部分(142-147行)
- sub WriteHtml {
- eval {
- open WRITELOG,">","$temp_log" or die "Can't write $temp_log : $!";
- format WRITELOG =
-
系统信息概览
- 时间: @<<<<<<<<<<<<<<<<<<<< 主机: @<<<<<<<<<<<<<<<<<<<<<<< IP地址: @<<<<<<<<<<<<<<<<<<<<<<<<<
- $date $hostname $ip
- 系统当前负载: @<<<<<<<<<<<<<<<<<<<<<<<<<<
- $load
- 总进程数量: @<<<<<<<<<< 当前运行的进程的个数: @<<<<<<<<<<<
- $totalProcess $runningprocess
- 系统当前远程登录用户数: @<<<<<<<<<<个
- $user
- 系统已经运行: @<<<<<<<<<<<<<<<<<<
- $starttime
- 系统版本: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 系统位数: @<<<<<<<<<< 位
- $linux_version $linux_bit
- 内核版本: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
- $kernel_version
-
当前网卡流量
- 接收: @<<<<<<<<<<<
- $RX
- 发送: @<<<<<<<<<<<
- $TX
-
磁盘使用情况
- @*
- $disk_size
- ---------
- @*
- $partitions
-
当前内存使用
- 总内存大小: @<<<<<<<<<<<
- $Total_Memory
- 已经使用的内存大小: @<<<<<<<<<<<<<<<<<
- $Used_Memory
- 还剩余的内存大小: @<<<<<<<<<<<<<<<<<<<<
- $Available_Memory
-
当前SWAP使用
- 总大小: @<<<<<<<<<<<<<<<<<<<<<<<<<<
- $total_swap
- 已经使用大小: @<<<<<<<<<<<<<<<<<<<<<<<<
- $used_swap
-
当前关键进程信息
- @*
- $process
-
当前80端口的网络连接状态
- @*
- $status
-
Mysql 主从复制状态
- @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
- $mysqlslavestatus
-
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 日志文件中的错误信息
- $logfile
- @*
- $errorlog
- .
- write WRITELOG;
- close WRITELOG;
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- }
- #获取日期
- sub getDate {
- my ($sec, $min, $hour, $mday, $mon, $year) = (localtime)[0..5];
- $date = sprintf "%4d-%02d-%02d %2d:%02d:%02d",$year + 1900,$mon + 1 ,$mday ,$hour ,$min ,$sec;
- }
- #获取IP地址
- sub getIP {
- eval {
- my $pack = pack("a*", shift);
- my $socket;
- socket($socket, AF_INET, SOCK_DGRAM, 0);
- ioctl($socket, SIOCGIFADDR(), $pack);
- $ipaddr = inet_ntoa(substr($pack,20,4));
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- return $ipaddr;
- }
- #发送邮件函数
- sub SendMail {
- eval {
- my $smtp = Net::SMTP->new($smtp_host,Timeout=>30,Hello=>$site);
- $smtp-> auth($from, $password) or die "Email user or password Auth Error!\n";
- $smtp-> mail($from);
- $smtp-> to($to);
- #$smtp-> to($to,$to2,$to3); #同时发送给多个人
- $smtp-> data();
- $smtp-> datasend("Subject: $subject\n");
- $smtp-> datasend("From: $from_info\n");
- $smtp-> datasend("To: $to");
- $smtp-> datasend("\n" );
- $smtp-> datasend("Mime-Version:1.0\n");
- $smtp-> datasend("Content-Type: text/html;charset=utf-8\n");
- $smtp-> datasend("Content-Transfer-Encoding: quoted-printable\n\n");
- $smtp-> datasend("@_\n" );
- $smtp-> dataend();
- $smtp-> quit;
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- }
- #获取系统概览信息
- sub getSystemOverview {
- &getLinuxVersion;
- &getSystemBit;
- &getCurrentLoginUser;
- &getSystemAlreadyStartTime;
- &getRunningProcessAndLoad;
- eval {
- open VERSION,"<","./version" or die "Can't open version : $!";
- $version_str =
; - close VERSION;
- $where1 = index($version_str,"(");
- $kernel_version = substr($version_str,0,$where1);
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- #获取Linux版本
- sub getLinuxVersion {
- eval {
- open LINUXVERSION,"<","/etc/redhat-release";
- chomp($linux_version =
); - close LINUXVERSION;
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@) \n";
- }
- }
- #获取系统的位数
- sub getSystemBit {
- eval {
- chomp($linux_bit = `getconf LONG_BIT`);
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- }
- #获取当前远程登录的用户
- sub getCurrentLoginUser {
- eval {
- $user = 0;
- foreach (`w`) {
- $user++ if /\b\d+\.\d+\.\d+\.\d+\b/;
- }
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- }
- #获取启动时间
- sub getSystemAlreadyStartTime {
- eval {
- open UPTIME,"<","./uptime" or die "Can't read:$!\n";
- $starttime = (split /\s+/,
)[0]; - close UPTIME;
- if ($starttime >= 86400) {
- $starttime = sprintf ("%d 天",$starttime/86400);
- }
- elsif ($starttime < 3600) {
- $starttime = sprintf ("%d 分钟",$starttime/60);
- }
- else {
- $starttime = sprintf ("%d 小时",$starttime/3600);
- }
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- }
- #获取CPU的负载和正在运行的进程数量
- sub getRunningProcessAndLoad {
- eval {
- open LOAD,"<","./loadavg" or die "Can't read:$!\n";
- ($load1,$load5,$load10,$runningprocess) = (split /\s+/,
)[0..3]; - close LOAD;
- $load = $load1 . " " . $load5 . " " . $load10;
- $runningprocess = substr($runningprocess,0,1);
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- }
- }
- #计算总进程数量
- sub getTotalProcess {
- eval {
- opendir my $PROCDIR , '.' or die "Unable to open $proc: $!\n";
- my @procdir = readdir $PROCDIR or die "Unable to read $workdir: $!\n";
- close $PROCDIR;
- foreach my $pid (@procdir){
- next if ( $pid eq '.' );
- next if ( $pid eq '..' );
- next unless (-d $pid);
- if ($pid =~ /\b\d+\b/) {
- push @pid_numbers,$pid;
- $totalProcess++;
- }
- }
- &getPidNameAndMemory(@pid_numbers);
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- #获取每个主要进程信息(内存占用、PID、进程名称)
- sub getPidNameAndMemory {
- eval {
- foreach (@_){
- next if($_ == 1);
- next if($_ == $currentscriptpid);
- open PIDSTATUS,"<","./$_/status" or die "Unable to open $pid_temp: $!\n";
- @pid_name_mem =
; - close PIDSTATUS;
- if ($pid_name_mem[0] =~ /\b(?:$process_pattern)\b/) {
- $pid_name = (split /\s+/,$pid_name_mem[0])[1];
- $pid_memory = (split /\s+/,$pid_name_mem[15])[1];
- $pid_memory = &FormatSize($pid_memory * 1024);
- $pid_directory = readlink "$_/cwd";
- @$_ = ($pid_name,$_,$pid_memory,$pid_directory);
- push @pid_information_array,[@$_];
- }
- }
- foreach my $pid_array (@pid_information_array){
- $process = "进程名: $$pid_array[0]" . " \ " . "PID: $$pid_array[1]" . " \ " . "内存占用: $$pid_array[2]" . " \ " . "工作目录: $$pid_array[3]" . "
" . "\n$process"; - }
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- }
- }
- #获取总内存大小、已经使用内存大小及可用内存大小使用情况
- sub getMemoryUsuage {
- eval {
- open MEMTOTAL,"<","./meminfo" or die "Can't read:$!\n";
- my @memory_usuage;
- for (my $var=0; $var<4; $var++) {
- chomp($mem_temp =
); - push @memory_usuage,(split /\s+/,$mem_temp)[1];
- }
- close MEMTOTAL;
- #计算内存总大小
- $Total_Memory_Temp = $memory_usuage[0];
- $Total_Memory = &FormatSize($Total_Memory_Temp * 1024);
- #计算可用内存大小
- $Available_Memory_Temp = $memory_usuage[1] + $memory_usuage[2] + $memory_usuage[3];
- $Available_Memory = &FormatSize($Available_Memory_Temp * 1024);
- #计算已经使用的内存大小
- $Used_Memory = $Total_Memory_Temp - $Available_Memory_Temp;
- $Used_Memory = &FormatSize($Used_Memory * 1024);
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- }
- #计算swap使用情况
- sub getSwapUsage {
- eval {
- open SWAP,"<","./swaps" or die "Can't read swaps file : $!";
- @swap =
; - close SWAP;
- ($total_swap,$used_swap) = (split /\s+/,$swap[1])[2,3];
- $total_swap = &FormatSize($total_swap * 1024);
- $used_swap = &FormatSize($used_swap * 1024);
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- }
- #获取磁盘分区使用情况
- sub getDiskUsage {
- eval {
- open PARTITION,"<","./partitions" or die "Can't read: $!\n";
- while (
) { - if ($. > 2) {
- if (/\b[a-z]+\b/) {
- ($total,$disk) = (split)[2,3];
- $disk = '/dev/' . "$disk";
- ($sum_used_disk,$sum_available) = &getUsedAndFreeTotalSize($disk);
- $total = &FormatSize($total * 1024);
- $sum_used_disk = &FormatSize($sum_used_disk);
- $sum_available = &FormatSize($sum_available);
- $disk_size = "$disk" . " \ " . "总大小: $total" . " \ " . "可用: $sum_available" . " \ " . "已用: $sum_used_disk". "
" . "\n$disk_size"; - }
- }
- }
- close PARTITION;
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@) \n";
- }
- #计算各分区可用空间总和和已使用空间的总和
- sub getUsedAndFreeTotalSize {
- eval {
- my $disk = shift @_;
- foreach (`df -Pk`) {
- if (/^(?:$disk\d+)/) {
- ($used_disk,$available_disk) = (split)[2,3];
- $sum_used_disk = $sum_used_disk + $used_disk;
- $sum_available = $sum_available + $available_disk;
- }
- }
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- return $sum_used_disk * 1024,$sum_available * 1024;
- }
- #主要分区的使用情况
- &getPartitionSize;
- sub getPartitionSize {
- eval {
- foreach (@partitions){
- my $partitions_size = df("$_");
- $total = &FormatSize($partitions_size->{blocks} * 1024);
- $free = &FormatSize($partitions_size->{bfree} * 1024);
- $used = &FormatSize($partitions_size->{used} * 1024);
- $partitions = "分区: $_" . " \ " . "总大小: $total" . " \ " . "可用: $free" . " \ " . "已用: $used" . "
" . "\n$partitions"; - }
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- }
- }
- #获取每秒的流量,以KB/s和MB/s,这里的B是字节
- sub getPerSecondTraffic {
- &getTraffic;
- $RX = $traffic[2] - $traffic[0];
- $RX = &formatTrafficSize($RX);
- $TX = $traffic[3] - $traffic[1];
- $TX = &formatTrafficSize($TX);
- #获取发送和接收的流量
- sub getTraffic {
- eval {
- $flag++;
- open TRAFFIC,"<","./net/dev" or die "Can't read /proc/net/dev: $!";
- chomp(@traffic_temp =
); - close TRAFFIC;
- foreach my $j (@traffic_temp) {
- if ($j =~ /\s+$interface/) {
- my($rx,$tx) = (split /\s+/,(split /:/,$j)[1])[0,8];
- push @traffic,$rx;
- push @traffic,$tx;
- }
- }
- if ($flag < 2) {
- sleep 1;
- &getTraffic;
- }
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- return;
- }
- }
- #获取80端口的网络连接状态
- sub getHttpPortConnectionStatus {
- eval {
- foreach (`netstat -ant`) {
- my ($port,$status) = (split)[3,-1];
- if ($port =~ /:\b80\b$/){
- $network_status{$status}++;
- }
- }
- foreach (keys %network_status){
- $status = "$_ = $network_status{$_}" . "
" . "\n$status"; - }
- };
- if ($@) {
- print RUNLOG "Occurred Error ($@)\n";
- }
- }