All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alipay.lookout.os.linux.IOStatsMetricsImporter Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.alipay.lookout.os.linux;

import com.alipay.lookout.api.Gauge;
import com.alipay.lookout.api.Id;
import com.alipay.lookout.api.Registry;
import com.alipay.lookout.api.composite.MixinMetric;
import com.alipay.lookout.common.log.LookoutLoggerFactory;
import com.alipay.lookout.os.CachedMetricsImporter;
import com.alipay.lookout.os.utils.FileUtils;
import com.alipay.lookout.os.utils.NumFormatUtils;
import org.slf4j.Logger;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author wuqin
 * @author [email protected]
 * @version $Id: IOStatsMetricsImporter.java, v 0.1 2017-03-18 下午6:23 wuqin Exp $$
 */
public class IOStatsMetricsImporter extends CachedMetricsImporter {

    private final Logger          logger                    = LookoutLoggerFactory
                                                                .getLogger(IOStatsMetricsImporter.class);

    private static final int      TOTAL_LENGTH              = 14;

    static final int              READ_REQUEST_INDEX        = 3;
    static final int              READ_MERGED_INDEX         = 4;
    static final int              READ_SECTORS_INDEX        = 5;
    static final int              MSEC_READ_INDEX           = 6;
    static final int              WRITE_REQUEST_INDEX       = 7;
    static final int              WRITE_MERGED_INDEX        = 8;
    static final int              WRITE_SECTORS_INDEX       = 9;
    static final int              MSEC_WRITE_INDEX          = 10;
    static final int              IOS_IN_PROGRESS_INDEX     = 11;
    static final int              MSEC_TOTAL_INDEX          = 12;
    static final int              MSEC_WEIGHTED_TOTAL_INDEX = 13;

    private static final String   DEFAULT_FILE_PATH         = "/proc/diskstats";
    private static final String   DEFAULT_UPTIME_FILE_PATH  = "/proc/uptime";

    private static final String   SPLIT                     = "\\s+";

    private String                filePath;
    private String                upTimeFilePath;
    private Map  statsByDevice;
    private Map lastDiskInfoMap;
    private float                 lastUpTime;

