com.mgmtp.perfload.perfmon.PerfMon.java Source code

Java tutorial

Introduction

Here is the source code for com.mgmtp.perfload.perfmon.PerfMon.java

Source

/*
 * Copyright (c) 2014 mgm technology partners GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mgmtp.perfload.perfmon;

import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.lang.text.StrBuilder;
import org.apache.commons.lang.time.FastDateFormat;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;
import org.hyperic.sigar.SigarProxy;
import org.hyperic.sigar.SigarProxyCache;
import org.hyperic.sigar.cmd.Shell;
import org.hyperic.sigar.cmd.Uptime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mgmtp.perfload.perfmon.cmd.BasePerfMonCommand;
import com.mgmtp.perfload.perfmon.cmd.PerfMonCpu;
import com.mgmtp.perfload.perfmon.cmd.PerfMonIo;
import com.mgmtp.perfload.perfmon.cmd.PerfMonJava;
import com.mgmtp.perfload.perfmon.cmd.PerfMonMem;
import com.mgmtp.perfload.perfmon.cmd.PerfMonNetStat;
import com.mgmtp.perfload.perfmon.cmd.PerfMonProc;
import com.mgmtp.perfload.perfmon.cmd.PerfMonSwap;
import com.mgmtp.perfload.perfmon.cmd.PerfMonTcp;
import com.mgmtp.perfload.perfmon.io.ConsoleOutputHandler;
import com.mgmtp.perfload.perfmon.io.FileOutputHandler;
import com.mgmtp.perfload.perfmon.io.OutputHandler;
import com.mgmtp.perfload.perfmon.util.PerfMonUtils;

/**
 * Utility program for logging system information using <a
 * href="http://www.hyperic.com/products/sigar">Sigar</a>.
 * 
 * @author rnaegele
 */
class PerfMon {
    private static final Logger LOG = LoggerFactory.getLogger(PerfMon.class);
    private static final String SEPARATOR = "\t";
    private static final Pattern LINE_PATTERN = Pattern.compile("^(.*)$", Pattern.MULTILINE);
    private static final FastDateFormat DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");

    private final long interval;
    private final List<BasePerfMonCommand> commands;
    private final OutputHandler outputHandler;

