e3c37a46e2
at the first iteration, only the following metrics are collected: - HTTP metrics of each API endpoint - cache counter for request/hit/miss - histogram of storage actions, including: GetContent, PutContent, Stat, List, Move, and Delete Signed-off-by: tifayuki <tifayuki@gmail.com>
330 lines
8.9 KiB
Go
330 lines
8.9 KiB
Go
// Copyright 2017 The Prometheus Authors
|
|
// 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 xfs
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/prometheus/procfs/internal/util"
|
|
)
|
|
|
|
// ParseStats parses a Stats from an input io.Reader, using the format
|
|
// found in /proc/fs/xfs/stat.
|
|
func ParseStats(r io.Reader) (*Stats, error) {
|
|
const (
|
|
// Fields parsed into stats structures.
|
|
fieldExtentAlloc = "extent_alloc"
|
|
fieldAbt = "abt"
|
|
fieldBlkMap = "blk_map"
|
|
fieldBmbt = "bmbt"
|
|
fieldDir = "dir"
|
|
fieldTrans = "trans"
|
|
fieldIg = "ig"
|
|
fieldLog = "log"
|
|
fieldRw = "rw"
|
|
fieldAttr = "attr"
|
|
fieldIcluster = "icluster"
|
|
fieldVnodes = "vnodes"
|
|
fieldBuf = "buf"
|
|
fieldXpc = "xpc"
|
|
|
|
// Unimplemented at this time due to lack of documentation.
|
|
fieldPushAil = "push_ail"
|
|
fieldXstrat = "xstrat"
|
|
fieldAbtb2 = "abtb2"
|
|
fieldAbtc2 = "abtc2"
|
|
fieldBmbt2 = "bmbt2"
|
|
fieldIbt2 = "ibt2"
|
|
fieldFibt2 = "fibt2"
|
|
fieldQm = "qm"
|
|
fieldDebug = "debug"
|
|
)
|
|
|
|
var xfss Stats
|
|
|
|
s := bufio.NewScanner(r)
|
|
for s.Scan() {
|
|
// Expect at least a string label and a single integer value, ex:
|
|
// - abt 0
|
|
// - rw 1 2
|
|
ss := strings.Fields(string(s.Bytes()))
|
|
if len(ss) < 2 {
|
|
continue
|
|
}
|
|
label := ss[0]
|
|
|
|
// Extended precision counters are uint64 values.
|
|
if label == fieldXpc {
|
|
us, err := util.ParseUint64s(ss[1:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
xfss.ExtendedPrecision, err = extendedPrecisionStats(us)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
// All other counters are uint32 values.
|
|
us, err := util.ParseUint32s(ss[1:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch label {
|
|
case fieldExtentAlloc:
|
|
xfss.ExtentAllocation, err = extentAllocationStats(us)
|
|
case fieldAbt:
|
|
xfss.AllocationBTree, err = btreeStats(us)
|
|
case fieldBlkMap:
|
|
xfss.BlockMapping, err = blockMappingStats(us)
|
|
case fieldBmbt:
|
|
xfss.BlockMapBTree, err = btreeStats(us)
|
|
case fieldDir:
|
|
xfss.DirectoryOperation, err = directoryOperationStats(us)
|
|
case fieldTrans:
|
|
xfss.Transaction, err = transactionStats(us)
|
|
case fieldIg:
|
|
xfss.InodeOperation, err = inodeOperationStats(us)
|
|
case fieldLog:
|
|
xfss.LogOperation, err = logOperationStats(us)
|
|
case fieldRw:
|
|
xfss.ReadWrite, err = readWriteStats(us)
|
|
case fieldAttr:
|
|
xfss.AttributeOperation, err = attributeOperationStats(us)
|
|
case fieldIcluster:
|
|
xfss.InodeClustering, err = inodeClusteringStats(us)
|
|
case fieldVnodes:
|
|
xfss.Vnode, err = vnodeStats(us)
|
|
case fieldBuf:
|
|
xfss.Buffer, err = bufferStats(us)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &xfss, s.Err()
|
|
}
|
|
|
|
// extentAllocationStats builds an ExtentAllocationStats from a slice of uint32s.
|
|
func extentAllocationStats(us []uint32) (ExtentAllocationStats, error) {
|
|
if l := len(us); l != 4 {
|
|
return ExtentAllocationStats{}, fmt.Errorf("incorrect number of values for XFS extent allocation stats: %d", l)
|
|
}
|
|
|
|
return ExtentAllocationStats{
|
|
ExtentsAllocated: us[0],
|
|
BlocksAllocated: us[1],
|
|
ExtentsFreed: us[2],
|
|
BlocksFreed: us[3],
|
|
}, nil
|
|
}
|
|
|
|
// btreeStats builds a BTreeStats from a slice of uint32s.
|
|
func btreeStats(us []uint32) (BTreeStats, error) {
|
|
if l := len(us); l != 4 {
|
|
return BTreeStats{}, fmt.Errorf("incorrect number of values for XFS btree stats: %d", l)
|
|
}
|
|
|
|
return BTreeStats{
|
|
Lookups: us[0],
|
|
Compares: us[1],
|
|
RecordsInserted: us[2],
|
|
RecordsDeleted: us[3],
|
|
}, nil
|
|
}
|
|
|
|
// BlockMappingStat builds a BlockMappingStats from a slice of uint32s.
|
|
func blockMappingStats(us []uint32) (BlockMappingStats, error) {
|
|
if l := len(us); l != 7 {
|
|
return BlockMappingStats{}, fmt.Errorf("incorrect number of values for XFS block mapping stats: %d", l)
|
|
}
|
|
|
|
return BlockMappingStats{
|
|
Reads: us[0],
|
|
Writes: us[1],
|
|
Unmaps: us[2],
|
|
ExtentListInsertions: us[3],
|
|
ExtentListDeletions: us[4],
|
|
ExtentListLookups: us[5],
|
|
ExtentListCompares: us[6],
|
|
}, nil
|
|
}
|
|
|
|
// DirectoryOperationStats builds a DirectoryOperationStats from a slice of uint32s.
|
|
func directoryOperationStats(us []uint32) (DirectoryOperationStats, error) {
|
|
if l := len(us); l != 4 {
|
|
return DirectoryOperationStats{}, fmt.Errorf("incorrect number of values for XFS directory operation stats: %d", l)
|
|
}
|
|
|
|
return DirectoryOperationStats{
|
|
Lookups: us[0],
|
|
Creates: us[1],
|
|
Removes: us[2],
|
|
Getdents: us[3],
|
|
}, nil
|
|
}
|
|
|
|
// TransactionStats builds a TransactionStats from a slice of uint32s.
|
|
func transactionStats(us []uint32) (TransactionStats, error) {
|
|
if l := len(us); l != 3 {
|
|
return TransactionStats{}, fmt.Errorf("incorrect number of values for XFS transaction stats: %d", l)
|
|
}
|
|
|
|
return TransactionStats{
|
|
Sync: us[0],
|
|
Async: us[1],
|
|
Empty: us[2],
|
|
}, nil
|
|
}
|
|
|
|
// InodeOperationStats builds an InodeOperationStats from a slice of uint32s.
|
|
func inodeOperationStats(us []uint32) (InodeOperationStats, error) {
|
|
if l := len(us); l != 7 {
|
|
return InodeOperationStats{}, fmt.Errorf("incorrect number of values for XFS inode operation stats: %d", l)
|
|
}
|
|
|
|
return InodeOperationStats{
|
|
Attempts: us[0],
|
|
Found: us[1],
|
|
Recycle: us[2],
|
|
Missed: us[3],
|
|
Duplicate: us[4],
|
|
Reclaims: us[5],
|
|
AttributeChange: us[6],
|
|
}, nil
|
|
}
|
|
|
|
// LogOperationStats builds a LogOperationStats from a slice of uint32s.
|
|
func logOperationStats(us []uint32) (LogOperationStats, error) {
|
|
if l := len(us); l != 5 {
|
|
return LogOperationStats{}, fmt.Errorf("incorrect number of values for XFS log operation stats: %d", l)
|
|
}
|
|
|
|
return LogOperationStats{
|
|
Writes: us[0],
|
|
Blocks: us[1],
|
|
NoInternalBuffers: us[2],
|
|
Force: us[3],
|
|
ForceSleep: us[4],
|
|
}, nil
|
|
}
|
|
|
|
// ReadWriteStats builds a ReadWriteStats from a slice of uint32s.
|
|
func readWriteStats(us []uint32) (ReadWriteStats, error) {
|
|
if l := len(us); l != 2 {
|
|
return ReadWriteStats{}, fmt.Errorf("incorrect number of values for XFS read write stats: %d", l)
|
|
}
|
|
|
|
return ReadWriteStats{
|
|
Read: us[0],
|
|
Write: us[1],
|
|
}, nil
|
|
}
|
|
|
|
// AttributeOperationStats builds an AttributeOperationStats from a slice of uint32s.
|
|
func attributeOperationStats(us []uint32) (AttributeOperationStats, error) {
|
|
if l := len(us); l != 4 {
|
|
return AttributeOperationStats{}, fmt.Errorf("incorrect number of values for XFS attribute operation stats: %d", l)
|
|
}
|
|
|
|
return AttributeOperationStats{
|
|
Get: us[0],
|
|
Set: us[1],
|
|
Remove: us[2],
|
|
List: us[3],
|
|
}, nil
|
|
}
|
|
|
|
// InodeClusteringStats builds an InodeClusteringStats from a slice of uint32s.
|
|
func inodeClusteringStats(us []uint32) (InodeClusteringStats, error) {
|
|
if l := len(us); l != 3 {
|
|
return InodeClusteringStats{}, fmt.Errorf("incorrect number of values for XFS inode clustering stats: %d", l)
|
|
}
|
|
|
|
return InodeClusteringStats{
|
|
Iflush: us[0],
|
|
Flush: us[1],
|
|
FlushInode: us[2],
|
|
}, nil
|
|
}
|
|
|
|
// VnodeStats builds a VnodeStats from a slice of uint32s.
|
|
func vnodeStats(us []uint32) (VnodeStats, error) {
|
|
// The attribute "Free" appears to not be available on older XFS
|
|
// stats versions. Therefore, 7 or 8 elements may appear in
|
|
// this slice.
|
|
l := len(us)
|
|
if l != 7 && l != 8 {
|
|
return VnodeStats{}, fmt.Errorf("incorrect number of values for XFS vnode stats: %d", l)
|
|
}
|
|
|
|
s := VnodeStats{
|
|
Active: us[0],
|
|
Allocate: us[1],
|
|
Get: us[2],
|
|
Hold: us[3],
|
|
Release: us[4],
|
|
Reclaim: us[5],
|
|
Remove: us[6],
|
|
}
|
|
|
|
// Skip adding free, unless it is present. The zero value will
|
|
// be used in place of an actual count.
|
|
if l == 7 {
|
|
return s, nil
|
|
}
|
|
|
|
s.Free = us[7]
|
|
return s, nil
|
|
}
|
|
|
|
// BufferStats builds a BufferStats from a slice of uint32s.
|
|
func bufferStats(us []uint32) (BufferStats, error) {
|
|
if l := len(us); l != 9 {
|
|
return BufferStats{}, fmt.Errorf("incorrect number of values for XFS buffer stats: %d", l)
|
|
}
|
|
|
|
return BufferStats{
|
|
Get: us[0],
|
|
Create: us[1],
|
|
GetLocked: us[2],
|
|
GetLockedWaited: us[3],
|
|
BusyLocked: us[4],
|
|
MissLocked: us[5],
|
|
PageRetries: us[6],
|
|
PageFound: us[7],
|
|
GetRead: us[8],
|
|
}, nil
|
|
}
|
|
|
|
// ExtendedPrecisionStats builds an ExtendedPrecisionStats from a slice of uint32s.
|
|
func extendedPrecisionStats(us []uint64) (ExtendedPrecisionStats, error) {
|
|
if l := len(us); l != 3 {
|
|
return ExtendedPrecisionStats{}, fmt.Errorf("incorrect number of values for XFS extended precision stats: %d", l)
|
|
}
|
|
|
|
return ExtendedPrecisionStats{
|
|
FlushBytes: us[0],
|
|
WriteBytes: us[1],
|
|
ReadBytes: us[2],
|
|
}, nil
|
|
}
|