    /**
     * default constructor
     */
    public IOStatsMetricsImporter() {
        this(DEFAULT_FILE_PATH, DEFAULT_UPTIME_FILE_PATH, DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
    }

    /**
     * @param filePath filePath
     * @param upTimeFilePath upTimeFilePath
     * @param timeout timeout
     * @param timeoutUnit timeoutUnit
     */
    public IOStatsMetricsImporter(String filePath, String upTimeFilePath, long timeout,
                                  TimeUnit timeoutUnit) {
        super(timeout, timeoutUnit);
        this.filePath = filePath;
        this.upTimeFilePath = upTimeFilePath;
        this.statsByDevice = new HashMap();
        this.lastDiskInfoMap = new HashMap();
        disable = !new File(filePath).exists();
        if (!disable)
            loadIfNessesary();
    }

    @Override
    protected void doRegister(Registry registry) {
        for (Map.Entry entry : statsByDevice.entrySet()) {
            final String device = entry.getKey();
            Id id = registry.createId("os.io.stats");
            id = id.withTag("device", device);
            MixinMetric mixin = registry.mixinMetric(id);

            mixin.gauge("svctm", new Gauge() {
                @Override
                public Float value() {
                    loadIfNessesary();
                    return statsByDevice.get(device)[IOStats.SVCTM.ordinal()];
                }
            });

            mixin.gauge("r_await", new Gauge() {
                @Override
                public Float value() {
                    loadIfNessesary();
                    return statsByDevice.get(device)[IOStats.R_AWAIT.ordinal()];
                }
            });

            mixin.gauge("w_await", new Gauge() {
                @Override
                public Float value() {
                    loadIfNessesary();
                    return statsByDevice.get(device)[IOStats.W_AWAIT.ordinal()];
                }
            });

            mixin.gauge("await", new Gauge() {
                @Override
                public Float value() {
                    loadIfNessesary();
                    return statsByDevice.get(device)[IOStats.AWAIT.ordinal()];
                }
            });

            mixin.gauge("util", new Gauge() {
                @Override
                public Float value() {
                    loadIfNessesary();
                    return statsByDevice.get(device)[IOStats.UTIL.ordinal()];
                }
            });
        }
    }

    @Override
    protected void loadValues() {
        float deltaTime = collectUpTime();
        if (deltaTime == 0.0f) {
            logger.debug("warning,calculate delta time failed!");
            return;
        }

        Map diskInfoMap = collectDiskInfo();
        for (Map.Entry entry : diskInfoMap.entrySet()) {
            String device = entry.getKey();
            if (statsByDevice.get(device) == null) {
                statsByDevice.put(device, new Float[IOStats.values().length]);
            }

            calDiskUsage(deltaTime, device, entry.getValue(), statsByDevice.get(device));
        }

        lastDiskInfoMap = diskInfoMap;
    }

    /**
     * @return
     */
    private float collectUpTime() {
        try {
            String line = FileUtils.readFile(upTimeFilePath);
            float currentUpTime = Float.parseFloat(line.split(SPLIT)[0]);
            float deltaTime = currentUpTime - lastUpTime;
            lastUpTime = currentUpTime;
            return deltaTime;
        } catch (Exception e) {
            logger.info("warning,can't parse line at /proc/uptime", e.getMessage());
        }
        return 0.0f;
    }

    /**
     * @return
     */
    private Map collectDiskInfo() {
        Map results = new HashMap();
        try {
            List lines = FileUtils.readFileAsStringArray(filePath);
            for (String line : lines) {
                String[] stats = line.trim().split(SPLIT);
                if (stats == null || stats.length != TOTAL_LENGTH) {
                    logger.debug("warning,can't parse text at /proc/diskstats, line: " + line);
                    continue;
                }
                if (Long.parseLong(stats[DiskStats.STATS.ordinal()]) == 0) {
                    continue;
                }

                if (isDevice(stats)) {
                    String device = stats[DiskStats.DEVICE.ordinal()];
                    DiskInfo diskInfo = new DiskInfo();
                    for (int index = DiskStats.STATS.ordinal(); index < stats.length; index++) {
                        long stat = Long.parseLong(stats[index]);
                        switch (index) {
                            case READ_REQUEST_INDEX:
                                diskInfo.readRequests = stat;
                                break;
                            case READ_MERGED_INDEX:
                                diskInfo.readMerged = stat;
                                break;
                            case READ_SECTORS_INDEX:
                                diskInfo.readSectors = stat;
                                break;
                            case MSEC_READ_INDEX:
                                diskInfo.msecRead = stat;
                                break;
                            case WRITE_REQUEST_INDEX:
                                diskInfo.writRequests = stat;
                                break;
                            case WRITE_MERGED_INDEX:
                                diskInfo.writeMerged = stat;
                                break;
                            case WRITE_SECTORS_INDEX:
                                diskInfo.writeSectors = stat;
                                break;
                            case MSEC_WRITE_INDEX:
                                diskInfo.msecWrite = stat;
                                break;
                            case IOS_IN_PROGRESS_INDEX:
                                diskInfo.iosInProgress = stat;
                                break;
                            case MSEC_TOTAL_INDEX:
                                diskInfo.msecTotal = stat;
                                break;
                            case MSEC_WEIGHTED_TOTAL_INDEX:
                                diskInfo.msecWeightedTotal = stat;
                                break;
                        }
                    }

                    results.put(device, diskInfo);
                }
            }
        } catch (Exception e) {
            logger.info("warning,can't parse text at /proc/diskstats", e.getMessage());
        }

        return results;
    }

    private boolean isDevice(String[] stats) {
        if (Long.parseLong(stats[DiskStats.MIN.ordinal()]) % 16 == 0
            && Long.parseLong(stats[DiskStats.MAJ.ordinal()]) > 0) {
            return true;
        } else {
            return false;
        }
    }

    private void calDiskUsage(float deltaTime, String device, DiskInfo diskInfo, Float[] stats) {
        DiskInfo lastDiskInfo = lastDiskInfoMap.get(device);
        if (lastDiskInfo == null) {
            stats[IOStats.UTIL.ordinal()] = 0.0f;
            stats[IOStats.SVCTM.ordinal()] = 0.0f;
            stats[IOStats.R_AWAIT.ordinal()] = 0.0f;
            stats[IOStats.W_AWAIT.ordinal()] = 0.0f;
            stats[IOStats.AWAIT.ordinal()] = 0.0f;
            return;
        }

        long totalRequests = diskInfo.readRequests + diskInfo.writRequests;
        long lastTotalRequests = lastDiskInfo.readRequests + lastDiskInfo.writRequests;
        long totalTicks = diskInfo.msecRead + diskInfo.msecWrite;
        long lastTotalTicks = lastDiskInfo.msecRead + lastDiskInfo.msecWrite;
        float util = 100.0f * (diskInfo.msecTotal - lastDiskInfo.msecTotal) / deltaTime;
        float tPut = NumFormatUtils.formatFloat(100.0f * (totalRequests - lastTotalRequests)
                                                / deltaTime);
        if (tPut > 0) {
            stats[IOStats.SVCTM.ordinal()] = NumFormatUtils.formatFloat(util / tPut);
        } else {
            stats[IOStats.SVCTM.ordinal()] = 0.0f;
        }

        if (diskInfo.readRequests != lastDiskInfo.readRequests) {
            stats[IOStats.R_AWAIT.ordinal()] = NumFormatUtils
                .formatFloat((diskInfo.msecRead - lastDiskInfo.msecRead)
                             / (float) (diskInfo.readRequests - lastDiskInfo.readRequests));
        } else {
            stats[IOStats.R_AWAIT.ordinal()] = 0.0f;
        }

        if (diskInfo.writRequests != lastDiskInfo.writRequests) {
            stats[IOStats.W_AWAIT.ordinal()] = NumFormatUtils
                .formatFloat((diskInfo.msecWrite - lastDiskInfo.msecWrite)
                             / (float) (diskInfo.writRequests - lastDiskInfo.writRequests));
        }
        if (totalRequests != lastTotalRequests) {
            stats[IOStats.AWAIT.ordinal()] = NumFormatUtils
                .formatFloat((totalTicks - lastTotalTicks)
                             / (float) (totalRequests - lastTotalRequests));
        }
        stats[IOStats.UTIL.ordinal()] = NumFormatUtils.formatFloat(util / 1000);
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    public void setUpTimeFilePath(String upTimeFilePath) {
        this.upTimeFilePath = upTimeFilePath;
    }

    private enum IOStats {
        SVCTM, //平均每次设备I/O操作的服务时间 (毫秒)
        R_AWAIT, //平均每次设备读I/O操作的等待时间(毫秒)
        W_AWAIT, //平均每次设备写I/O操作的等待时间(毫秒)
        AWAIT, //平均每次设备I/O操作的等待时间(毫秒)
        UTIL // 一秒中有百分之多少的时间用于I/O操作
    }

    private enum DiskStats {
        MAJ, MIN, DEVICE, STATS
    }

    private class DiskInfo {
        /**
         * Total number of reads completed successfully.
         */
        private long readRequests;

        /**
         * Adjacent read requests merged in a single req.
         */
        private long readMerged;

        /**
         * Total number of sectors read successfully.
         */
        private long readSectors;

        /**
         * Total number of ms spent by all reads.
         */
        private long msecRead;

        /**
         * Total number of writes completed successfully.
         */
        private long writRequests;

        /**
         * Adjacent write requests merged in a single req.
         */
        private long writeMerged;

        /**
         * Total number of sectors written successfully.
         */
        private long writeSectors;

        /**
         * Total number of ms spent by all writes.
         */
        private long msecWrite;

        /**
         * Number of actual I/O requests currently in flight.
         */
        private long iosInProgress;

        /**
         * Amount of time during which ios_in_progress >= 1.
         */
        private long msecTotal;

        /**
         * Measure of recent I/O completion time and backlog.
         */
        private long msecWeightedTotal;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy