OS monitoring with… Java

Sometimes it may be useful to get system information like the usage of a disk or the available network interfaces.
For instance, Elasticsearch use this kind of tools in order to display at startup time some infos about open file descriptors or the size of the direct memory available for the JVM.
The aim is not to replace a real system monitoring agent, but to guide the user to take advantage of the product by configuring it properly.

Sigar is an open source project from Hyperic which provides a portable interface for gathering system information such as:

  • System memory, swap, cpu, load average, uptime, logins
  • Per-process memory, cpu, credential info, state, arguments, environment, open files
  • File system detection and metrics
  • Network interface detection, configuration info and metrics
  • TCP and UDP connection tables
  • Network route table

In Java, these informations are very difficult to get because you need to use dependent platform native system calls.
Sigar has one API to access this information regardless of the underlying platform implemented in C with bindings currently implemented for Java, Perl, Ruby, Python, Erlang, PHP and C#.

Many platforms are currently supported by Sigar:
* Linux
* Windows
* Solaris
* Mac OS X
* FreeBSD
* AIX
* HP-UX

and each time for x86 and x64 architectures.

If you want to use it in your project, you’ll have to add the following Maven dependency:

<dependency>
    <groupId>org.fusesource</groupId>
    <artifactId>sigar</artifactId>
    <version>1.6.4</version>
</dependency>

But there is one missing thing: Sigar needs the native lib for your platform and this Maven dependency doesn’t contain it. If you try to use Sigar as it, you will end with things like this:

org.hyperic.sigar.SigarException: no sigar-XXXXXX in java.library.path
        at org.hyperic.sigar.Sigar.loadLibrary(Sigar.java:172)
        at org.hyperic.sigar.Sigar.<clinit>(Sigar.java:100)
        at org.hyperic.sigar.win32.Win32.<clinit>(Win32.java:29)
        at com.mycompany.testadobeinstalled.TestAdobe.main(TestAdobe.java:28)
Exception in thread "main" java.lang.UnsatisfiedLinkError: org.hyperic.sigar.win32.RegistryKey.RegOpenKey(JLjava/lang/String;)J
        at org.hyperic.sigar.win32.RegistryKey.RegOpenKey(Native Method)
        at org.hyperic.sigar.win32.RegistryKey.openSubKey(RegistryKey.java:221)
        at com.mycompany.testadobeinstalled.TestAdobe.main(TestAdobe.java:28)

A second dependency is needed:

<dependency>
    <groupId>io.kamon</groupId>
    <artifactId>sigar-loader</artifactId>
    <version>1.6.6-rev002</version>
</dependency>

This loader contains all the so/sl/dll/…, if you have a look inside:

ls -l /home/devil/.m2/repository/io/kamon/sigar-loader/1.6.6-rev002/sigar-loader-1.6.6-rev002/native | awk '{print $9}'

libsigar-amd64-freebsd-6.so
libsigar-amd64-linux.so
libsigar-amd64-solaris.so
libsigar-ia64-hpux-11.sl
libsigar-ia64-linux.so
libsigar-pa64-hpux-11.sl
libsigar-pa-hpux-11.sl
libsigar-ppc64-aix-5.so
libsigar-ppc64-aix-7.so
libsigar-ppc64-linux.so
libsigar-ppc-aix-5.so
libsigar-ppc-aix-7.so
libsigar-ppc-linux.so
libsigar-s390x-linux.so
libsigar-sparc64-solaris.so
libsigar-sparc-solaris.so
libsigar-universal64-macosx.dylib
libsigar-universal-macosx.dylib
libsigar-x86-freebsd-5.so
libsigar-x86-freebsd-6.so
libsigar-x86-linux.so
libsigar-x86-solaris.so
sigar-amd64-winnt.dll
sigar-amd64-winnt.lib
sigar-x86-winnt.dll
sigar-x86-winnt.lib

Now we can test if Sigar is working:

# Load native librairies
SigarProvisioner.provision();
# Sigar is ready!
Sigar sigar = new Sigar();
sigar.getPid();

Let’s what we can do with it:

import kamon.sigar.SigarProvisioner;
import lombok.extern.slf4j.Slf4j;

import org.hyperic.sigar.NetInterfaceConfig;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;

import com.google.common.base.Throwables;

@Slf4j
public class SigarService {

    private final Sigar sigar;

    public SigarService() {
        Sigar sigar = null;
        try {
            SigarProvisioner.provision();
            sigar = new Sigar();
            // call it to make sure the library was loaded
            sigar.getPid();
            log.debug("sigar loaded successfully");
        } catch (Throwable t) {
            log.error("failed to load sigar", t);
            throw Throwables.propagate(t);
        }
        this.sigar = sigar;
    }

    public Infos infos() {

        Infos infos = new Infos();
        try {
            infos.setPid(sigar.getPid());
            infos.setFqdn(sigar.getFQDN());
            infos.setHostname(sigar.getNetInfo().getHostName());
            infos.setUptime(sigar.getUptime().getUptime());

            String[] netInterfaceList = sigar.getNetInterfaceList();
            for (String netInterfaceName : netInterfaceList) {
                // Add net interface
                NetInterfaceConfig config = sigar.getNetInterfaceConfig(netInterfaceName);
                Interface netInterface = new Interface();
                netInterface.setName(config.getName());
                netInterface.setType(config.getType());
                netInterface.setAddress(config.getAddress());
                infos.getInterfaces().add(netInterface);
            }

            // Add cpu
            infos.getCpu().setSys(sigar.getThreadCpu().getSys());
            infos.getCpu().setTotal(sigar.getThreadCpu().getTotal());
            infos.getCpu().setUser(sigar.getThreadCpu().getUser());

            // Add mem
            infos.getMem().setTotal(sigar.getMem().getTotal());
            infos.getMem().setUsed(sigar.getMem().getUsed());
            infos.getMem().setFree(sigar.getMem().getFree());

            // Add swap
            infos.getSwap().setTotal(sigar.getSwap().getTotal());
            infos.getSwap().setUsed(sigar.getSwap().getUsed());
            infos.getSwap().setFree(sigar.getSwap().getFree());

        } catch (SigarException e) {
            throw Throwables.propagate(e);
        }

        return infos;
    }
}
import java.util.ArrayList;
import java.util.List;

import lombok.Data;

@Data
public class Infos {

    private long pid;
    private String fqdn;
    private String hostname;
    private double uptime;
    private Cpu cpu = new Cpu();
    private Mem mem = new Mem();
    private Swap swap = new Swap();
    private List<Interface> interfaces = new ArrayList<>(2);
    private Limits limits = new Limits();

  @Data
    public static class Cpu {
        private long sys;
        private long total;
        private long user;
    }

    @Data
    public static class Mem {
        private long total;
        private long used;
        private long free;
    }

    @Data
    public static class Swap {
        private long total;
        private long used;
        private long free;
    }

    @Data
    public static class Interface {
        private String name;
        private String type;
        private String address;
    }
}

More infos:
https://github.com/hyperic/sigar
https://support.hyperic.com/display/SIGAR/Home

Credits:
“Cold Nights on California Street” by Thomas Hawk is licensed under CC BY-NC 2.0

Related Posts

Leave a comment

About privacy:

This site uses Akismet to reduce spam. Learn how your comment data is processed.