    private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);

    // It is important to use SigarProxyCache and to reuse the instance in order to avoid CPU spikes.
    private final SigarProxy sigar;

    PerfMon(final SigarProxy sigar, final long interval, final List<BasePerfMonCommand> commands,
            final OutputHandler outputHandler) {
        this.sigar = sigar;
        this.interval = interval;
        this.commands = commands;
        this.outputHandler = outputHandler;
    }

    void writeHeader() {
        String hostName = PerfMonUtils.getHostName();

        StringBuilder metaBuffer = new StringBuilder(200);
        metaBuffer.append(DATE_FORMAT.format(System.currentTimeMillis()));
        metaBuffer.append(SEPARATOR);
        metaBuffer.append("meta");
        metaBuffer.append(SEPARATOR);
        metaBuffer.append("perfMon ").append(PerfMonUtils.getPerfMonVersion());
        metaBuffer.append(SEPARATOR);
        metaBuffer.append(hostName);

        LOG.info("Hostname: {}", hostName);
        try {
            LOG.info("CPUs: {}", sigar.getCpuList().length);
            LOG.info("Total RAM: {} MB", sigar.getMem().getRam());
            LOG.info("Uptime: {}", Uptime.getInfo(sigar));
        } catch (SigarException ex) {
            LOG.error("Error retrieving system information from Sigar.", ex);
        }

        try {
            InetAddress[] networkIPAddresses = PerfMonUtils.getNetworkIPAddresses();
            if (networkIPAddresses.length > 0) {
                LOG.info("Local IP addresses:");
                metaBuffer.append(SEPARATOR);
                for (int i = 0; i < networkIPAddresses.length; ++i) {
                    InetAddress ip = networkIPAddresses[i];
                    if (i > 0) {
                        metaBuffer.append(',');
                    }
                    metaBuffer.append(ip);
                    LOG.info(ip.toString());
                }
            }
        } catch (UnknownHostException ex) {
            LOG.error("Error retrieving IP addresses of the localhost.", ex);
        }

        try {
            LOG.info("Full list of network interfaces:");
            List<NetworkInterface> networkInterfaces = PerfMonUtils.getNetworkInterfaces();
            for (NetworkInterface ni : networkInterfaces) {
                for (Enumeration<InetAddress> en = ni.getInetAddresses(); en.hasMoreElements();) {
                    LOG.info("\t\t{}", en.nextElement().toString());
                }
            }
        } catch (SocketException ex) {
            LOG.error("Error retrieving network interfaces.", ex);
        }

        outputHandler.writeln(metaBuffer.toString());
    }

    void scheduleInformationGathering() {
        executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    StrBuilder buffer = new StrBuilder(2000);

                    for (BasePerfMonCommand cmd : commands) {
                        try {
                            String result = cmd.executeCommand(sigar);
                            buffer.appendSeparator(PerfMonUtils.LINE_SEP);
                            buffer.append(result);
                        } catch (SigarException ex) {
                            LOG.error("Error executing command: {}" + cmd.getClass().getSimpleName(), ex);
                        }
                    }

                    String output = buffer.toString();
                    // insert timestamp at the beginning of each line
                    String isoTimestamp = DATE_FORMAT.format(System.currentTimeMillis());
                    output = LINE_PATTERN.matcher(output).replaceAll(isoTimestamp + SEPARATOR + "$1");
                    outputHandler.writeln(output);
                } catch (Exception ex) {
                    LOG.error(ex.getMessage(), ex);
                }
            }
        }, 0L, interval, TimeUnit.SECONDS);
    }

    void runShutdownHook() {
        try {
            LOG.info("Running shutdown hook...");
            executor.shutdownNow();
            executor.awaitTermination(interval, TimeUnit.SECONDS);
            outputHandler.close();
            LOG.info("Good bye.");
        } catch (InterruptedException ex) {
            // ignore
        }
    }

    public static void main(final String[] args) {
        CommandLine cmd = parseArgs(args);

        if (cmd != null) {
            final Sigar sigar = new Sigar();
            Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {
                    sigar.close();
                }
            });

            if (cmd.hasOption('s')) {
                shutdown(sigar);
                return;
            }

            long interval = Long.parseLong(cmd.getOptionValue('i', "5"));
            String fileName = cmd.getOptionValue('f');
            boolean java = cmd.hasOption('j');
            boolean tcp = cmd.hasOption('t');
            boolean netstat = cmd.hasOption('n');

            OutputHandler outputHandler = fileName != null ? new FileOutputHandler(fileName)
                    : new ConsoleOutputHandler();
            try {
                outputHandler.open();

                // Initialize Sigar libraries in order to fail fast.
                // Lazy initialization would cause perfMon to keep running logging errors all the time.
                // See https://github.com/mgm-tp/perfload/issues/3
                Sigar.load();

                SigarProxy sigarProxy = SigarProxyCache.newInstance(sigar);
                List<BasePerfMonCommand> commands = createCommandsList(java, tcp, netstat);
                final PerfMon perfMon = new PerfMon(sigarProxy, interval, commands, outputHandler);

                Runtime.getRuntime().addShutdownHook(new Thread() {
                    @Override
                    public void run() {
                        perfMon.runShutdownHook();
                    }
                });

                perfMon.writeHeader();
                perfMon.scheduleInformationGathering();
            } catch (IOException ex) {
                LOG.error("Error opening output file: " + fileName, ex);
            } catch (SigarException ex) {
                LOG.error(ex.getMessage(), ex);
            }
        }
    }

    static void shutdown(final Sigar sigar) {
        LOG.info("Shutting down perfMon...");

        String[] type = new String[] { "State.Name.sw=java,Args.*.re=perf(m|M)on" };

        try {
            long ownPid = sigar.getPid();
            long[] pids = Shell.getPids(sigar, type);
            for (long pid : pids) {
                if (pid != ownPid) {
                    LOG.info("Sending SIGTERM signal to perfMon process with PID {}", pid);
                    sigar.kill(pid, "TERM");
                }
            }
        } catch (SigarException ex) {
            LOG.error(ex.getMessage(), ex);
        }
    }

    static List<BasePerfMonCommand> createCommandsList(final boolean java, final boolean tcp,
            final boolean netStat) {

        List<BasePerfMonCommand> commands = new ArrayList<BasePerfMonCommand>();
        commands.add(new PerfMonCpu(SEPARATOR));
        commands.add(new PerfMonMem(SEPARATOR));
        commands.add(new PerfMonSwap(SEPARATOR));
        if (tcp) {
            commands.add(new PerfMonTcp(SEPARATOR));
        }
        if (netStat) {
            commands.add(new PerfMonNetStat(SEPARATOR));
        }
        commands.add(new PerfMonIo(SEPARATOR));
        commands.add(new PerfMonProc(SEPARATOR));
        if (java) {
            commands.add(new PerfMonJava(SEPARATOR));
        }
        return commands;
    }

    static CommandLine parseArgs(final String[] args) {
        Options options = new Options();
        try {
            Option option = new Option("i", "interval", true,
                    "The interval for printing the system information (default 5 sec).");
            option.setArgName("SECONDS");
            options.addOption(option);

            option = new Option("f", "file", true,
                    "The output file the information is written to. Stdout is used if no file is specified.");
            option.setArgName("FILE");
            options.addOption(option);

            options.addOption("c", "csv", false,
                    "Write all data in one line for easier import in tools such as Excel.");
            options.addOption("j", "java", false, "Write information on Java processes.");
            options.addOption("t", "tcp", false, "Write TCP connection information.");
            options.addOption("n", "netstat", false, "Write network statistics");
            options.addOption("s", "shutdown", false, "Shutdown all running perfMon processes on this machine.");
            options.addOption("h", "help", false, "Prints usage information.");

            CommandLineParser parser = new PosixParser();
            CommandLine cmd = parser.parse(options, args);
            if (!cmd.hasOption('h')) {
                return cmd;
            }
        } catch (ParseException ex) {
            LOG.error(ex.getMessage(), ex);
        }

        printUsage(options);
        return null;
    }

    static void printUsage(final Options options) {
        HelpFormatter help = new HelpFormatter();
        help.printHelp("perfMon", options);
    }
}